Code

a6daf3fe11c0202a8767961dbb0858fcc11a3bc2
[gosa.git] / gosa-si / gosa-si-server
1 #!/usr/bin/perl
2 #===============================================================================
3 #
4 #         FILE:  gosa-sd
5 #
6 #        USAGE:  ./gosa-sd
7 #
8 #  DESCRIPTION:
9 #
10 #      OPTIONS:  ---
11 # REQUIREMENTS:  libconfig-inifiles-perl libcrypt-rijndael-perl libxml-simple-perl 
12 #                libdata-dumper-simple-perl libdbd-sqlite3-perl libnet-ldap-perl
13 #                libpoe-perl
14 #         BUGS:  ---
15 #        NOTES:
16 #       AUTHOR:   (Andreas Rettenberger), <rettenberger@gonicus.de>
17 #      COMPANY:
18 #      VERSION:  1.0
19 #      CREATED:  12.09.2007 08:54:41 CEST
20 #     REVISION:  ---
21 #===============================================================================
23 my $server_version = '$HeadURL: https://oss.gonicus.de/repositories/gosa/trunk/gosa-si/gosa-si-server $:$Rev$';
25 use strict;
26 use warnings;
27 use Getopt::Long;
28 use Config::IniFiles;
29 use POSIX;
31 use Fcntl qw/:flock/;
32 use IO::Socket::INET;
33 use IO::Handle;
34 use IO::Select;
35 use Symbol qw(qualify_to_ref);
36 use Crypt::Rijndael;
37 use MIME::Base64;
38 use Digest::MD5  qw(md5 md5_hex md5_base64);
39 use XML::Simple;
40 use Data::Dumper;
41 use Sys::Syslog qw( :DEFAULT setlogsock);
42 use Time::HiRes qw( usleep);
43 use Cwd;
44 use File::Spec;
45 use File::Basename;
46 use File::Find;
47 use File::Copy;
48 use File::Path;
49 use GOSA::GosaSupportDaemon;
50 use POE qw(Component::Server::TCP Wheel::Run Filter::Reference);
51 use Net::LDAP;
52 use Net::LDAP::Util qw(:escape);
53 use ResourcePool;
54 use ResourcePool::Factory::Net::LDAP;
56 # revision number of server and program name
57 my $server_headURL;
58 my $server_revision;
59 my $server_status;
60 our $prg= basename($0);
62 my $db_module = "DBsqlite";
63 {
64 no strict "refs";
65 require ("GOSA/".$db_module.".pm");
66 ("GOSA/".$db_module)->import;
67 daemon_log("0 INFO: importing database module '$db_module'", 1);
68 }
70 my $modules_path = "/usr/lib/gosa-si/modules";
71 use lib "/usr/lib/gosa-si/modules";
73 our $global_kernel;
74 my ($foreground, $ping_timeout);
75 my ($server);
76 my ($gosa_server, $job_queue_timeout, $job_queue_loop_delay);
77 my ($messaging_db_loop_delay);
78 my ($procid, $pid);
79 my ($arp_fifo, $ldap_pool, $ldap_factory);
80 my ($xml);
81 my $sources_list;
82 my $max_clients;
83 my %repo_files=();
84 my $repo_path;
85 my %repo_dirs=();
87 # Variables declared in config file are always set to 'our'
88 our (%cfg_defaults, $log_file, $pid_file, 
89     $server_ip, $server_port, $ClientPackages_key, $dns_lookup,
90     $arp_activ, $gosa_unit_tag,
91     $GosaPackages_key, $gosa_timeout,
92     $foreign_server_string, $server_domain, $ServerPackages_key, $foreign_servers_register_delay,
93     $wake_on_lan_passwd, $job_synchronization, $modified_jobs_loop_delay,
94     $arp_enabled, $arp_interface,
95     $opsi_enabled, $opsi_server, $opsi_admin, $opsi_password,
96                 $new_systems_ou,
97 );
99 # additional variable which should be globaly accessable
100 our $server_address;
101 our $server_mac_address;
102 our $gosa_address;
103 our $no_arp;
104 our $verbose;
105 our $forground;
106 our $cfg_file;
107 our ($ldap_uri, $ldap_base, $ldap_admin_dn, $ldap_admin_password, $ldap_server_dn, $ldap_version);
108 our ($mysql_username, $mysql_password, $mysql_database, $mysql_host);
109 our $known_modules;
110 our $root_uid;
111 our $adm_gid;
114 # specifies the verbosity of the daemon_log
115 $verbose = 0 ;
117 # if foreground is not null, script will be not forked to background
118 $foreground = 0 ;
120 # specifies the timeout seconds while checking the online status of a registrating client
121 $ping_timeout = 5;
123 $no_arp = 0;
124 my $packages_list_under_construction = "/tmp/packages_list_creation_in_progress";
125 my @packages_list_statements;
126 my $watch_for_new_jobs_in_progress = 0;
128 # holds all incoming decrypted messages
129 our $incoming_db;
130 our $incoming_tn = 'incoming';
131 my $incoming_file_name;
132 my @incoming_col_names = ("id INTEGER PRIMARY KEY",
133         "timestamp VARCHAR(14) DEFAULT 'none'", 
134         "headertag VARCHAR(255) DEFAULT 'none'",
135         "targettag VARCHAR(255) DEFAULT 'none'",
136         "xmlmessage TEXT",
137         "module VARCHAR(255) DEFAULT 'none'",
138         "sessionid VARCHAR(255) DEFAULT '0'",
139 );
141 # holds all gosa jobs
142 our $job_db;
143 our $job_queue_tn = 'jobs';
144 my $job_queue_file_name;
145 my @job_queue_col_names = ("id INTEGER PRIMARY KEY",
146         "timestamp VARCHAR(14) DEFAULT 'none'", 
147         "status VARCHAR(255) DEFAULT 'none'", 
148         "result TEXT",
149         "progress VARCHAR(255) DEFAULT 'none'",
150         "headertag VARCHAR(255) DEFAULT 'none'",
151         "targettag VARCHAR(255) DEFAULT 'none'", 
152         "xmlmessage TEXT", 
153         "macaddress VARCHAR(17) DEFAULT 'none'",
154         "plainname VARCHAR(255) DEFAULT 'none'",
155         "siserver VARCHAR(255) DEFAULT 'none'",
156         "modified INTEGER DEFAULT '0'",
157 );
159 # holds all other gosa-si-server
160 our $known_server_db;
161 our $known_server_tn = "known_server";
162 my $known_server_file_name;
163 my @known_server_col_names = ("hostname VARCHAR(255)", "macaddress VARCHAR(17)", "status VARCHAR(255)", "hostkey VARCHAR(255)", "loaded_modules TEXT", "timestamp VARCHAR(14)");
165 # holds all registrated clients
166 our $known_clients_db;
167 our $known_clients_tn = "known_clients";
168 my $known_clients_file_name;
169 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)");
171 # holds all registered clients at a foreign server
172 our $foreign_clients_db;
173 our $foreign_clients_tn = "foreign_clients"; 
174 my $foreign_clients_file_name;
175 my @foreign_clients_col_names = ("hostname VARCHAR(255)", "macaddress VARCHAR(17)", "regserver VARCHAR(255)", "timestamp VARCHAR(14)");
177 # holds all logged in user at each client 
178 our $login_users_db;
179 our $login_users_tn = "login_users";
180 my $login_users_file_name;
181 my @login_users_col_names = ("client VARCHAR(255)", "user VARCHAR(255)", "timestamp VARCHAR(14)", "regserver VARCHAR(255) DEFAULT 'localhost'");
183 # holds all fai server, the debian release and tag
184 our $fai_server_db;
185 our $fai_server_tn = "fai_server"; 
186 my $fai_server_file_name;
187 our @fai_server_col_names = ("timestamp VARCHAR(14)", "server VARCHAR(255)", "fai_release VARCHAR(255)", "sections VARCHAR(255)", "tag VARCHAR(255)"); 
189 our $fai_release_db;
190 our $fai_release_tn = "fai_release"; 
191 my $fai_release_file_name;
192 our @fai_release_col_names = ("timestamp VARCHAR(14)", "fai_release VARCHAR(255)", "class VARCHAR(255)", "type VARCHAR(255)", "state VARCHAR(255)"); 
194 # holds all packages available from different repositories
195 our $packages_list_db;
196 our $packages_list_tn = "packages_list";
197 my $packages_list_file_name;
198 our @packages_list_col_names = ("distribution VARCHAR(255)", "package VARCHAR(255)", "version VARCHAR(255)", "section VARCHAR(255)", "description TEXT", "template LONGBLOB", "timestamp VARCHAR(14)");
199 my $outdir = "/tmp/packages_list_db";
200 my $arch = "i386"; 
202 # holds all messages which should be delivered to a user
203 our $messaging_db;
204 our $messaging_tn = "messaging"; 
205 our @messaging_col_names = ("id INTEGER", "subject TEXT", "message_from VARCHAR(255)", "message_to VARCHAR(255)", 
206         "flag VARCHAR(255)", "direction VARCHAR(255)", "delivery_time VARCHAR(255)", "message TEXT", "timestamp VARCHAR(14)" );
207 my $messaging_file_name;
209 # path to directory to store client install log files
210 our $client_fai_log_dir = "/var/log/fai"; 
212 # queue which stores taskes until one of the $max_children children are ready to process the task
213 #my @tasks = qw();
214 my @msgs_to_decrypt = qw();
215 my $max_children = 2;
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         "ldap-version"                  => [\$ldap_version, 3],
253     "gosa-unit-tag"         => [\$gosa_unit_tag, ""],
254     "max-clients"           => [\$max_clients, 10],
255     "wol-password"          => [\$wake_on_lan_passwd, ""],
256         "mysql-username"        => [\$mysql_username, "gosa_si"],
257         "mysql-password"        => [\$mysql_password, ""],
258         "mysql-database"        => [\$mysql_database, "gosa_si"],
259         "mysql-host"            => [\$mysql_host, "127.0.0.1"],
260     },
261 "GOsaPackages" => {
262     "job-queue" => [\$job_queue_file_name, '/var/lib/gosa-si/jobs.db'],
263     "job-queue-loop-delay" => [\$job_queue_loop_delay, 3],
264     "messaging-db-loop-delay" => [\$messaging_db_loop_delay, 3],
265     "key" => [\$GosaPackages_key, "none"],
266                 "new-systems-ou" => [\$new_systems_ou, 'ou=workstations,ou=systems'],
267     },
268 "ClientPackages" => {
269     "key" => [\$ClientPackages_key, "none"],
270     "user-date-of-expiry" => [\$logged_in_user_date_of_expiry, 600],
271     },
272 "ServerPackages"=> {
273     "address"      => [\$foreign_server_string, ""],
274     "dns-lookup"            => [\$dns_lookup, "true"],
275     "domain"  => [\$server_domain, ""],
276     "key"     => [\$ServerPackages_key, "none"],
277     "key-lifetime" => [\$foreign_servers_register_delay, 120],
278     "job-synchronization-enabled" => [\$job_synchronization, "true"],
279     "synchronization-loop" => [\$modified_jobs_loop_delay, 5],
280     },
281 "ArpHandler" => {
282     "enabled"   => [\$arp_enabled, "true"],
283     "interface" => [\$arp_interface, "all"],
284         },
285 "Opsi" => {
286     "enabled"  => [\$opsi_enabled, "false"], 
287     "server"   => [\$opsi_server, "localhost"],
288     "admin"    => [\$opsi_admin, "opsi-admin"],
289     "password" => [\$opsi_password, "secret"],
290    },
292 );
295 #===  FUNCTION  ================================================================
296 #         NAME:  usage
297 #   PARAMETERS:  nothing
298 #      RETURNS:  nothing
299 #  DESCRIPTION:  print out usage text to STDERR
300 #===============================================================================
301 sub usage {
302     print STDERR << "EOF" ;
303 usage: $prg [-hvf] [-c config]
305            -h        : this (help) message
306            -c <file> : config file
307            -f        : foreground, process will not be forked to background
308            -v        : be verbose (multiple to increase verbosity)
309            -no-arp   : starts $prg without connection to arp module
310  
311 EOF
312     print "\n" ;
316 #===  FUNCTION  ================================================================
317 #         NAME:  logging
318 #   PARAMETERS:  level - string - default 'info'
319 #                msg - string -
320 #                facility - string - default 'LOG_DAEMON'
321 #      RETURNS:  nothing
322 #  DESCRIPTION:  function for logging
323 #===============================================================================
324 sub daemon_log {
325     # log into log_file
326     my( $msg, $level ) = @_;
327     if(not defined $msg) { return }
328     if(not defined $level) { $level = 1 }
329     if(defined $log_file){
330         my $open_log_fh = sysopen(LOG_HANDLE, $log_file, O_WRONLY | O_CREAT | O_APPEND , 0440);
331         if(not $open_log_fh) {
332             print STDERR "cannot open $log_file: $!";
333             return;
334         }
335         # check owner and group of log_file and update settings if necessary
336         my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = stat($log_file);
337         if((not $uid eq $root_uid) || (not $gid eq $adm_gid)) {
338             chown($root_uid, $adm_gid, $log_file);
339         }
341         chomp($msg);
342         #$msg =~s/\n//g;   # no newlines are allowed in log messages, this is important for later log parsing
343         if($level <= $verbose){
344             my ($seconds, $minutes, $hours, $monthday, $month,
345                     $year, $weekday, $yearday, $sommertime) = localtime(time);
346             $hours = $hours < 10 ? $hours = "0".$hours : $hours;
347             $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
348             $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
349             my @monthnames = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
350             $month = $monthnames[$month];
351             $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
352             $year+=1900;
353             my $name = $prg;
355             my $log_msg = "$month $monthday $hours:$minutes:$seconds $name $msg\n";
356                         flock(LOG_HANDLE, LOCK_EX);
357                         seek(LOG_HANDLE, 0, 2);
358             print LOG_HANDLE $log_msg;
359                         flock(LOG_HANDLE, LOCK_UN);
360             if( $foreground ) { 
361                 print STDERR $log_msg;
362             }
363         }
364         close( LOG_HANDLE );
365     }
369 #===  FUNCTION  ================================================================
370 #         NAME:  check_cmdline_param
371 #   PARAMETERS:  nothing
372 #      RETURNS:  nothing
373 #  DESCRIPTION:  validates commandline parameter
374 #===============================================================================
375 sub check_cmdline_param () {
376     my $err_config;
377     my $err_counter = 0;
378         if(not defined($cfg_file)) {
379                 $cfg_file = "/etc/gosa-si/server.conf";
380                 if(! -r $cfg_file) {
381                         $err_config = "please specify a config file";
382                         $err_counter += 1;
383                 }
384     }
385     if( $err_counter > 0 ) {
386         &usage( "", 1 );
387         if( defined( $err_config)) { print STDERR "$err_config\n"}
388         print STDERR "\n";
389         exit( -1 );
390     }
394 #===  FUNCTION  ================================================================
395 #         NAME:  check_pid
396 #   PARAMETERS:  nothing
397 #      RETURNS:  nothing
398 #  DESCRIPTION:  handels pid processing
399 #===============================================================================
400 sub check_pid {
401     $pid = -1;
402     # Check, if we are already running
403     if( open(LOCK_FILE, "<$pid_file") ) {
404         $pid = <LOCK_FILE>;
405         if( defined $pid ) {
406             chomp( $pid );
407             if( -f "/proc/$pid/stat" ) {
408                 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
409                 if( $stat ) {
410                                         daemon_log("ERROR: Already running",1);
411                     close( LOCK_FILE );
412                     exit -1;
413                 }
414             }
415         }
416         close( LOCK_FILE );
417         unlink( $pid_file );
418     }
420     # create a syslog msg if it is not to possible to open PID file
421     if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
422         my($msg) = "Couldn't obtain lockfile '$pid_file' ";
423         if (open(LOCK_FILE, '<', $pid_file)
424                 && ($pid = <LOCK_FILE>))
425         {
426             chomp($pid);
427             $msg .= "(PID $pid)\n";
428         } else {
429             $msg .= "(unable to read PID)\n";
430         }
431         if( ! ($foreground) ) {
432             openlog( $0, "cons,pid", "daemon" );
433             syslog( "warning", $msg );
434             closelog();
435         }
436         else {
437             print( STDERR " $msg " );
438         }
439         exit( -1 );
440     }
443 #===  FUNCTION  ================================================================
444 #         NAME:  import_modules
445 #   PARAMETERS:  module_path - string - abs. path to the directory the modules 
446 #                are stored
447 #      RETURNS:  nothing
448 #  DESCRIPTION:  each file in module_path which ends with '.pm' and activation 
449 #                state is on is imported by "require 'file';"
450 #===============================================================================
451 sub import_modules {
452     daemon_log(" ", 1);
454     if (not -e $modules_path) {
455         daemon_log("0 ERROR: cannot find directory or directory is not readable: $modules_path", 1);   
456     }
458     opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
460         my $ldap_handle = &get_ldap_handle;
461     while (defined (my $file = readdir (DIR))) {
462         if (not $file =~ /(\S*?).pm$/) {
463             next;
464         }
465                 my $mod_name = $1;
467         # ArpHandler switch
468         if( $file =~ /ArpHandler.pm/ ) {
469             if( $arp_enabled eq "false" ) { next; }
470         }
471         
472         eval { require $file; };
473         if ($@) {
474             daemon_log("0 ERROR: gosa-si-server could not load module $file", 1);
475             daemon_log("$@", 1);
476             exit;
477                 } else {
478                         my $info = eval($mod_name.'::get_module_info($ldap_handle)');
479                         # Only load module if get_module_info() returns a non-null object
480                         if( $info ) {
481                                 my ($input_address, $input_key, $event_hash) = @{$info};
482                                 $known_modules->{$mod_name} = $info;
483                                 daemon_log("0 INFO: module $mod_name loaded", 5);
484                         }
485                 }
486     }   
487         &release_ldap_handle($ldap_handle); 
488     close (DIR);
491 #===  FUNCTION  ================================================================
492 #         NAME:  password_check
493 #   PARAMETERS:  nothing
494 #      RETURNS:  nothing
495 #  DESCRIPTION:  escalates an critical error if two modules exist which are avaialable by 
496 #                the same password
497 #===============================================================================
498 sub password_check {
499     my $passwd_hash = {};
500     while (my ($mod_name, $mod_info) = each %$known_modules) {
501         my $mod_passwd = @$mod_info[1];
502         if (not defined $mod_passwd) { next; }
503         if (not exists $passwd_hash->{$mod_passwd}) {
504             $passwd_hash->{$mod_passwd} = $mod_name;
506         # escalates critical error
507         } else {
508             &daemon_log("0 ERROR: two loaded modules do have the same password. Please modify the 'key'-parameter in config file");
509             &daemon_log("0 ERROR: module='$mod_name' and module='".$passwd_hash->{$mod_passwd}."'");
510             exit( -1 );
511         }
512     }
517 #===  FUNCTION  ================================================================
518 #         NAME:  sig_int_handler
519 #   PARAMETERS:  signal - string - signal arose from system
520 #      RETURNS:  nothing
521 #  DESCRIPTION:  handels tasks to be done befor signal becomes active
522 #===============================================================================
523 sub sig_int_handler {
524     my ($signal) = @_;
526 #       if (defined($ldap_handle)) {
527 #               $ldap_handle->disconnect;
528 #       }
529     # TODO alle verbliebenden ldap verbindungen aus allen heaps beenden
530     
532     daemon_log("shutting down gosa-si-server", 1);
533     system("kill `ps -C gosa-si-server -o pid=`");
535 $SIG{INT} = \&sig_int_handler;
538 sub check_key_and_xml_validity {
539     my ($crypted_msg, $module_key, $session_id) = @_;
540     my $msg;
541     my $msg_hash;
542     my $error_string;
543     eval{
544         $msg = &decrypt_msg($crypted_msg, $module_key);
546         if ($msg =~ /<xml>/i){
547             $msg =~ s/\s+/ /g;  # just for better daemon_log
548             daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 9);
549             $msg_hash = $xml->XMLin($msg, ForceArray=>1);
551             ##############
552             # check header
553             if( not exists $msg_hash->{'header'} ) { die "no header specified"; }
554             my $header_l = $msg_hash->{'header'};
555             if( (1 > @{$header_l}) || ( ( 'HASH' eq ref @{$header_l}[0]) && (1 > keys %{@{$header_l}[0]}) ) ) { die 'empty header tag'; }
556             if( 1 < @{$header_l} ) { die 'more than one header specified'; }
557             my $header = @{$header_l}[0];
558             if( 0 == length $header) { die 'empty string in header tag'; }
560             ##############
561             # check source
562             if( not exists $msg_hash->{'source'} ) { die "no source specified"; }
563             my $source_l = $msg_hash->{'source'};
564             if( (1 > @{$source_l}) || ( ( 'HASH' eq ref @{$source_l}[0]) && (1 > keys %{@{$source_l}[0]}) ) ) { die 'empty source tag'; }
565             if( 1 < @{$source_l} ) { die 'more than one source specified'; }
566             my $source = @{$source_l}[0];
567             if( 0 == length $source) { die 'source error'; }
569             ##############
570             # check target
571             if( not exists $msg_hash->{'target'} ) { die "no target specified"; }
572             my $target_l = $msg_hash->{'target'};
573             if( (1 > @{$target_l}) || ( ('HASH' eq ref @{$target_l}[0]) && (1 > keys %{@{$target_l}[0]}) ) ) { die 'empty target tag'; }
574         }
575     };
576     if($@) {
577         daemon_log("$session_id ERROR: do not understand the message: $@", 1);
578         $msg = undef;
579         $msg_hash = undef;
580     }
582     return ($msg, $msg_hash);
586 sub check_outgoing_xml_validity {
587     my ($msg, $session_id) = @_;
589     my $msg_hash;
590     eval{
591         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
593         ##############
594         # check header
595         my $header_l = $msg_hash->{'header'};
596         if( 1 != @{$header_l} ) {
597             die 'no or more than one headers specified';
598         }
599         my $header = @{$header_l}[0];
600         if( 0 == length $header) {
601             die 'header has length 0';
602         }
604         ##############
605         # check source
606         my $source_l = $msg_hash->{'source'};
607         if( 1 != @{$source_l} ) {
608             die 'no or more than 1 sources specified';
609         }
610         my $source = @{$source_l}[0];
611         if( 0 == length $source) {
612             die 'source has length 0';
613         }
615                                 # Check if source contains hostname instead of ip address
616                                 if(not $source =~ /^[a-z0-9\.]+:\d+$/i) {
617                                                 my ($hostname,$port) = split(/:/, $source);
618                                                 my $ip_address = inet_ntoa(scalar gethostbyname($hostname));
619                                                 if(defined($ip_address) && $ip_address =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/ && $port =~ /^\d+$/) {
620                                                         # Write ip address to $source variable
621                                                         $source = "$ip_address:$port";
622                                                 }
623                                 }
624         unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
625                 $source =~ /^GOSA$/i) {
626             die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
627         }
628         
629         ##############
630         # check target  
631         my $target_l = $msg_hash->{'target'};
632         if( 0 == @{$target_l} ) {
633             die "no targets specified";
634         }
635         foreach my $target (@$target_l) {
636             if( 0 == length $target) {
637                 die "target has length 0";
638             }
639             unless( $target =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
640                     $target =~ /^GOSA$/i ||
641                     $target =~ /^\*$/ ||
642                     $target =~ /KNOWN_SERVER/i ||
643                     $target =~ /JOBDB/i ||
644                     $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 ){
645                 die "target '$target' is not a complete ip-address with port or a valid target name or a mac-address";
646             }
647         }
648     };
649     if($@) {
650         daemon_log("$session_id ERROR: outgoing msg is not gosa-si envelope conform: $@", 1);
651         daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 1);
652         $msg_hash = undef;
653     }
655     return ($msg_hash);
659 sub input_from_known_server {
660     my ($input, $remote_ip, $session_id) = @_ ;  
661     my ($msg, $msg_hash, $module);
663     my $sql_statement= "SELECT * FROM known_server";
664     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
666     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
667         my $host_name = $hit->{hostname};
668         if( not $host_name =~ "^$remote_ip") {
669             next;
670         }
671         my $host_key = $hit->{hostkey};
672         daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 7);
673         daemon_log("$session_id DEBUG: input_from_known_server: host_key: $host_key", 7);
675         # check if module can open msg envelope with module key
676         my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
677         if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
678             daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 7);
679             daemon_log("$@", 8);
680             next;
681         }
682         else {
683             $msg = $tmp_msg;
684             $msg_hash = $tmp_msg_hash;
685             $module = "ServerPackages";
686             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 7);
687             last;
688         }
689     }
691     if( (!$msg) || (!$msg_hash) || (!$module) ) {
692         daemon_log("$session_id DEBUG: Incoming message is not from a known server", 7);
693     }
694   
695     return ($msg, $msg_hash, $module);
699 sub input_from_known_client {
700     my ($input, $remote_ip, $session_id) = @_ ;  
701     my ($msg, $msg_hash, $module);
703     my $sql_statement= "SELECT * FROM known_clients";
704     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
705     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
706         my $host_name = $hit->{hostname};
707         if( not $host_name =~ /^$remote_ip:\d*$/) {
708                 next;
709                 }
710         my $host_key = $hit->{hostkey};
711         &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 7);
712         &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 7);
714         # check if module can open msg envelope with module key
715         ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
717         if( (!$msg) || (!$msg_hash) ) {
718             &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 7);
719             &daemon_log("$@", 8);
720             next;
721         }
722         else {
723             $module = "ClientPackages";
724             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 7);
725             last;
726         }
727     }
729     if( (!$msg) || (!$msg_hash) || (!$module) ) {
730         &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 7);
731     }
733     return ($msg, $msg_hash, $module);
737 sub input_from_unknown_host {
738         no strict "refs";
739         my ($input, $session_id) = @_ ;
740         my ($msg, $msg_hash, $module);
741         my $error_string;
743         my %act_modules = %$known_modules;
745         while( my ($mod, $info) = each(%act_modules)) {
747                 # check a key exists for this module
748                 my $module_key = ${$mod."_key"};
749                 if( not defined $module_key ) {
750                         if( $mod eq 'ArpHandler' ) {
751                                 next;
752                         }
753                         daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
754                         next;
755                 }
756                 daemon_log("$session_id DEBUG: $mod: $module_key", 7);
758                 # check if module can open msg envelope with module key
759                 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
760                 if( (not defined $msg) || (not defined $msg_hash) ) {
761                         next;
762                 } else {
763                         $module = $mod;
764             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 7);
765                         last;
766                 }
767         }
769         if( (!$msg) || (!$msg_hash) || (!$module)) {
770                 daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 7);
771         }
773         return ($msg, $msg_hash, $module);
777 sub create_ciphering {
778     my ($passwd) = @_;
779         if((!defined($passwd)) || length($passwd)==0) {
780                 $passwd = "";
781         }
782     $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
783     my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
784     my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
785     $my_cipher->set_iv($iv);
786     return $my_cipher;
790 sub encrypt_msg {
791     my ($msg, $key) = @_;
792     my $my_cipher = &create_ciphering($key);
793     my $len;
794     {
795             use bytes;
796             $len= 16-length($msg)%16;
797     }
798     $msg = "\0"x($len).$msg;
799     $msg = $my_cipher->encrypt($msg);
800     chomp($msg = &encode_base64($msg));
801     # there are no newlines allowed inside msg
802     $msg=~ s/\n//g;
803     return $msg;
807 sub decrypt_msg {
809     my ($msg, $key) = @_ ;
810     $msg = &decode_base64($msg);
811     my $my_cipher = &create_ciphering($key);
812     $msg = $my_cipher->decrypt($msg); 
813     $msg =~ s/\0*//g;
814     return $msg;
818 sub get_encrypt_key {
819     my ($target) = @_ ;
820     my $encrypt_key;
821     my $error = 0;
823     # target can be in known_server
824     if( not defined $encrypt_key ) {
825         my $sql_statement= "SELECT * FROM known_server WHERE hostname='$target'";
826         my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
827         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
828             my $host_name = $hit->{hostname};
829             if( $host_name ne $target ) {
830                 next;
831             }
832             $encrypt_key = $hit->{hostkey};
833             last;
834         }
835     }
837     # target can be in known_client
838     if( not defined $encrypt_key ) {
839         my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$target'";
840         my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
841         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
842             my $host_name = $hit->{hostname};
843             if( $host_name ne $target ) {
844                 next;
845             }
846             $encrypt_key = $hit->{hostkey};
847             last;
848         }
849     }
851     return $encrypt_key;
855 #===  FUNCTION  ================================================================
856 #         NAME:  open_socket
857 #   PARAMETERS:  PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
858 #                [PeerPort] string necessary if port not appended by PeerAddr
859 #      RETURNS:  socket IO::Socket::INET
860 #  DESCRIPTION:  open a socket to PeerAddr
861 #===============================================================================
862 sub open_socket {
863     my ($PeerAddr, $PeerPort) = @_ ;
864     if(defined($PeerPort)){
865         $PeerAddr = $PeerAddr.":".$PeerPort;
866     }
867     my $socket;
868     $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
869             Porto => "tcp",
870             Type => SOCK_STREAM,
871             Timeout => 5,
872             );
873     if(not defined $socket) {
874         return;
875     }
876 #    &daemon_log("DEBUG: open_socket: $PeerAddr", 7);
877     return $socket;
881 sub send_msg_to_target {
882     my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
883     my $error = 0;
884     my $header;
885     my $timestamp = &get_time();
886     my $new_status;
887     my $act_status;
888     my ($sql_statement, $res);
889   
890     if( $msg_header ) {
891         $header = "'$msg_header'-";
892     } else {
893         $header = "";
894     }
896         # Patch the source ip
897         if($msg =~ /<source>0\.0\.0\.0:\d*?<\/source>/) {
898                 my $remote_ip = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
899                 $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$remote_ip:$2<\/source>/s;
900         }
902     # encrypt xml msg
903     my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
905     # opensocket
906     my $socket = &open_socket($address);
907     if( !$socket ) {
908         daemon_log("$session_id WARNING: cannot send ".$header."msg to $address , host not reachable", 3);
909         $error++;
910     }
911     
912     if( $error == 0 ) {
913         # send xml msg
914         print $socket $crypted_msg."\n";
916         daemon_log("$session_id INFO: send ".$header."msg to $address", 5);
917         daemon_log("$session_id DEBUG: message:\n$msg", 9);
918         
919     }
921     # close socket in any case
922     if( $socket ) {
923         close $socket;
924     }
926     if( $error > 0 ) { $new_status = "down"; }
927     else { $new_status = $msg_header; }
930     # known_clients
931     $sql_statement = "SELECT * FROM $known_clients_tn WHERE hostname='$address'";
932     $res = $known_clients_db->select_dbentry($sql_statement);
933     if( keys(%$res) == 1) {
934         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
935         if ($act_status eq "down" && $new_status eq "down") {
936             $sql_statement = "DELETE FROM known_clients WHERE hostname='$address'";
937             $res = $known_clients_db->del_dbentry($sql_statement);
938             daemon_log("$session_id WARNING: failed 2x to send msg to host '$address', delete host from known_clients", 3);
939         } else { 
940             $sql_statement = "UPDATE known_clients SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
941             $res = $known_clients_db->update_dbentry($sql_statement);
942             if($new_status eq "down"){
943                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
944             } else {
945                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
946             }
947         }
948     }
950     # known_server
951     $sql_statement = "SELECT * FROM $known_server_tn WHERE hostname='$address'";
952     $res = $known_server_db->select_dbentry($sql_statement);
953     if( keys(%$res) == 1) {
954         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
955         if ($act_status eq "down" && $new_status eq "down") {
956             $sql_statement = "DELETE FROM known_server WHERE hostname='$address'";
957             $res = $known_server_db->del_dbentry($sql_statement);
958             daemon_log("$session_id WARNING: failed 2x to send a message to host '$address', delete host from known_server", 3);
959         } 
960         else { 
961             $sql_statement = "UPDATE known_server SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
962             $res = $known_server_db->update_dbentry($sql_statement);
963             if($new_status eq "down"){
964                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
965             } else {
966                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
967             }
968         }
969     }
970     return $error; 
974 sub update_jobdb_status_for_send_msgs {
975     my ($session_id, $answer, $error) = @_;
976     &daemon_log("$session_id DEBUG: try to update job status", 7); 
977     if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
978         my $jobdb_id = $1;
979     
980         $answer =~ /<header>(.*)<\/header>/;
981         my $job_header = $1;
983         $answer =~ /<target>(.*)<\/target>/;
984         my $job_target = $1;
985             
986         # Sending msg failed
987         if( $error ) {
989             # Set jobs to done, jobs do not need to deliver their message in any case
990             if (($job_header eq "trigger_action_localboot")
991                     ||($job_header eq "trigger_action_lock")
992                     ||($job_header eq "trigger_action_halt") 
993                     ) {
994                 my $sql_statement = "UPDATE $job_queue_tn SET status='done' WHERE id=$jobdb_id";
995                 &daemon_log("$session_id DEBUG: $sql_statement", 7); 
996                 my $res = $job_db->update_dbentry($sql_statement);
997                 
998             # Reactivate jobs, jobs need to deliver their message
999             } elsif (($job_header eq "trigger_action_activate")
1000                     ||($job_header eq "trigger_action_update")
1001                     ||($job_header eq "trigger_action_reinstall") 
1002                     ||($job_header eq "trigger_activate_new")
1003                     ) {
1004                 &reactivate_job_with_delay($session_id, $job_target, $job_header, 30 );
1006             # For all other messages
1007             } else {
1008                 my $sql_statement = "UPDATE $job_queue_tn ".
1009                     "SET status='error', result='can not deliver msg, please consult log file' ".
1010                     "WHERE id=$jobdb_id";
1011                 &daemon_log("$session_id DEBUG: $sql_statement", 7); 
1012                 my $res = $job_db->update_dbentry($sql_statement);
1013             }
1015         # Sending msg was successful
1016         } else {
1017             # Set jobs localboot, lock, activate, halt, reboot and wake to done
1018             # jobs reinstall, update, inst_update do themself setting to done
1019             if (($job_header eq "trigger_action_localboot")
1020                     ||($job_header eq "trigger_action_lock")
1021                     ||($job_header eq "trigger_action_activate")
1022                     ||($job_header eq "trigger_action_halt") 
1023                     ||($job_header eq "trigger_action_reboot")
1024                     ||($job_header eq "trigger_action_wake")
1025                     ||($job_header eq "trigger_wake")
1026                     ) {
1028                 my $sql_statement = "UPDATE $job_queue_tn ".
1029                     "SET status='done' ".
1030                     "WHERE id=$jobdb_id AND status='processed'";
1031                 &daemon_log("$session_id DEBUG: $sql_statement", 7); 
1032                 my $res = $job_db->update_dbentry($sql_statement);
1033             } else { 
1034                 &daemon_log("$session_id DEBUG: sending message succeed but cannot update job status.", 7); 
1035             } 
1036         } 
1037     } else { 
1038         &daemon_log("$session_id DEBUG: cannot update job status, msg has no jobdb_id-tag: $answer", 7); 
1039     }
1042 sub reactivate_job_with_delay {
1043     my ($session_id, $target, $header, $delay) = @_ ;
1044     # 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
1045     
1046     if (not defined $delay) { $delay = 30 } ;
1047     my $delay_timestamp = &calc_timestamp(&get_time(), "plus", $delay);
1049     my $sql = "UPDATE $job_queue_tn Set timestamp='$delay_timestamp', status='waiting' WHERE (macaddress='$target' AND headertag='$header')"; 
1050     my $res = $job_db->update_dbentry($sql);
1051     daemon_log("$session_id INFO: '$header'-job will be reactivated at '$delay_timestamp' ".
1052             "cause client '$target' is currently not available", 5);
1053     daemon_log("$session_id $sql", 7);                             
1054     return;
1058 sub sig_handler {
1059         my ($kernel, $signal) = @_[KERNEL, ARG0] ;
1060         daemon_log("0 INFO got signal '$signal'", 1); 
1061         $kernel->sig_handled();
1062         return;
1066 sub msg_to_decrypt {
1067         my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
1068         my $session_id = $session->ID;
1069         my ($msg, $msg_hash, $module);
1070         my $error = 0;
1072         # fetch new msg out of @msgs_to_decrypt
1073         my $tmp_next_msg = shift @msgs_to_decrypt;
1074     my ($next_msg, $msg_source) = split(/;/, $tmp_next_msg);
1076         # msg is from a new client or gosa
1077         ($msg, $msg_hash, $module) = &input_from_unknown_host($next_msg, $session_id);
1079         # msg is from a gosa-si-server
1080         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1081                 ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $heap->{'remote_ip'}, $session_id);
1082         }
1083         # msg is from a gosa-si-client
1084         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1085                 ($msg, $msg_hash, $module) = &input_from_known_client($next_msg, $heap->{'remote_ip'}, $session_id);
1086         }
1087         # an error occurred
1088         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1089                 # if an incoming msg could not be decrypted (maybe a wrong key), send client a ping. If the client
1090                 # could not understand a msg from its server the client cause a re-registering process
1091         my $remote_ip = $heap->{'remote_ip'};
1092         my $remote_port = $heap->{'remote_port'};
1093         my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source><target>$msg_source</target></xml>";
1094         my ($test_error, $test_error_string) = &send_msg_to_target($ping_msg, "$msg_source", "dummy-key", "gosa_ping", $session_id);
1096                 daemon_log("$session_id WARNING cannot understand incoming msg, send 'ping'-msg to all host with ip '".$heap->{remote_ip}.
1097                         "' to cause a re-registering of the client if necessary", 3);
1098                 $error++;
1099         }
1102         my $header;
1103         my $target;
1104         my $source;
1105         my $done = 0;
1106         my $sql;
1107         my $res;
1109         # check whether this message should be processed here
1110         if ($error == 0) {
1111                 $header = @{$msg_hash->{'header'}}[0];
1112                 $target = @{$msg_hash->{'target'}}[0];
1113                 $source = @{$msg_hash->{'source'}}[0];
1114                 my $not_found_in_known_clients_db = 0;
1115                 my $not_found_in_known_server_db = 0;
1116                 my $not_found_in_foreign_clients_db = 0;
1117                 my $local_address;
1118                 my $local_mac;
1119                 my ($target_ip, $target_port) = split(':', $target);
1121                 # Determine the local ip address if target is an ip address
1122                 if ($target =~ /^\d+\.\d+\.\d+\.\d+:\d+$/) {
1123                         $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1124                 } else {
1125                         $local_address = $server_address;
1126                 }
1128                 # Determine the local mac address if target is a mac address
1129                 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) {
1130                         my $loc_ip = &get_local_ip_for_remote_ip($heap->{'remote_ip'});
1131                         my $network_interface= &get_interface_for_ip($loc_ip);
1132                         $local_mac = &get_mac_for_interface($network_interface);
1133                 } else {
1134                         $local_mac = $server_mac_address;
1135                 }
1137                 # target and source is equal to GOSA -> process here
1138                 if (not $done) {
1139                         if ($target eq "GOSA" && $source eq "GOSA") {
1140                                 $done = 1;                    
1141                                 &daemon_log("$session_id DEBUG: target and source is 'GOSA' -> process here", 7);
1142                         }
1143                 }
1145                 # target is own address without forward_to_gosa-tag -> process here
1146                 if (not $done) {
1147                         #if ((($target eq $local_address) || ($target eq $local_mac) ) && (not exists $msg_hash->{'forward_to_gosa'})) {
1148                         if (($target eq $local_address) && (not exists $msg_hash->{'forward_to_gosa'})) {
1149                                 $done = 1;
1150                                 if ($source eq "GOSA") {
1151                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1152                                 }
1153                                 &daemon_log("$session_id DEBUG: target is own address without forward_to_gosa-tag -> process here", 7);
1154                         }
1155                 }
1157                 # target is a client address in known_clients -> process here
1158                 if (not $done) {
1159                         $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')"; 
1160                         $res = $known_clients_db->select_dbentry($sql);
1161                         if (keys(%$res) > 0) {
1162                                 $done = 1; 
1163                                 my $hostname = $res->{1}->{'hostname'};
1164                                 $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1165                                 my $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1166                                 if ($source eq "GOSA") {
1167                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1168                                 }
1169                                 &daemon_log("$session_id DEBUG: target is a client address in known_clients -> process here", 7);
1171                         } else {
1172                                 $not_found_in_known_clients_db = 1;
1173                         }
1174                 }
1176                 # target ist own address with forward_to_gosa-tag not pointing to myself -> process here
1177                 if (not $done) {
1178                         my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1179                         my $gosa_at;
1180                         my $gosa_session_id;
1181                         if (($target eq $local_address) && (defined $forward_to_gosa)){
1182                                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1183                                 if ($gosa_at ne $local_address) {
1184                                         $done = 1;
1185                                         &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag not pointing to myself -> process here", 7); 
1186                                 }
1187                         }
1188                 }
1190                 # if message should be processed here -> add message to incoming_db
1191                 if ($done) {
1192                         # if a job or a gosa message comes from a foreign server, fake module to GosaPackages
1193                         # so gosa-si-server knows how to process this kind of messages
1194                         if ($header =~ /^gosa_/ || $header =~ /^job_/) {
1195                                 $module = "GosaPackages";
1196                         }
1198                         my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1199                                         primkey=>[],
1200                                         headertag=>$header,
1201                                         targettag=>$target,
1202                                         xmlmessage=>&encode_base64($msg),
1203                                         timestamp=>&get_time,
1204                                         module=>$module,
1205                                         sessionid=>$session_id,
1206                                 } );
1208                 }
1210                 # target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa
1211                 if (not $done) {
1212                         my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1213                         my $gosa_at;
1214                         my $gosa_session_id;
1215                         if (($target eq $local_address) && (defined $forward_to_gosa)){
1216                                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1217                                 if ($gosa_at eq $local_address) {
1218                                         my $session_reference = $kernel->ID_id_to_session($gosa_session_id);
1219                                         if( defined $session_reference ) {
1220                                                 $heap = $session_reference->get_heap();
1221                                         }
1222                                         if(exists $heap->{'client'}) {
1223                                                 $msg = &encrypt_msg($msg, $GosaPackages_key);
1224                                                 $heap->{'client'}->put($msg);
1225                                                 &daemon_log("$session_id INFO: incoming '$header' message forwarded to GOsa", 5); 
1226                                         }
1227                                         $done = 1;
1228                                         &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa", 7);
1229                                 }
1230                         }
1232                 }
1234                 # target is a client address in foreign_clients -> forward to registration server
1235                 if (not $done) {
1236                         $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1237                         $res = $foreign_clients_db->select_dbentry($sql);
1238                         if (keys(%$res) > 0) {
1239                                 my $hostname = $res->{1}->{'hostname'};
1240                                 my ($host_ip, $host_port) = split(/:/, $hostname);
1241                                 my $local_address =  &get_local_ip_for_remote_ip($host_ip).":$server_port";
1242                                 my $regserver = $res->{1}->{'regserver'};
1243                                 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$regserver'"; 
1244                                 my $res = $known_server_db->select_dbentry($sql);
1245                                 if (keys(%$res) > 0) {
1246                                         my $regserver_key = $res->{1}->{'hostkey'};
1247                                         $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1248                                         $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1249                                         if ($source eq "GOSA") {
1250                                                 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1251                                         }
1252                                         &send_msg_to_target($msg, $regserver, $regserver_key, $header, $session_id);
1253                                 }
1254                                 $done = 1;
1255                                 &daemon_log("$session_id DEBUG: target is a client address in foreign_clients -> forward to registration server", 7);
1256                         } else {
1257                                 $not_found_in_foreign_clients_db = 1;
1258                         }
1259                 }
1261                 # target is a server address -> forward to server
1262                 if (not $done) {
1263                         $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1264                         $res = $known_server_db->select_dbentry($sql);
1265                         if (keys(%$res) > 0) {
1266                                 my $hostkey = $res->{1}->{'hostkey'};
1268                                 if ($source eq "GOSA") {
1269                                         $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1270                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1272                                 }
1274                                 &send_msg_to_target($msg, $target, $hostkey, $header, $session_id);
1275                                 $done = 1;
1276                                 &daemon_log("$session_id DEBUG: target is a server address -> forward to server", 7);
1277                         } else {
1278                                 $not_found_in_known_server_db = 1;
1279                         }
1280                 }
1283                 # target is not in foreign_clients_db, known_server_db or known_clients_db, maybe it is a complete new one -> process here
1284                 if ( $not_found_in_foreign_clients_db 
1285                         && $not_found_in_known_server_db
1286                         && $not_found_in_known_clients_db) {
1287                         &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);
1288             if ($header =~ /^gosa_/ || $header =~ /^job_/) { 
1289                 $module = "GosaPackages"; 
1290             }
1291                         my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1292                                         primkey=>[],
1293                                         headertag=>$header,
1294                                         targettag=>$target,
1295                                         xmlmessage=>&encode_base64($msg),
1296                                         timestamp=>&get_time,
1297                                         module=>$module,
1298                                         sessionid=>$session_id,
1299                                 } );
1300                         $done = 1;
1301                 }
1304                 if (not $done) {
1305                         daemon_log("$session_id ERROR: do not know what to do with this message: $msg", 1);
1306                         if ($source eq "GOSA") {
1307                                 my %data = ('error_msg' => &encode_base64($msg), 'error_string' => "Do not know what to do with this message!");
1308                                 my $error_msg = &build_msg("error", $local_address, "GOSA", \%data ); 
1310                                 my $session_reference = $kernel->ID_id_to_session($session_id);
1311                                 if( defined $session_reference ) {
1312                                         $heap = $session_reference->get_heap();
1313                                 }
1314                                 if(exists $heap->{'client'}) {
1315                                         $error_msg = &encrypt_msg($error_msg, $GosaPackages_key);
1316                                         $heap->{'client'}->put($error_msg);
1317                                 }
1318                         }
1319                 }
1321         }
1323         return;
1327 sub next_task {
1328     my ($session, $heap, $task, $ldap_handle) = @_[SESSION, HEAP, ARG0, ARG1];
1329     my $running_task = POE::Wheel::Run->new(
1330             Program => sub { process_task($session, $heap, $task, $ldap_handle) },
1331             StdioFilter => POE::Filter::Reference->new(),
1332             StdoutEvent  => "task_result",
1333             StderrEvent  => "task_debug",
1334             CloseEvent   => "task_done",
1335             );
1336     $heap->{task}->{ $running_task->ID } = $running_task;
1337         $heap->{ldap_handle}->{$running_task->ID} = $ldap_handle;
1340 sub handle_task_result {
1341     my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
1342     my $client_answer = $result->{'answer'};
1343     if( $client_answer =~ s/session_id=(\d+)$// ) {
1344         my $session_id = $1;
1345         if( defined $session_id ) {
1346             my $session_reference = $kernel->ID_id_to_session($session_id);
1347             if( defined $session_reference ) {
1348                 $heap = $session_reference->get_heap();
1349             }
1350         }
1352         if(exists $heap->{'client'}) {
1353             $heap->{'client'}->put($client_answer);
1354         }
1355     }
1356     $kernel->sig(CHLD => "child_reap");
1359 sub handle_task_debug {
1360     my $result = $_[ARG0];
1361     print STDERR "$result\n";
1364 sub handle_task_done {
1365     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1366     delete $heap->{task}->{$task_id};
1367         if (exists $heap->{ldap_handle}->{$task_id}) {
1368                 &release_ldap_handle($heap->{ldap_handle}->{$task_id});
1369         }
1372 sub process_task {
1373     no strict "refs";
1374     #CHECK: Not @_[...]?
1375     my ($session, $heap, $task, $ldap_handle) = @_;
1376     my $error = 0;
1377     my $answer_l;
1378     my ($answer_header, @answer_target_l, $answer_source);
1379     my $client_answer = "";
1381     # prepare all variables needed to process message
1382     #my $msg = $task->{'xmlmessage'};
1383     my $msg = &decode_base64($task->{'xmlmessage'});
1384     my $incoming_id = $task->{'id'};
1385     my $module = $task->{'module'};
1386     my $header =  $task->{'headertag'};
1387     my $session_id = $task->{'sessionid'};
1388                 my $msg_hash;
1389                 eval {
1390         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
1391                 }; 
1392                 daemon_log("ERROR: XML failure '$@'") if ($@);
1393     my $source = @{$msg_hash->{'source'}}[0];
1394     
1395     # set timestamp of incoming client uptodate, so client will not 
1396     # be deleted from known_clients because of expiration
1397     my $cur_time = &get_time();
1398     my $sql = "UPDATE $known_clients_tn SET timestamp='$cur_time' WHERE hostname='$source'"; 
1399     my $res = $known_clients_db->exec_statement($sql);
1401     ######################
1402     # process incoming msg
1403     if( $error == 0) {
1404         daemon_log("$session_id INFO: Incoming msg (session_id=$session_id) with header '".@{$msg_hash->{'header'}}[0]."'", 5); 
1405         daemon_log("$session_id DEBUG: Processing module ".$module, 7);
1406         $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id, $ldap_handle);
1408         if ( 0 < @{$answer_l} ) {
1409             my $answer_str = join("\n", @{$answer_l});
1410             while ($answer_str =~ /<header>(\w+)<\/header>/g) {
1411                 daemon_log("$session_id INFO: got answer message with header '$1'", 5);
1412             }
1413             daemon_log("$session_id DEBUG: $module: got answer from module: \n".$answer_str,9);
1414         } else {
1415             daemon_log("$session_id DEBUG: $module: got no answer from module!" ,7);
1416         }
1418     }
1419     if( !$answer_l ) { $error++ };
1421     ########
1422     # answer
1423     if( $error == 0 ) {
1425         foreach my $answer ( @{$answer_l} ) {
1426             # check outgoing msg to xml validity
1427             my $answer_hash = &check_outgoing_xml_validity($answer, $session_id);
1428             if( not defined $answer_hash ) { next; }
1429             
1430             $answer_header = @{$answer_hash->{'header'}}[0];
1431             @answer_target_l = @{$answer_hash->{'target'}};
1432             $answer_source = @{$answer_hash->{'source'}}[0];
1434             # deliver msg to all targets 
1435             foreach my $answer_target ( @answer_target_l ) {
1437                 # targets of msg are all gosa-si-clients in known_clients_db
1438                 if( $answer_target eq "*" ) {
1439                     # answer is for all clients
1440                     my $sql_statement= "SELECT * FROM known_clients";
1441                     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
1442                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1443                         my $host_name = $hit->{hostname};
1444                         my $host_key = $hit->{hostkey};
1445                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1446                         &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1447                     }
1448                 }
1450                 # targets of msg are all gosa-si-server in known_server_db
1451                 elsif( $answer_target eq "KNOWN_SERVER" ) {
1452                     # answer is for all server in known_server
1453                     my $sql_statement= "SELECT * FROM $known_server_tn";
1454                     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
1455                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1456                         my $host_name = $hit->{hostname};
1457                         my $host_key = $hit->{hostkey};
1458                         $answer =~ s/<target>\S+<\/target>/<target>$host_name<\/target>/g;
1459                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1460                         &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1461                     }
1462                 }
1464                 # target of msg is GOsa
1465                                 elsif( $answer_target eq "GOSA" ) {
1466                                         my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1467                                         my $add_on = "";
1468                     if( defined $session_id ) {
1469                         $add_on = ".session_id=$session_id";
1470                     }
1471                     # answer is for GOSA and has to returned to connected client
1472                     my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1473                     $client_answer = $gosa_answer.$add_on;
1474                 }
1476                 # target of msg is job queue at this host
1477                 elsif( $answer_target eq "JOBDB") {
1478                     $answer =~ /<header>(\S+)<\/header>/;   
1479                     my $header;
1480                     if( defined $1 ) { $header = $1; }
1481                     my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1482                     &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1483                 }
1485                 # Target of msg is a mac address
1486                 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 ) {
1487                     daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients and foreign_clients", 5);
1489                     # Looking for macaddress in known_clients
1490                     my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1491                     my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1492                     my $found_ip_flag = 0;
1493                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1494                         my $host_name = $hit->{hostname};
1495                         my $host_key = $hit->{hostkey};
1496                         $answer =~ s/$answer_target/$host_name/g;
1497                         daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1498                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1499                         &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1500                         $found_ip_flag++ ;
1501                     }   
1503                     # Looking for macaddress in foreign_clients
1504                     if ($found_ip_flag == 0) {
1505                         my $sql = "SELECT * FROM $foreign_clients_tn WHERE macaddress LIKE '$answer_target'";
1506                         my $res = $foreign_clients_db->select_dbentry($sql);
1507                         while( my ($hit_num, $hit) = each %{ $res } ) {
1508                             my $host_name = $hit->{hostname};
1509                             my $reg_server = $hit->{regserver};
1510                             daemon_log("$session_id INFO: found host '$host_name' with mac '$answer_target', registered at '$reg_server'", 5);
1511                             
1512                             # Fetch key for reg_server
1513                             my $reg_server_key;
1514                             my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$reg_server'";
1515                             my $res = $known_server_db->select_dbentry($sql);
1516                             if (exists $res->{1}) {
1517                                 $reg_server_key = $res->{1}->{'hostkey'}; 
1518                             } else {
1519                                 daemon_log("$session_id ERROR: cannot find hostkey for '$host_name' in '$known_server_tn'", 1); 
1520                                 daemon_log("$session_id ERROR: unable to forward answer to correct registration server, processing is aborted!", 1); 
1521                                 $reg_server_key = undef;
1522                             }
1524                             # Send answer to server where client is registered
1525                             if (defined $reg_server_key) {
1526                                 $answer =~ s/$answer_target/$host_name/g;
1527                                 my $error = &send_msg_to_target($answer, $reg_server, $reg_server_key, $answer_header, $session_id);
1528                                 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1529                                 $found_ip_flag++ ;
1530                             }
1531                         }
1532                     }
1534                     # No mac to ip matching found
1535                     if( $found_ip_flag == 0) {
1536                         daemon_log("$session_id WARNING: no host found in known_clients or foreign_clients with mac address '$answer_target'", 3);
1537                         &reactivate_job_with_delay($session_id, $answer_target, $answer_header, 30);
1538                     }
1540                 # Answer is for one specific host   
1541                 } else {
1542                     # get encrypt_key
1543                     my $encrypt_key = &get_encrypt_key($answer_target);
1544                     if( not defined $encrypt_key ) {
1545                         # unknown target
1546                         daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1547                         next;
1548                     }
1549                     my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1550                     &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1551                 }
1552             }
1553         }
1554     }
1556     my $filter = POE::Filter::Reference->new();
1557     my %result = ( 
1558             status => "seems ok to me",
1559             answer => $client_answer,
1560             );
1562     my $output = $filter->put( [ \%result ] );
1563     print @$output;
1568 sub session_start {
1569     my ($kernel) = $_[KERNEL];
1570     $global_kernel = $kernel;
1571     $kernel->yield('register_at_foreign_servers');
1572         $kernel->yield('create_fai_server_db', $fai_server_tn );
1573         $kernel->yield('create_fai_release_db', $fai_release_tn );
1574     $kernel->yield('watch_for_next_tasks');
1575         $kernel->sig(USR1 => "sig_handler");
1576         $kernel->sig(USR2 => "recreate_packages_db");
1577         $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1578         $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay); 
1579     $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay); 
1580         $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1581     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1582         $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1583     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1585     # Start opsi check
1586     if ($opsi_enabled eq "true") {
1587         $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay); 
1588     }
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' WHERE macaddress LIKE '$macaddress' AND headertag = 'trigger_action_reinstall'");
1802                                                 $job_db->exec_statement("UPDATE $job_queue_tn SET timestamp='".&calc_timestamp(&get_time(), 'plus', 30)."' WHERE macaddress LIKE '$macaddress' AND headertag = 'trigger_action_reinstall'");
1803                                         }
1804                                 }
1805                                 next;
1806                         }
1808                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1809                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1810                                 if(defined($jobdb_id)) {
1811                                         my $job_msg = @{$hits->{$macaddress}->{$jobdb_headertag}}[7];
1813                                         daemon_log("J DEBUG: its time to execute $job_msg", 7);
1814                                         my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1815                                         my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1817                                         # expect macaddress is unique!!!!!!
1818                                         my $target = $res_hash->{1}->{hostname};
1820                                         # change header
1821                                         $job_msg =~ s/<header>job_/<header>gosa_/;
1823                                         # add sqlite_id
1824                                         $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1826                                         $job_msg =~ /<header>(\S+)<\/header>/;
1827                                         my $header = $1 ;
1828                                         my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");                    
1830                                         # update status in job queue to ...
1831                     # ... 'processing', for jobs: 'reinstall', 'update'
1832                     if (($header =~ /gosa_trigger_action_reinstall/) 
1833                             || ($header =~ /gosa_trigger_activate_new/)
1834                             || ($header =~ /gosa_trigger_action_update/)) {
1835                         my $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
1836                         my $dbres = $job_db->update_dbentry($sql_statement);
1837                     }
1839                     # ... 'done', for all other jobs, they are no longer needed in the jobqueue
1840                     else {
1841                         my $sql_statement = "UPDATE $job_queue_tn SET status='done' WHERE id=$jobdb_id";
1842                         my $dbres = $job_db->update_dbentry($sql_statement);
1843                     }
1844                 
1846                                         # We don't want parallel processing
1847                                         last;
1848                                 }
1849                         }
1850                 }
1852                 $watch_for_new_jobs_in_progress = 0;
1853                 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1854         }
1858 sub watch_for_new_messages {
1859     my ($kernel,$heap) = @_[KERNEL, HEAP];
1860     my @coll_user_msg;   # collection list of outgoing messages
1861     
1862     # check messaging_db for new incoming messages with executable timestamp
1863     my $timestamp = &get_time();
1864     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS UNSIGNED))<$timestamp AND flag='n' AND direction='in' )";
1865     my $res = $messaging_db->exec_statement( $sql_statement );
1866         foreach my $hit (@{$res}) {
1868         # create outgoing messages
1869         my $message_to = @{$hit}[3];
1870         # translate message_to to plain login name
1871         my @message_to_l = split(/,/, $message_to);  
1872                 my %receiver_h; 
1873                 foreach my $receiver (@message_to_l) {
1874                         if ($receiver =~ /^u_([\s\S]*)$/) {
1875                                 $receiver_h{$1} = 0;
1876                         } elsif ($receiver =~ /^g_([\s\S]*)$/) {
1877                                 my $group_name = $1;
1878                                 # fetch all group members from ldap and add them to receiver hash
1879                                 my $ldap_handle = &get_ldap_handle();
1880                                 if (defined $ldap_handle) {
1881                                                 my $mesg = $ldap_handle->search(
1882                                                                                 base => $ldap_base,
1883                                                                                 scope => 'sub',
1884                                                                                 attrs => ['memberUid'],
1885                                                                                 filter => "cn=$group_name",
1886                                                                                 );
1887                                                 &release_ldap_handle($ldap_handle);
1888                                                 if ($mesg->count) {
1889                                                                 my @entries = $mesg->entries;
1890                                                                 foreach my $entry (@entries) {
1891                                                                                 my @receivers= $entry->get_value("memberUid");
1892                                                                                 foreach my $receiver (@receivers) { 
1893                                                                                                 $receiver_h{$receiver} = 0;
1894                                                                                 }
1895                                                                 }
1896                                                 } 
1897                                                 # translating errors ?
1898                                                 if ($mesg->code) {
1899                                                                 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: $mesg->error", 1);
1900                                                 }
1901                                 # ldap handle error ?           
1902                                 } else {
1903                                         daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: no ldap handle available", 1);
1904                                 }
1905                         } else {
1906                                 my $sbjct = &encode_base64(@{$hit}[1]);
1907                                 my $msg = &encode_base64(@{$hit}[7]);
1908                                 &daemon_log("M WARNING: unknown receiver '$receiver' for a user-message '$sbjct - $msg'", 3); 
1909                         }
1910                 }
1911                 my @receiver_l = keys(%receiver_h);
1913         my $message_id = @{$hit}[0];
1915         #add each outgoing msg to messaging_db
1916         my $receiver;
1917         foreach $receiver (@receiver_l) {
1918             my $sql_statement = "INSERT INTO $messaging_tn (id, subject, message_from, message_to, flag, direction, delivery_time, message, timestamp) ".
1919                 "VALUES ('".
1920                 $message_id."', '".    # id
1921                 @{$hit}[1]."', '".     # subject
1922                 @{$hit}[2]."', '".     # message_from
1923                 $receiver."', '".      # message_to
1924                 "none"."', '".         # flag
1925                 "out"."', '".          # direction
1926                 @{$hit}[6]."', '".     # delivery_time
1927                 @{$hit}[7]."', '".     # message
1928                 $timestamp."'".     # timestamp
1929                 ")";
1930             &daemon_log("M DEBUG: $sql_statement", 1);
1931             my $res = $messaging_db->exec_statement($sql_statement);
1932             &daemon_log("M INFO: message '".@{$hit}[0]."' is prepared for delivery to receiver '$receiver'", 5);
1933         }
1935         # set incoming message to flag d=deliverd
1936         $sql_statement = "UPDATE $messaging_tn SET flag='p' WHERE id='$message_id'"; 
1937         &daemon_log("M DEBUG: $sql_statement", 7);
1938         $res = $messaging_db->update_dbentry($sql_statement);
1939         &daemon_log("M INFO: message '$message_id' is set to flag 'p' (processed)", 5);
1940     }
1942     $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay); 
1943     return;
1946 sub watch_for_delivery_messages {
1947     my ($kernel, $heap) = @_[KERNEL, HEAP];
1949     # select outgoing messages
1950     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( flag='p' AND direction='out' )";
1951     #&daemon_log("0 DEBUG: $sql", 7);
1952     my $res = $messaging_db->exec_statement( $sql_statement );
1953     
1954     # build out msg for each    usr
1955     foreach my $hit (@{$res}) {
1956         my $receiver = @{$hit}[3];
1957         my $msg_id = @{$hit}[0];
1958         my $subject = @{$hit}[1];
1959         my $message = @{$hit}[7];
1961         # resolve usr -> host where usr is logged in
1962         my $sql = "SELECT * FROM $login_users_tn WHERE (user='$receiver')"; 
1963         #&daemon_log("0 DEBUG: $sql", 7);
1964         my $res = $login_users_db->exec_statement($sql);
1966         # receiver is logged in nowhere
1967         if (not ref(@$res[0]) eq "ARRAY") { next; }    
1969         # receiver ist logged in at a client registered at local server
1970                 my $send_succeed = 0;
1971                 foreach my $hit (@$res) {
1972                                 my $receiver_host = @$hit[0];
1973                 my $delivered2host = 0;
1974                                 &daemon_log("M DEBUG: user '$receiver' is logged in at host '$receiver_host'", 7);
1976                                 # Looking for host in know_clients_db 
1977                                 my $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$receiver_host')";
1978                                 my $res = $known_clients_db->exec_statement($sql);
1980                 # Host is known in known_clients_db
1981                 if (ref(@$res[0]) eq "ARRAY") {
1982                     my $receiver_key = @{@{$res}[0]}[2];
1983                     my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
1984                     my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
1985                     my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0); 
1986                     if ($error == 0 ) {
1987                         $send_succeed++ ;
1988                         $delivered2host++ ;
1989                         &daemon_log("M DEBUG: send message for user '$receiver' to host '$receiver_host'", 7); 
1990                     } else {
1991                         &daemon_log("M DEBUG: cannot send message for user '$receiver' to host '$receiver_host'", 7); 
1992                     }
1993                 }
1994                 
1995                 # Message already send, do not need to do anything more, otherwise ...
1996                 if ($delivered2host) { next;}
1997     
1998                 # ...looking for host in foreign_clients_db
1999                 $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$receiver_host')";
2000                 $res = $foreign_clients_db->exec_statement($sql);
2001   
2002                                 # Host is known in foreign_clients_db 
2003                                 if (ref(@$res[0]) eq "ARRAY") { 
2004                     my $registration_server = @{@{$res}[0]}[2];
2005                     
2006                     # Fetch encryption key for registration server
2007                     my $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$registration_server')";
2008                     my $res = $known_server_db->exec_statement($sql);
2009                     if (ref(@$res[0]) eq "ARRAY") { 
2010                         my $registration_server_key = @{@{$res}[0]}[3];
2011                         my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
2012                         my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
2013                         my $error = &send_msg_to_target($out_msg, $registration_server, $registration_server_key, "usr_msg", 0); 
2014                         if ($error == 0 ) {
2015                             $send_succeed++ ;
2016                             $delivered2host++ ;
2017                             &daemon_log("M DEBUG: send message for user '$receiver' to server '$registration_server'", 7); 
2018                         } else {
2019                             &daemon_log("M ERROR: cannot send message for user '$receiver' to server '$registration_server'", 1); 
2020                         }
2022                     } else {
2023                         &daemon_log("M ERROR: host '$receiver_host' is reported to be ".
2024                                 "registrated at server '$registration_server', ".
2025                                 "but no data available in known_server_db ", 1); 
2026                     }
2027                 }
2028                 
2029                 if (not $delivered2host) {
2030                     &daemon_log("M ERROR: unable to send user message to host '$receiver_host'", 1);
2031                 }
2032                 }
2034                 if ($send_succeed) {
2035                                 # set outgoing msg at db to deliverd
2036                                 my $sql = "UPDATE $messaging_tn SET flag='d' WHERE (id='$msg_id' AND direction='out' AND message_to='$receiver')"; 
2037                                 my $res = $messaging_db->exec_statement($sql); 
2038                 &daemon_log("M INFO: send message for user '$receiver' to logged in hosts", 5);
2039                 } else {
2040             &daemon_log("M WARNING: failed to deliver message for user '$receiver'", 3); 
2041         }
2042         }
2044     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay); 
2045     return;
2049 sub watch_for_done_messages {
2050     my ($kernel,$heap) = @_[KERNEL, HEAP];
2052     my $sql = "SELECT * FROM $messaging_tn WHERE (flag='p' AND direction='in')"; 
2053     #&daemon_log("0 DEBUG: $sql", 7);
2054     my $res = $messaging_db->exec_statement($sql); 
2056     foreach my $hit (@{$res}) {
2057         my $msg_id = @{$hit}[0];
2059         my $sql = "SELECT * FROM $messaging_tn WHERE (id='$msg_id' AND direction='out' AND (NOT flag='s'))"; 
2060         #&daemon_log("0 DEBUG: $sql", 7); 
2061         my $res = $messaging_db->exec_statement($sql);
2063         # not all usr msgs have been seen till now
2064         if ( ref(@$res[0]) eq "ARRAY") { next; }
2065         
2066         $sql = "DELETE FROM $messaging_tn WHERE (id='$msg_id')"; 
2067         #&daemon_log("0 DEBUG: $sql", 7);
2068         $res = $messaging_db->exec_statement($sql);
2069     
2070     }
2072     $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay); 
2073     return;
2077 sub watch_for_old_known_clients {
2078     my ($kernel,$heap) = @_[KERNEL, HEAP];
2080     my $sql_statement = "SELECT * FROM $known_clients_tn";
2081     my $res = $known_clients_db->select_dbentry( $sql_statement );
2083     my $cur_time = int(&get_time());
2085     while ( my ($hit_num, $hit) = each %$res) {
2086         my $expired_timestamp = int($hit->{'timestamp'});
2087         $expired_timestamp =~ /(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/;
2088         my $dt = DateTime->new( year   => $1,
2089                 month  => $2,
2090                 day    => $3,
2091                 hour   => $4,
2092                 minute => $5,
2093                 second => $6,
2094                 );
2096         $dt->add( seconds => 2 * int($hit->{'keylifetime'}) );
2097         $expired_timestamp = $dt->ymd('').$dt->hms('');
2098         if ($cur_time > $expired_timestamp) {
2099             my $hostname = $hit->{'hostname'};
2100             my $del_sql = "DELETE FROM $known_clients_tn WHERE hostname='$hostname'"; 
2101             my $del_res = $known_clients_db->exec_statement($del_sql);
2103             &main::daemon_log("0 INFO: timestamp '".$hit->{'timestamp'}."' of client '$hostname' is expired('$expired_timestamp'), client will be deleted from known_clients_db", 5);
2104         }
2106     }
2108     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
2112 sub watch_for_next_tasks {
2113     my ($kernel,$heap) = @_[KERNEL, HEAP];
2115     my $sql = "SELECT * FROM $incoming_tn";
2116     my $res = $incoming_db->select_dbentry($sql);
2117     
2118     while ( my ($hit_num, $hit) = each %$res) {
2119         my $headertag = $hit->{'headertag'};
2120         if ($headertag =~ /^answer_(\d+)/) {
2121             # do not start processing, this message is for a still running POE::Wheel
2122             next;
2123         }
2124         my $message_id = $hit->{'id'};
2125         my $session_id = $hit->{'sessionid'};
2126         &daemon_log("$session_id DEBUG: start processing for message with incoming id: '$message_id'", 7);
2128                 my $ldap_handle = &get_ldap_handle();
2129                 if (not defined $ldap_handle) { next; }
2130         $kernel->yield('next_task', $hit, $ldap_handle);
2132         my $sql = "DELETE FROM $incoming_tn WHERE id=$message_id";
2133         my $res = $incoming_db->exec_statement($sql);
2134     }
2136     $kernel->delay_set('watch_for_next_tasks', 1); 
2140 sub get_ldap_handle {
2141         my ($session_id) = @_;
2142         my $heap;
2144         if (not defined $session_id ) { $session_id = 0 };
2145         if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
2147         (my $package, my $file, my $row, my $subroutine, my $hasArgs, my $wantArray, my $evalText, my $isRequire) = caller(1);
2148         my $caller_text = "subroutin $subroutine";
2149         if ($subroutine eq "(eval)") {
2150                 $caller_text = "eval block within file '$file' for '$evalText'"; 
2151         }
2152         daemon_log("$session_id INFO: new ldap handle for $caller_text required");
2154         my $ldap_handle = $ldap_pool->get();
2155         
2156         if (not defined $ldap_handle) {
2157                 daemon_log("$session_id INFO: ldap handle for $caller_text not available");
2158         }
2159         daemon_log("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
2160         return $ldap_handle;
2163 sub release_ldap_handle {
2164         my ($ldap_handle) = @_ ;
2165         $ldap_pool->free($ldap_handle);
2166         daemon_log("0 INFO: ldap handle released!\n-------------------------------------------------------------------------------");
2167         return;
2171 sub change_fai_state {
2172     my ($st, $targets, $session_id) = @_;
2173     $session_id = 0 if not defined $session_id;
2174     # Set FAI state to localboot
2175     my %mapActions= (
2176         reboot    => '',
2177         update    => 'softupdate',
2178         localboot => 'localboot',
2179         reinstall => 'install',
2180         rescan    => '',
2181         wake      => '',
2182         memcheck  => 'memcheck',
2183         sysinfo   => 'sysinfo',
2184         install   => 'install',
2185     );
2187     # Return if this is unknown
2188     if (!exists $mapActions{ $st }){
2189         daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1); 
2190       return;
2191     }
2193     my $state= $mapActions{ $st };
2195     #if( defined($ldap_handle) ) {
2197       # Build search filter for hosts
2198         my $search= "(&(objectClass=GOhard)";
2199         foreach (@{$targets}){
2200             $search.= "(macAddress=$_)";
2201         }
2202         $search.= ")";
2204       # If there's any host inside of the search string, procress them
2205         if (!($search =~ /macAddress/)){
2206             daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);    
2207             return;
2208         }
2210         my $ldap_handle = &get_ldap_handle($session_id);
2211       # Perform search for Unit Tag
2212       my $mesg = $ldap_handle->search(
2213           base   => $ldap_base,
2214           scope  => 'sub',
2215           attrs  => ['dn', 'FAIstate', 'objectClass'],
2216           filter => "$search"
2217           );
2219           if ($mesg->count) {
2220                   my @entries = $mesg->entries;
2221                   if (0 == @entries) {
2222                                   daemon_log("$session_id ERROR: ldap search failed: ldap_base=$ldap_base, filter=$search", 1); 
2223                   }
2225                   foreach my $entry (@entries) {
2226                           # Only modify entry if it is not set to '$state'
2227                           if ($entry->get_value("FAIstate") ne "$state"){
2228                                   daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
2229                                   my $result;
2230                                   my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
2231                                   if (exists $tmp{'FAIobject'}){
2232                                           if ($state eq ''){
2233                                                   $result= $ldap_handle->modify($entry->dn, changes => [ delete => [ FAIstate => [] ] ]);
2234                                           } else {
2235                                                   $result= $ldap_handle->modify($entry->dn, changes => [ replace => [ FAIstate => $state ] ]);
2236                                           }
2237                                   } elsif ($state ne ''){
2238                                           $result= $ldap_handle->modify($entry->dn, changes => [ add => [ objectClass => 'FAIobject' ], add => [ FAIstate => $state ] ]);
2239                                   }
2241                                   # Errors?
2242                                   if ($result->code){
2243                                           daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2244                                   }
2245                           } else {
2246                                   daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 7); 
2247                           }  
2248                   }
2249           } else {
2250                 daemon_log("$session_id ERROR: LDAP search failed: ldap_base=$ldap_base, filter=$search", 1);
2251           }
2252           &release_ldap_handle($ldap_handle);             
2254     # if no ldap handle defined
2255     #} else {
2256     #    daemon_log("$session_id ERROR: no LDAP handle defined for update FAIstate", 1); 
2257     #}
2259         return;
2263 sub change_goto_state {
2264     my ($st, $targets, $session_id) = @_;
2265     $session_id = 0  if not defined $session_id;
2267     # Switch on or off?
2268     my $state= $st eq 'active' ? 'active': 'locked';
2270     my $ldap_handle = &get_ldap_handle($session_id);
2271     if( defined($ldap_handle) ) {
2273       # Build search filter for hosts
2274       my $search= "(&(objectClass=GOhard)";
2275       foreach (@{$targets}){
2276         $search.= "(macAddress=$_)";
2277       }
2278       $search.= ")";
2280       # If there's any host inside of the search string, procress them
2281       if (!($search =~ /macAddress/)){
2282         return;
2283       }
2285       # Perform search for Unit Tag
2286       my $mesg = $ldap_handle->search(
2287           base   => $ldap_base,
2288           scope  => 'sub',
2289           attrs  => ['dn', 'gotoMode'],
2290           filter => "$search"
2291           );
2293       if ($mesg->count) {
2294         my @entries = $mesg->entries;
2295         foreach my $entry (@entries) {
2297           # Only modify entry if it is not set to '$state'
2298           if ($entry->get_value("gotoMode") ne $state){
2300             daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
2301             my $result;
2302             $result= $ldap_handle->modify($entry->dn, changes => [replace => [ gotoMode => $state ] ]);
2304             # Errors?
2305             if ($result->code){
2306               &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2307             }
2309           }
2310         }
2311       } else {
2312                 daemon_log("$session_id ERROR: LDAP search failed in function change_goto_state: ldap_base=$ldap_base, filter=$search", 1);
2313           }
2315     }
2316         &release_ldap_handle($ldap_handle);
2317         return;
2321 sub run_recreate_packages_db {
2322     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2323     my $session_id = $session->ID;
2324         &main::daemon_log("$session_id INFO: Recreating FAI Packages DB ('$fai_release_tn', '$fai_server_tn', '$packages_list_tn')", 5);
2325         $kernel->yield('create_fai_release_db', $fai_release_tn);
2326         $kernel->yield('create_fai_server_db', $fai_server_tn);
2327         return;
2331 sub run_create_fai_server_db {
2332     my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2333     my $session_id = $session->ID;
2334         my $ldap_handle = &get_ldap_handle();
2335         if (not defined $ldap_handle) {
2336                 $kernel->delay_set('create_fai_server_db', 1, $table_name);
2337                 return;
2338         }
2339     my $task = POE::Wheel::Run->new(
2340             Program => sub { &create_fai_server_db($table_name,$kernel, undef, $session_id, $ldap_handle) },
2341             StdoutEvent  => "session_run_result",
2342             StderrEvent  => "session_run_debug",
2343             CloseEvent   => "session_run_done",
2344             );
2346     $heap->{task}->{ $task->ID } = $task;
2347         $heap->{ldap_handle}->{$task->ID} = $ldap_handle;
2348     return;
2352 sub create_fai_server_db {
2353         my ($table_name, $kernel, $dont_create_packages_list, $session_id, $ldap_handle) = @_;
2354         my $result;
2356         if (not defined $session_id) { $session_id = 0; }
2357         if(defined($ldap_handle)) {
2358                 daemon_log("$session_id INFO: create_fai_server_db: start", 5);
2359                 my $mesg= $ldap_handle->search(
2360                         base   => $ldap_base,
2361                         scope  => 'sub',
2362                         attrs  => ['FAIrepository', 'gosaUnitTag'],
2363                         filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
2364                 );
2365                 if($mesg->{'resultCode'} == 0 &&
2366                         $mesg->count != 0) {
2367                         foreach my $entry (@{$mesg->{entries}}) {
2368                                 if($entry->exists('FAIrepository')) {
2369                                         # Add an entry for each Repository configured for server
2370                                         foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
2371                                                 my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
2372                                                 my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
2373                                                 $result= $fai_server_db->add_dbentry( { 
2374                                                                 table => $table_name,
2375                                                                 primkey => ['server', 'fai_release', 'tag'],
2376                                                                 server => $tmp_url,
2377                                                                 fai_release => $tmp_release,
2378                                                                 sections => $tmp_sections,
2379                                                                 tag => (length($tmp_tag)>0)?$tmp_tag:"",
2380                                                         } );
2381                                         }
2382                                 }
2383                         }
2384                 }
2385                 daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
2387                 # TODO: Find a way to post the 'create_packages_list_db' event
2388                 if(not defined($dont_create_packages_list)) {
2389                         &create_packages_list_db(undef, $session_id);
2390                 }
2391         }       
2393         return $result;
2397 sub run_create_fai_release_db {
2398         my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2399         my $session_id = $session->ID;
2400         my $ldap_handle = &get_ldap_handle();
2401         if (not defined $ldap_handle) {
2402                 $kernel->delay_set('create_fai_release_db', 1, $table_name);
2403                 return;
2404         }
2405         my $task = POE::Wheel::Run->new(
2406                 Program => sub { &create_fai_release_db($table_name, $session_id, $ldap_handle) },
2407                 StdoutEvent  => "session_run_result",
2408                 StderrEvent  => "session_run_debug",
2409                 CloseEvent   => "session_run_done",
2410         );
2412         $heap->{task}->{ $task->ID } = $task;
2413         $heap->{ldap_handle}->{$task->ID} = $ldap_handle;
2414         return;
2418 sub create_fai_release_db {
2419         my ($table_name, $session_id, $ldap_handle) = @_;
2420         my $result;
2422         # used for logging
2423         if (not defined $session_id) { $session_id = 0; }
2425         #my $ldap_handle = &get_ldap_handle();
2426         if(defined($ldap_handle)) {
2427                 daemon_log("$session_id INFO: create_fai_release_db: start",5);
2428                 my $mesg= $ldap_handle->search(
2429                         base   => $ldap_base,
2430                         scope  => 'sub',
2431                         attrs  => [],
2432                         filter => "(&(objectClass=organizationalUnit)(ou=fai))",
2433                 );
2434                 if(($mesg->code == 0) && ($mesg->count != 0))
2435                 {
2436                         daemon_log("$session_id DEBUG: create_fai_release_db: count " . $mesg->count,8);
2438                         # Walk through all possible FAI container ou's
2439                         my @sql_list;
2440                         my $timestamp= &get_time();
2441                         foreach my $ou (@{$mesg->{entries}}) {
2442                                 my $tmp_classes= resolve_fai_classes($ou->dn, $ldap_handle, $session_id);
2443                                 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
2444                                         my @tmp_array=get_fai_release_entries($tmp_classes);
2445                                         if(@tmp_array) {
2446                                                 foreach my $entry (@tmp_array) {
2447                                                         if(defined($entry) && ref($entry) eq 'HASH') {
2448                                                                 my $sql= 
2449                                                                 "INSERT INTO $table_name "
2450                                                                 ."(timestamp, fai_release, class, type, state) VALUES ("
2451                                                                 .$timestamp.","
2452                                                                 ."'".$entry->{'release'}."',"
2453                                                                 ."'".$entry->{'class'}."',"
2454                                                                 ."'".$entry->{'type'}."',"
2455                                                                 ."'".$entry->{'state'}."')";
2456                                                                 push @sql_list, $sql;
2457                                                         }
2458                                                 }
2459                                         }
2460                                 }
2461                         }
2463                         daemon_log("$session_id DEBUG: create_fai_release_db: Inserting ".scalar @sql_list." entries to DB",8);
2464                         if(@sql_list) {
2465                                 unshift @sql_list, "VACUUM";
2466                                 unshift @sql_list, "DELETE FROM $table_name";
2467                                 $fai_release_db->exec_statementlist(\@sql_list);
2468                         }
2469                         daemon_log("$session_id DEBUG: create_fai_release_db: Done with inserting",7);
2470                 } else {
2471                         daemon_log("$session_id INFO: create_fai_release_db: error: " . $mesg->code, 5);
2472                 }
2473                 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
2474         }
2475         #&release_ldap_handle($ldap_handle);
2476         return $result;
2479 sub get_fai_types {
2480         my $tmp_classes = shift || return undef;
2481         my @result;
2483         foreach my $type(keys %{$tmp_classes}) {
2484                 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
2485                         my $entry = {
2486                                 type => $type,
2487                                 state => $tmp_classes->{$type}[0],
2488                         };
2489                         push @result, $entry;
2490                 }
2491         }
2493         return @result;
2496 sub get_fai_state {
2497         my $result = "";
2498         my $tmp_classes = shift || return $result;
2500         foreach my $type(keys %{$tmp_classes}) {
2501                 if(defined($tmp_classes->{$type}[0])) {
2502                         $result = $tmp_classes->{$type}[0];
2503                         
2504                 # State is equal for all types in class
2505                         last;
2506                 }
2507         }
2509         return $result;
2512 sub resolve_fai_classes {
2513         my ($fai_base, $ldap_handle, $session_id) = @_;
2514         if (not defined $session_id) { $session_id = 0; }
2515         my $result;
2516         my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
2517         my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
2518         my $fai_classes;
2520         daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base",7);
2521         my $mesg= $ldap_handle->search(
2522                 base   => $fai_base,
2523                 scope  => 'sub',
2524                 attrs  => ['cn','objectClass','FAIstate'],
2525                 filter => $fai_filter,
2526         );
2527         daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries",7);
2529         if($mesg->{'resultCode'} == 0 &&
2530                 $mesg->count != 0) {
2531                 foreach my $entry (@{$mesg->{entries}}) {
2532                         if($entry->exists('cn')) {
2533                                 my $tmp_dn= $entry->dn();
2534                                 $tmp_dn= substr( $tmp_dn, 0, length($tmp_dn)
2535                                         - length($fai_base) - 1 );
2537                                 # Skip classname and ou dn parts for class
2538                                 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?)$/;
2540                                 # Skip classes without releases
2541                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2542                                         next;
2543                                 }
2545                                 my $tmp_cn= $entry->get_value('cn');
2546                                 my $tmp_state= $entry->get_value('FAIstate');
2548                                 my $tmp_type;
2549                                 # Get FAI type
2550                                 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
2551                                         if(grep $_ eq $oclass, @possible_fai_classes) {
2552                                                 $tmp_type= $oclass;
2553                                                 last;
2554                                         }
2555                                 }
2557                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2558                                         # A Subrelease
2559                                         my @sub_releases = split(/,/, $tmp_release);
2561                                         # Walk through subreleases and build hash tree
2562                                         my $hash;
2563                                         while(my $tmp_sub_release = pop @sub_releases) {
2564                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2565                                         }
2566                                         eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
2567                                 } else {
2568                                         # A branch, no subrelease
2569                                         push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
2570                                 }
2571                         } elsif (!$entry->exists('cn')) {
2572                                 my $tmp_dn= $entry->dn();
2573                                 $tmp_dn= substr( $tmp_dn, 0, length($tmp_dn)
2574                                         - length($fai_base) - 1 );
2575                                 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?)$/;
2577                                 # Skip classes without releases
2578                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2579                                         next;
2580                                 }
2582                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2583                                         # A Subrelease
2584                                         my @sub_releases= split(/,/, $tmp_release);
2586                                         # Walk through subreleases and build hash tree
2587                                         my $hash;
2588                                         while(my $tmp_sub_release = pop @sub_releases) {
2589                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2590                                         }
2591                                         # Remove the last two characters
2592                                         chop($hash);
2593                                         chop($hash);
2595                                         eval('$fai_classes->'.$hash.'= {}');
2596                                 } else {
2597                                         # A branch, no subrelease
2598                                         if(!exists($fai_classes->{$tmp_release})) {
2599                                                 $fai_classes->{$tmp_release} = {};
2600                                         }
2601                                 }
2602                         }
2603                 }
2605                 # The hash is complete, now we can honor the copy-on-write based missing entries
2606                 foreach my $release (keys %$fai_classes) {
2607                         $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
2608                 }
2609         }
2610         return $result;
2613 sub apply_fai_inheritance {
2614        my $fai_classes = shift || return {};
2615        my $tmp_classes;
2617        # Get the classes from the branch
2618        foreach my $class (keys %{$fai_classes}) {
2619                # Skip subreleases
2620                if($class =~ /^ou=.*$/) {
2621                        next;
2622                } else {
2623                        $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
2624                }
2625        }
2627        # Apply to each subrelease
2628        foreach my $subrelease (keys %{$fai_classes}) {
2629                if($subrelease =~ /ou=/) {
2630                        foreach my $tmp_class (keys %{$tmp_classes}) {
2631                                if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
2632                                        $fai_classes->{$subrelease}->{$tmp_class} =
2633                                        deep_copy($tmp_classes->{$tmp_class});
2634                                } else {
2635                                        foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
2636                                                if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
2637                                                        $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
2638                                                        deep_copy($tmp_classes->{$tmp_class}->{$type});
2639                                                }
2640                                        }
2641                                }
2642                        }
2643                }
2644        }
2646        # Find subreleases in deeper levels
2647        foreach my $subrelease (keys %{$fai_classes}) {
2648                if($subrelease =~ /ou=/) {
2649                        foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
2650                                if($subsubrelease =~ /ou=/) {
2651                                        apply_fai_inheritance($fai_classes->{$subrelease});
2652                                }
2653                        }
2654                }
2655        }
2657        return $fai_classes;
2660 sub get_fai_release_entries {
2661         my $tmp_classes = shift || return;
2662         my $parent = shift || "";
2663         my @result = shift || ();
2665         foreach my $entry (keys %{$tmp_classes}) {
2666                 if(defined($entry)) {
2667                         if($entry =~ /^ou=.*$/) {
2668                                 my $release_name = $entry;
2669                                 $release_name =~ s/ou=//g;
2670                                 if(length($parent)>0) {
2671                                         $release_name = $parent."/".$release_name;
2672                                 }
2673                                 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2674                                 foreach my $bufentry(@bufentries) {
2675                                         push @result, $bufentry;
2676                                 }
2677                         } else {
2678                                 my @types = get_fai_types($tmp_classes->{$entry});
2679                                 foreach my $type (@types) {
2680                                         push @result, 
2681                                         {
2682                                                 'class' => $entry,
2683                                                 'type' => $type->{'type'},
2684                                                 'release' => $parent,
2685                                                 'state' => $type->{'state'},
2686                                         };
2687                                 }
2688                         }
2689                 }
2690         }
2692         return @result;
2695 sub deep_copy {
2696         my $this = shift;
2697         if (not ref $this) {
2698                 $this;
2699         } elsif (ref $this eq "ARRAY") {
2700                 [map deep_copy($_), @$this];
2701         } elsif (ref $this eq "HASH") {
2702                 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2703         } else { die "what type is $_?" }
2707 sub session_run_result {
2708     my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];    
2709     $kernel->sig(CHLD => "child_reap");
2712 sub session_run_debug {
2713     my $result = $_[ARG0];
2714     print STDERR "$result\n";
2717 sub session_run_done {
2718     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2719     delete $heap->{task}->{$task_id};
2720         if (exists $heap->{ldap_handle}->{$task_id}) {
2721                 &release_ldap_handle($heap->{ldap_handle}->{$task_id});
2722         }
2723         delete $heap->{ldap_handle}->{$task_id};
2727 sub create_sources_list {
2728         my $session_id = shift;
2729         my $result="/tmp/gosa_si_tmp_sources_list";
2731         # Remove old file
2732         if(stat($result)) {
2733                 unlink($result);
2734                 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7); 
2735         }
2737         my $fh;
2738         open($fh, ">$result");
2739         if (not defined $fh) {
2740                 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7); 
2741                 return undef;
2742         }
2743         if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2744                 my $ldap_handle = &get_ldap_handle();
2745                 my $mesg=$ldap_handle->search(
2746                         base    => $main::ldap_server_dn,
2747                         scope   => 'base',
2748                         attrs   => 'FAIrepository',
2749                         filter  => 'objectClass=FAIrepositoryServer'
2750                 );
2751                 &release_ldap_handle($ldap_handle);
2752                 if($mesg->count) {
2753                         foreach my $entry(@{$mesg->{'entries'}}) {
2754                                 foreach my $value(@{$entry->get_value('FAIrepository', asref => 1)}) {
2755                                         my ($server, $tag, $release, $sections)= split /\|/, $value;
2756                                         my $line = "deb $server $release";
2757                                         $sections =~ s/,/ /g;
2758                                         $line.= " $sections";
2759                                         print $fh $line."\n";
2760                                 }
2761                         }
2762                 }
2763         } else {
2764                 if (defined $main::ldap_server_dn){
2765                         &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1); 
2766                 } else {
2767                         &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2768                 }
2769         }
2770         close($fh);
2772         return $result;
2776 sub run_create_packages_list_db {
2777     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2778         my $session_id = $session->ID;
2779         my $task = POE::Wheel::Run->new(
2780                                         Priority => +20,
2781                                         Program => sub {&create_packages_list_db(undef, $session_id)},
2782                                         StdoutEvent  => "session_run_result",
2783                                         StderrEvent  => "session_run_debug",
2784                                         CloseEvent   => "session_run_done",
2785                                         );
2786         $heap->{task}->{ $task->ID } = $task;
2790 sub create_packages_list_db {
2791         my ($sources_file, $session_id) = @_;
2792         
2793         # it should not be possible to trigger a recreation of packages_list_db
2794         # while packages_list_db is under construction, so set flag packages_list_under_construction
2795         # which is tested befor recreation can be started
2796         if (-r $packages_list_under_construction) {
2797                 daemon_log("$session_id WARNING: packages_list_db is right now under construction, please wait until this process is finished", 3);
2798                 return;
2799         } else {
2800                 daemon_log("$session_id INFO: create_packages_list_db: start", 5); 
2801                 # set packages_list_under_construction to true
2802                 system("touch $packages_list_under_construction");
2803                 @packages_list_statements=();
2804         }
2806         if (not defined $session_id) { $session_id = 0; }
2808         if (not defined $sources_file) { 
2809                 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5); 
2810                 $sources_file = &create_sources_list($session_id);
2811         }
2813         if (not defined $sources_file) {
2814                 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1); 
2815                 unlink($packages_list_under_construction);
2816                 return;
2817         }
2819         my $line;
2821         open(CONFIG, "<$sources_file") or do {
2822                 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2823                 unlink($packages_list_under_construction);
2824                 return;
2825         };
2827         # Read lines
2828         while ($line = <CONFIG>){
2829                 # Unify
2830                 chop($line);
2831                 $line =~ s/^\s+//;
2832                 $line =~ s/^\s+/ /;
2834                 # Strip comments
2835                 $line =~ s/#.*$//g;
2837                 # Skip empty lines
2838                 if ($line =~ /^\s*$/){
2839                         next;
2840                 }
2842                 # Interpret deb line
2843                 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2844                         my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2845                         my $section;
2846                         foreach $section (split(' ', $sections)){
2847                                 &parse_package_info( $baseurl, $dist, $section, $session_id );
2848                         }
2849                 }
2850         }
2852         close (CONFIG);
2854         if(keys(%repo_dirs)) {
2855                 find(\&cleanup_and_extract, keys( %repo_dirs ));
2856                 &main::strip_packages_list_statements();
2857                 $packages_list_db->exec_statementlist(\@packages_list_statements);
2858         }
2859         unlink($packages_list_under_construction);
2860         daemon_log("$session_id INFO: create_packages_list_db: finished", 5); 
2861         return;
2864 # This function should do some intensive task to minimize the db-traffic
2865 sub strip_packages_list_statements {
2866         my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2867         my @new_statement_list=();
2868         my $hash;
2869         my $insert_hash;
2870         my $update_hash;
2871         my $delete_hash;
2872         my $known_packages_hash;
2873         my $local_timestamp=get_time();
2875         foreach my $existing_entry (@existing_entries) {
2876                 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
2877         }
2879         foreach my $statement (@packages_list_statements) {
2880                 if($statement =~ /^INSERT/i) {
2881                         # Assign the values from the insert statement
2882                         my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~ 
2883                         /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
2884                         if(exists($hash->{$distribution}->{$package}->{$version})) {
2885                                 # If section or description has changed, update the DB
2886                                 if( 
2887                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or 
2888                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
2889                                 ) {
2890                                         @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
2891                                 } else {
2892                                         # package is already present in database. cache this knowledge for later use
2893                                         @{$known_packages_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2894                                 }
2895                         } else {
2896                                 # Insert a non-existing entry to db
2897                                 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2898                         }
2899                 } elsif ($statement =~ /^UPDATE/i) {
2900                         my ($template,$package,$version) = ($1,$2,$3) if $statement =~
2901                         /^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;
2902                         foreach my $distribution (keys %{$hash}) {
2903                                 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
2904                                         # update the insertion hash to execute only one query per package (insert instead insert+update)
2905                                         @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
2906                                 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
2907                                         if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
2908                                                 my $section;
2909                                                 my $description;
2910                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
2911                                                         length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
2912                                                         $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
2913                                                 }
2914                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2915                                                         $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
2916                                                 }
2917                                                 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2918                                         }
2919                                 }
2920                         }
2921                 }
2922         }
2924         # Check for orphaned entries
2925         foreach my $existing_entry (@existing_entries) {
2926                 my $distribution= @{$existing_entry}[0];
2927                 my $package= @{$existing_entry}[1];
2928                 my $version= @{$existing_entry}[2];
2929                 my $section= @{$existing_entry}[3];
2931                 if(
2932                         exists($insert_hash->{$distribution}->{$package}->{$version}) ||
2933                         exists($update_hash->{$distribution}->{$package}->{$version}) ||
2934                         exists($known_packages_hash->{$distribution}->{$package}->{$version})
2935                 ) {
2936                         next;
2937                 } else {
2938                         # Insert entry to delete hash
2939                         @{$delete_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section);
2940                 }
2941         }
2943         # unroll the insert hash
2944         foreach my $distribution (keys %{$insert_hash}) {
2945                 foreach my $package (keys %{$insert_hash->{$distribution}}) {
2946                         foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
2947                                 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
2948                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
2949                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
2950                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
2951                                 ."'$local_timestamp')";
2952                         }
2953                 }
2954         }
2956         # unroll the update hash
2957         foreach my $distribution (keys %{$update_hash}) {
2958                 foreach my $package (keys %{$update_hash->{$distribution}}) {
2959                         foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
2960                                 my $set = "";
2961                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
2962                                         $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
2963                                 }
2964                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2965                                         $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
2966                                 }
2967                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
2968                                         $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
2969                                 }
2970                                 if(defined($set) and length($set) > 0) {
2971                                         $set .= "timestamp = '$local_timestamp'";
2972                                 } else {
2973                                         next;
2974                                 }
2975                                 push @new_statement_list, 
2976                                 "UPDATE $main::packages_list_tn SET $set WHERE"
2977                                 ." distribution = '$distribution'"
2978                                 ." AND package = '$package'"
2979                                 ." AND version = '$version'";
2980                         }
2981                 }
2982         }
2983         
2984         # unroll the delete hash
2985         foreach my $distribution (keys %{$delete_hash}) {
2986                 foreach my $package (keys %{$delete_hash->{$distribution}}) {
2987                         foreach my $version (keys %{$delete_hash->{$distribution}->{$package}}) {
2988                                 my $section = @{$delete_hash->{$distribution}->{$package}->{$version}}[3];
2989                                 push @new_statement_list, "DELETE FROM $main::packages_list_tn WHERE distribution='$distribution' AND package='$package' AND version='$version' AND section='$section'";
2990                         }
2991                 }
2992         }
2994         unshift(@new_statement_list, "VACUUM");
2996         @packages_list_statements = @new_statement_list;
3000 sub parse_package_info {
3001     my ($baseurl, $dist, $section, $session_id)= @_;
3002     my ($package);
3003     if (not defined $session_id) { $session_id = 0; }
3004     my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
3005     $repo_dirs{ "${repo_path}/pool" } = 1;
3007     foreach $package ("Packages.gz"){
3008         daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
3009         get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
3010         parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
3011     }
3012     
3016 sub get_package {
3017     my ($url, $dest, $session_id)= @_;
3018     if (not defined $session_id) { $session_id = 0; }
3020     my $tpath = dirname($dest);
3021     -d "$tpath" || mkpath "$tpath";
3023     # This is ugly, but I've no time to take a look at "how it works in perl"
3024     if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
3025         system("gunzip -cd '$dest' > '$dest.in'");
3026         daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 5);
3027         unlink($dest);
3028         daemon_log("$session_id DEBUG: delete file '$dest'", 5); 
3029     } else {
3030         daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' into '$dest' failed!", 1);
3031     }
3032     return 0;
3036 sub parse_package {
3037     my ($path, $dist, $srv_path, $session_id)= @_;
3038     if (not defined $session_id) { $session_id = 0;}
3039     my ($package, $version, $section, $description);
3040     my $PACKAGES;
3041     my $timestamp = &get_time();
3043     if(not stat("$path.in")) {
3044         daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
3045         return;
3046     }
3048     open($PACKAGES, "<$path.in");
3049     if(not defined($PACKAGES)) {
3050         daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1); 
3051         return;
3052     }
3054     # Read lines
3055     while (<$PACKAGES>){
3056         my $line = $_;
3057         # Unify
3058         chop($line);
3060         # Use empty lines as a trigger
3061         if ($line =~ /^\s*$/){
3062             my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
3063             push(@packages_list_statements, $sql);
3064             $package = "none";
3065             $version = "none";
3066             $section = "none";
3067             $description = "none"; 
3068             next;
3069         }
3071         # Trigger for package name
3072         if ($line =~ /^Package:\s/){
3073             ($package)= ($line =~ /^Package: (.*)$/);
3074             next;
3075         }
3077         # Trigger for version
3078         if ($line =~ /^Version:\s/){
3079             ($version)= ($line =~ /^Version: (.*)$/);
3080             next;
3081         }
3083         # Trigger for description
3084         if ($line =~ /^Description:\s/){
3085             ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
3086             next;
3087         }
3089         # Trigger for section
3090         if ($line =~ /^Section:\s/){
3091             ($section)= ($line =~ /^Section: (.*)$/);
3092             next;
3093         }
3095         # Trigger for filename
3096         if ($line =~ /^Filename:\s/){
3097             my ($filename) = ($line =~ /^Filename: (.*)$/);
3098             store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
3099             next;
3100         }
3101     }
3103     close( $PACKAGES );
3104     unlink( "$path.in" );
3108 sub store_fileinfo {
3109     my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
3111     my %fileinfo = (
3112         'package' => $package,
3113         'dist' => $dist,
3114         'version' => $vers,
3115     );
3117     $repo_files{ "${srvdir}/$file" } = \%fileinfo;
3121 sub cleanup_and_extract {
3122         my $fileinfo = $repo_files{ $File::Find::name };
3124         if( defined $fileinfo ) {
3125                 my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
3126                 my $sql;
3127                 my $package = $fileinfo->{ 'package' };
3128                 my $newver = $fileinfo->{ 'version' };
3130                 mkpath($dir);
3131                 system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
3133                 if( -f "$dir/DEBIAN/templates" ) {
3135                         daemon_log("0 DEBUG: Found debconf templates in '$package' - $newver", 7);
3137                         my $tmpl= ""; {
3138                                 local $/=undef;
3139                                 open FILE, "$dir/DEBIAN/templates";
3140                                 $tmpl = &encode_base64(<FILE>);
3141                                 close FILE;
3142                         }
3143                         rmtree("$dir/DEBIAN/templates");
3145                         $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
3146                         push @packages_list_statements, $sql;
3147                 }
3148         }
3150         return;
3154 sub register_at_foreign_servers {   
3155     my ($kernel) = $_[KERNEL];
3157     # hole alle bekannten server aus known_server_db
3158     my $server_sql = "SELECT * FROM $known_server_tn";
3159     my $server_res = $known_server_db->exec_statement($server_sql);
3161     # no entries in known_server_db
3162     if (not ref(@$server_res[0]) eq "ARRAY") { 
3163         # TODO
3164     }
3166     # detect already connected clients
3167     my $client_sql = "SELECT * FROM $known_clients_tn"; 
3168     my $client_res = $known_clients_db->exec_statement($client_sql);
3170     # send my server details to all other gosa-si-server within the network
3171     foreach my $hit (@$server_res) {
3172         my $hostname = @$hit[0];
3173         my $hostkey = &create_passwd;
3175         # add already connected clients to registration message 
3176         my $myhash = &create_xml_hash('new_server', $server_address, $hostname);
3177         &add_content2xml_hash($myhash, 'key', $hostkey);
3178         map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res);
3180         # add locally loaded gosa-si modules to registration message
3181         my $loaded_modules = {};
3182         while (my ($package, $pck_info) = each %$known_modules) {
3183                                                 next if ((!defined(@$pck_info[2])) || (!(ref (@$pck_info[2]) eq 'HASH')));
3184                                                 foreach my $act_module (keys(%{@$pck_info[2]})) {
3185                                                         $loaded_modules->{$act_module} = ""; 
3186                                                 }
3187         }
3189         map(&add_content2xml_hash($myhash, "loaded_modules", $_), keys(%$loaded_modules));
3191         # add macaddress to registration message
3192         my ($host_ip, $host_port) = split(/:/, $hostname);
3193         my $local_ip = &get_local_ip_for_remote_ip($host_ip);
3194         my $network_interface= &get_interface_for_ip($local_ip);
3195         my $host_mac = &get_mac_for_interface($network_interface);
3196         &add_content2xml_hash($myhash, 'macaddress', $host_mac);
3197         
3198         # build registration message and send it
3199         my $foreign_server_msg = &create_xml_string($myhash);
3200         my $error = &send_msg_to_target($foreign_server_msg, $hostname, $ServerPackages_key, "new_server", 0); 
3201     }
3202     
3203     $kernel->delay_set("register_at_foreign_servers", $foreign_servers_register_delay); 
3204     return;
3208 #==== MAIN = main ==============================================================
3209 #  parse commandline options
3210 Getopt::Long::Configure( "bundling" );
3211 GetOptions("h|help" => \&usage,
3212         "c|config=s" => \$cfg_file,
3213         "f|foreground" => \$foreground,
3214         "v|verbose+" => \$verbose,
3215         "no-arp+" => \$no_arp,
3216            );
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 # forward error messages to logfile
3226 if( ! $foreground ) {
3227   open( STDIN,  '+>/dev/null' );
3228   open( STDOUT, '+>&STDIN'    );
3229   open( STDERR, '+>&STDIN'    );
3232 # Just fork, if we are not in foreground mode
3233 if( ! $foreground ) { 
3234     chdir '/'                 or die "Can't chdir to /: $!";
3235     $pid = fork;
3236     setsid                    or die "Can't start a new session: $!";
3237     umask 0;
3238 } else { 
3239     $pid = $$; 
3242 # Do something useful - put our PID into the pid_file
3243 if( 0 != $pid ) {
3244     open( LOCK_FILE, ">$pid_file" );
3245     print LOCK_FILE "$pid\n";
3246     close( LOCK_FILE );
3247     if( !$foreground ) { 
3248         exit( 0 ) 
3249     };
3252 # parse head url and revision from svn
3253 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
3254 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
3255 $server_headURL = defined $1 ? $1 : 'unknown' ;
3256 $server_revision = defined $2 ? $2 : 'unknown' ;
3257 if ($server_headURL =~ /\/tag\// || 
3258         $server_headURL =~ /\/branches\// ) {
3259     $server_status = "stable"; 
3260 } else {
3261     $server_status = "developmental" ;
3263 # Prepare log file and set permissions
3264 $root_uid = getpwnam('root');
3265 $adm_gid = getgrnam('adm');
3266 open(FH, ">>$log_file");
3267 close FH;
3268 chmod(0440, $log_file);
3269 chown($root_uid, $adm_gid, $log_file);
3270 chown($root_uid, $adm_gid, "/var/lib/gosa-si");
3272 daemon_log(" ", 1);
3273 daemon_log("$0 started!", 1);
3274 daemon_log("status: $server_status", 1);
3275 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1); 
3277 # Create a pool of LDAP handles
3278 $ldap_factory =  ResourcePool::Factory::Net::LDAP->new($ldap_uri, version => $ldap_version);
3279 $ldap_factory->bind($ldap_admin_dn, password=>$ldap_admin_password);
3280 $ldap_pool = ResourcePool->new($ldap_factory,
3281                 Max         => 10,
3282                 #MaxTry      => 1,
3283                 #SleepOnFail    => [0, 0, 1, 1],
3284                 PreCreate       => 5,
3285 );
3288 # Buildup data bases
3290     no strict "refs";
3292     if ($db_module eq "DBmysql") {
3293         # connect to incoming_db
3294         $incoming_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3296         # connect to gosa-si job queue
3297         $job_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3299         # connect to known_clients_db
3300         $known_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3302         # connect to foreign_clients_db
3303         $foreign_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3305         # connect to known_server_db
3306         $known_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3308         # connect to login_usr_db
3309         $login_users_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3311         # connect to fai_server_db 
3312         $fai_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3314         # connect to fai_release_db
3315         $fai_release_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3317         # connect to packages_list_db
3318         $packages_list_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3320         # connect to messaging_db
3321         $messaging_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3323     } elsif ($db_module eq "DBsqlite") {
3324         # connect to incoming_db
3325         unlink($incoming_file_name);
3326         $incoming_db = GOSA::DBsqlite->new($incoming_file_name);
3327         
3328         # connect to gosa-si job queue
3329         unlink($job_queue_file_name);  ## just for debugging
3330         $job_db = GOSA::DBsqlite->new($job_queue_file_name);
3331         chmod(0640, $job_queue_file_name);
3332         chown($root_uid, $adm_gid, $job_queue_file_name);
3333         
3334         # connect to known_clients_db
3335         unlink($known_clients_file_name);   ## just for debugging
3336         $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
3337         chmod(0640, $known_clients_file_name);
3338         chown($root_uid, $adm_gid, $known_clients_file_name);
3339         
3340         # connect to foreign_clients_db
3341         unlink($foreign_clients_file_name);
3342         $foreign_clients_db = GOSA::DBsqlite->new($foreign_clients_file_name);
3343         chmod(0640, $foreign_clients_file_name);
3344         chown($root_uid, $adm_gid, $foreign_clients_file_name);
3345         
3346         # connect to known_server_db
3347         unlink($known_server_file_name);
3348         $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
3349         chmod(0640, $known_server_file_name);
3350         chown($root_uid, $adm_gid, $known_server_file_name);
3351         
3352         # connect to login_usr_db
3353         unlink($login_users_file_name);
3354         $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
3355         chmod(0640, $login_users_file_name);
3356         chown($root_uid, $adm_gid, $login_users_file_name);
3357         
3358         # connect to fai_server_db
3359         unlink($fai_server_file_name);
3360         $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
3361         chmod(0640, $fai_server_file_name);
3362         chown($root_uid, $adm_gid, $fai_server_file_name);
3363         
3364         # connect to fai_release_db
3365         unlink($fai_release_file_name);
3366         $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
3367         chmod(0640, $fai_release_file_name);
3368         chown($root_uid, $adm_gid, $fai_release_file_name);
3369         
3370         # connect to packages_list_db
3371         #unlink($packages_list_file_name);
3372         unlink($packages_list_under_construction);
3373         $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
3374         chmod(0640, $packages_list_file_name);
3375         chown($root_uid, $adm_gid, $packages_list_file_name);
3376         
3377         # connect to messaging_db
3378         unlink($messaging_file_name);
3379         $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
3380         chmod(0640, $messaging_file_name);
3381         chown($root_uid, $adm_gid, $messaging_file_name);
3382     }
3386 # Creating tables
3387 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
3388 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
3389 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
3390 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
3391 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
3392 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
3393 $foreign_clients_db->create_table($foreign_clients_tn, \@foreign_clients_col_names);
3394 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
3395 $incoming_db->create_table($incoming_tn, \@incoming_col_names);
3396 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
3398 # create xml object used for en/decrypting
3399 $xml = new XML::Simple();
3402 # foreign servers 
3403 my @foreign_server_list;
3405 # add foreign server from cfg file
3406 if ($foreign_server_string ne "") {
3407     my @cfg_foreign_server_list = split(",", $foreign_server_string);
3408     foreach my $foreign_server (@cfg_foreign_server_list) {
3409         push(@foreign_server_list, $foreign_server);
3410     }
3412     daemon_log("0 INFO: found foreign server in config file: ".join(", ", @foreign_server_list), 5);
3415 # Perform a DNS lookup for server registration if flag is true
3416 if ($dns_lookup eq "true") {
3417     # Add foreign server from dns
3418     my @tmp_servers;
3419     if (not $server_domain) {
3420         # Try our DNS Searchlist
3421         for my $domain(get_dns_domains()) {
3422             chomp($domain);
3423             my ($tmp_domains, $error_string) = &get_server_addresses($domain);
3424             if(@$tmp_domains) {
3425                 for my $tmp_server(@$tmp_domains) {
3426                     push @tmp_servers, $tmp_server;
3427                 }
3428             }
3429         }
3430         if(@tmp_servers && length(@tmp_servers)==0) {
3431             daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3432         }
3433     } else {
3434         @tmp_servers = &get_server_addresses($server_domain);
3435         if( 0 == @tmp_servers ) {
3436             daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3437         }
3438     }
3440     daemon_log("0 INFO: found foreign server via DNS ".join(", ", @tmp_servers), 5);    
3442     foreach my $server (@tmp_servers) { 
3443         unshift(@foreign_server_list, $server); 
3444     }
3445 } else {
3446     daemon_log("0 INFO: DNS lookup for server registration is disabled", 5);
3450 # eliminate duplicate entries
3451 @foreign_server_list = &del_doubles(@foreign_server_list);
3452 my $all_foreign_server = join(", ", @foreign_server_list);
3453 daemon_log("0 INFO: found foreign server in config file and DNS: '$all_foreign_server'", 5);
3455 # add all found foreign servers to known_server
3456 my $cur_timestamp = &get_time();
3457 foreach my $foreign_server (@foreign_server_list) {
3459         # do not add myself to known_server_db
3460         if (&is_local($foreign_server)) { next; }
3461         ######################################
3463     my $res = $known_server_db->add_dbentry( {table=>$known_server_tn, 
3464             primkey=>['hostname'],
3465             hostname=>$foreign_server,
3466             macaddress=>"",
3467             status=>'not_yet_registered',
3468             hostkey=>"none",
3469             loaded_modules => "none", 
3470             timestamp=>$cur_timestamp,
3471             } );
3475 # Import all modules
3476 &import_modules;
3478 # Check wether all modules are gosa-si valid passwd check
3479 &password_check;
3481 # Prepare for using Opsi 
3482 if ($opsi_enabled eq "true") {
3483     use JSON::RPC::Client;
3484     use XML::Quote qw(:all);
3485     $opsi_url= "https://".$opsi_admin.":".$opsi_password."@".$opsi_server.":4447/rpc";
3486     $opsi_client = new JSON::RPC::Client;
3490 POE::Component::Server::TCP->new(
3491         Alias => "TCP_SERVER",
3492         Port => $server_port,
3493         ClientInput => sub {
3494                 my ($kernel, $input, $heap, $session) = @_[KERNEL, ARG0, HEAP, SESSION];
3495         my $session_id = $session->ID;
3496         my $remote_ip = $heap->{'remote_ip'};
3497                 push(@msgs_to_decrypt, $input);
3498         &daemon_log("$session_id DEBUG: incoming message from '$remote_ip'", 7);
3499                 $kernel->yield("msg_to_decrypt");
3500         },
3501         InlineStates => {
3502                 msg_to_decrypt => \&msg_to_decrypt,
3503                 next_task => \&next_task,
3504                 task_result => \&handle_task_result,
3505                 task_done   => \&handle_task_done,
3506                 task_debug  => \&handle_task_debug,
3507                 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3508         }
3509 );
3511 daemon_log("0 INFO: start socket for incoming xml messages at port '$server_port' ", 1);
3513 # create session for repeatedly checking the job queue for jobs
3514 POE::Session->create(
3515         inline_states => {
3516                 _start => \&session_start,
3517         register_at_foreign_servers => \&register_at_foreign_servers,
3518         sig_handler => \&sig_handler,
3519         next_task => \&next_task,
3520         task_result => \&handle_task_result,
3521         task_done   => \&handle_task_done,
3522         task_debug  => \&handle_task_debug,
3523         watch_for_next_tasks => \&watch_for_next_tasks,
3524         watch_for_new_messages => \&watch_for_new_messages,
3525         watch_for_delivery_messages => \&watch_for_delivery_messages,
3526         watch_for_done_messages => \&watch_for_done_messages,
3527                 watch_for_new_jobs => \&watch_for_new_jobs,
3528         watch_for_modified_jobs => \&watch_for_modified_jobs,
3529         watch_for_done_jobs => \&watch_for_done_jobs,
3530         watch_for_opsi_jobs => \&watch_for_opsi_jobs,
3531         watch_for_old_known_clients => \&watch_for_old_known_clients,
3532         create_packages_list_db => \&run_create_packages_list_db,
3533         create_fai_server_db => \&run_create_fai_server_db,
3534         create_fai_release_db => \&run_create_fai_release_db,
3535                 recreate_packages_db => \&run_recreate_packages_db,
3536         session_run_result => \&session_run_result,
3537         session_run_debug => \&session_run_debug,
3538         session_run_done => \&session_run_done,
3539         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3540         }
3541 );
3544 POE::Kernel->run();
3545 exit;