Code

932f2878fbe313108db5e418d02caab8ae9b14f4
[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 queue for jobs with executable timestamp
1746                 my $timestamp = &get_time();
1747                 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE siserver='localhost' AND 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         return $ldap_handle;
2162 sub release_ldap_handle {
2163         my ($ldap_handle) = @_ ;
2164         $ldap_pool->free($ldap_handle);
2165         return;
2169 sub change_fai_state {
2170     my ($st, $targets, $session_id) = @_;
2171     $session_id = 0 if not defined $session_id;
2172     # Set FAI state to localboot
2173     my %mapActions= (
2174         reboot    => '',
2175         update    => 'softupdate',
2176         localboot => 'localboot',
2177         reinstall => 'install',
2178         rescan    => '',
2179         wake      => '',
2180         memcheck  => 'memcheck',
2181         sysinfo   => 'sysinfo',
2182         install   => 'install',
2183     );
2185     # Return if this is unknown
2186     if (!exists $mapActions{ $st }){
2187         daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1); 
2188       return;
2189     }
2191     my $state= $mapActions{ $st };
2193     #if( defined($ldap_handle) ) {
2195       # Build search filter for hosts
2196         my $search= "(&(objectClass=GOhard)";
2197         foreach (@{$targets}){
2198             $search.= "(macAddress=$_)";
2199         }
2200         $search.= ")";
2202       # If there's any host inside of the search string, procress them
2203         if (!($search =~ /macAddress/)){
2204             daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);    
2205             return;
2206         }
2208         my $ldap_handle = &get_ldap_handle($session_id);
2209       # Perform search for Unit Tag
2210       my $mesg = $ldap_handle->search(
2211           base   => $ldap_base,
2212           scope  => 'sub',
2213           attrs  => ['dn', 'FAIstate', 'objectClass'],
2214           filter => "$search"
2215           );
2217           if ($mesg->count) {
2218                   my @entries = $mesg->entries;
2219                   if (0 == @entries) {
2220                                   daemon_log("$session_id ERROR: ldap search failed: ldap_base=$ldap_base, filter=$search", 1); 
2221                   }
2223                   foreach my $entry (@entries) {
2224                           # Only modify entry if it is not set to '$state'
2225                           if ($entry->get_value("FAIstate") ne "$state"){
2226                                   daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
2227                                   my $result;
2228                                   my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
2229                                   if (exists $tmp{'FAIobject'}){
2230                                           if ($state eq ''){
2231                                                   $result= $ldap_handle->modify($entry->dn, changes => [ delete => [ FAIstate => [] ] ]);
2232                                           } else {
2233                                                   $result= $ldap_handle->modify($entry->dn, changes => [ replace => [ FAIstate => $state ] ]);
2234                                           }
2235                                   } elsif ($state ne ''){
2236                                           $result= $ldap_handle->modify($entry->dn, changes => [ add => [ objectClass => 'FAIobject' ], add => [ FAIstate => $state ] ]);
2237                                   }
2239                                   # Errors?
2240                                   if ($result->code){
2241                                           daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2242                                   }
2243                           } else {
2244                                   daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 7); 
2245                           }  
2246                   }
2247           } else {
2248                 daemon_log("$session_id ERROR: LDAP search failed: ldap_base=$ldap_base, filter=$search", 1);
2249           }
2250           &release_ldap_handle($ldap_handle);             
2252     # if no ldap handle defined
2253     #} else {
2254     #    daemon_log("$session_id ERROR: no LDAP handle defined for update FAIstate", 1); 
2255     #}
2257         return;
2261 sub change_goto_state {
2262     my ($st, $targets, $session_id) = @_;
2263     $session_id = 0  if not defined $session_id;
2265     # Switch on or off?
2266     my $state= $st eq 'active' ? 'active': 'locked';
2268     my $ldap_handle = &get_ldap_handle($session_id);
2269     if( defined($ldap_handle) ) {
2271       # Build search filter for hosts
2272       my $search= "(&(objectClass=GOhard)";
2273       foreach (@{$targets}){
2274         $search.= "(macAddress=$_)";
2275       }
2276       $search.= ")";
2278       # If there's any host inside of the search string, procress them
2279       if (!($search =~ /macAddress/)){
2280         return;
2281       }
2283       # Perform search for Unit Tag
2284       my $mesg = $ldap_handle->search(
2285           base   => $ldap_base,
2286           scope  => 'sub',
2287           attrs  => ['dn', 'gotoMode'],
2288           filter => "$search"
2289           );
2291       if ($mesg->count) {
2292         my @entries = $mesg->entries;
2293         foreach my $entry (@entries) {
2295           # Only modify entry if it is not set to '$state'
2296           if ($entry->get_value("gotoMode") ne $state){
2298             daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
2299             my $result;
2300             $result= $ldap_handle->modify($entry->dn, changes => [replace => [ gotoMode => $state ] ]);
2302             # Errors?
2303             if ($result->code){
2304               &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2305             }
2307           }
2308         }
2309       } else {
2310                 daemon_log("$session_id ERROR: LDAP search failed in function change_goto_state: ldap_base=$ldap_base, filter=$search", 1);
2311           }
2313     }
2314         &release_ldap_handle($ldap_handle);
2315         return;
2319 sub run_recreate_packages_db {
2320     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2321     my $session_id = $session->ID;
2322         &main::daemon_log("$session_id INFO: Recreating FAI Packages DB ('$fai_release_tn', '$fai_server_tn', '$packages_list_tn')", 5);
2323         $kernel->yield('create_fai_release_db', $fai_release_tn);
2324         $kernel->yield('create_fai_server_db', $fai_server_tn);
2325         return;
2329 sub run_create_fai_server_db {
2330     my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2331     my $session_id = $session->ID;
2332         my $ldap_handle = &get_ldap_handle();
2333         if (not defined $ldap_handle) {
2334                 $kernel->delay_set('create_fai_server_db', 1, $table_name);
2335                 return;
2336         }
2337     my $task = POE::Wheel::Run->new(
2338             Program => sub { &create_fai_server_db($table_name,$kernel, undef, $session_id, $ldap_handle) },
2339             StdoutEvent  => "session_run_result",
2340             StderrEvent  => "session_run_debug",
2341             CloseEvent   => "session_run_done",
2342             );
2344     $heap->{task}->{ $task->ID } = $task;
2345         $heap->{ldap_handle}->{$task->ID} = $ldap_handle;
2346     return;
2350 sub create_fai_server_db {
2351         my ($table_name, $kernel, $dont_create_packages_list, $session_id, $ldap_handle) = @_;
2352         my $result;
2354         if (not defined $session_id) { $session_id = 0; }
2355         if(defined($ldap_handle)) {
2356                 daemon_log("$session_id INFO: create_fai_server_db: start", 5);
2357                 my $mesg= $ldap_handle->search(
2358                         base   => $ldap_base,
2359                         scope  => 'sub',
2360                         attrs  => ['FAIrepository', 'gosaUnitTag'],
2361                         filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
2362                 );
2363                 if($mesg->{'resultCode'} == 0 &&
2364                         $mesg->count != 0) {
2365                         foreach my $entry (@{$mesg->{entries}}) {
2366                                 if($entry->exists('FAIrepository')) {
2367                                         # Add an entry for each Repository configured for server
2368                                         foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
2369                                                 my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
2370                                                 my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
2371                                                 $result= $fai_server_db->add_dbentry( { 
2372                                                                 table => $table_name,
2373                                                                 primkey => ['server', 'fai_release', 'tag'],
2374                                                                 server => $tmp_url,
2375                                                                 fai_release => $tmp_release,
2376                                                                 sections => $tmp_sections,
2377                                                                 tag => (length($tmp_tag)>0)?$tmp_tag:"",
2378                                                         } );
2379                                         }
2380                                 }
2381                         }
2382                 }
2383                 daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
2385                 # TODO: Find a way to post the 'create_packages_list_db' event
2386                 if(not defined($dont_create_packages_list)) {
2387                         &create_packages_list_db(undef, $session_id);
2388                 }
2389         }       
2391         return $result;
2395 sub run_create_fai_release_db {
2396         my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2397         my $session_id = $session->ID;
2398         my $ldap_handle = &get_ldap_handle();
2399         if (not defined $ldap_handle) {
2400                 $kernel->delay_set('create_fai_release_db', 1, $table_name);
2401                 return;
2402         }
2403         my $task = POE::Wheel::Run->new(
2404                 Program => sub { &create_fai_release_db($table_name, $session_id, $ldap_handle) },
2405                 StdoutEvent  => "session_run_result",
2406                 StderrEvent  => "session_run_debug",
2407                 CloseEvent   => "session_run_done",
2408         );
2410         $heap->{task}->{ $task->ID } = $task;
2411         $heap->{ldap_handle}->{$task->ID} = $ldap_handle;
2412         return;
2416 sub create_fai_release_db {
2417         my ($table_name, $session_id, $ldap_handle) = @_;
2418         my $result;
2420         # used for logging
2421         if (not defined $session_id) { $session_id = 0; }
2423         #my $ldap_handle = &get_ldap_handle();
2424         if(defined($ldap_handle)) {
2425                 daemon_log("$session_id INFO: create_fai_release_db: start",5);
2426                 my $mesg= $ldap_handle->search(
2427                         base   => $ldap_base,
2428                         scope  => 'sub',
2429                         attrs  => [],
2430                         filter => "(&(objectClass=organizationalUnit)(ou=fai))",
2431                 );
2432                 if(($mesg->code == 0) && ($mesg->count != 0))
2433                 {
2434                         daemon_log("$session_id DEBUG: create_fai_release_db: count " . $mesg->count,8);
2436                         # Walk through all possible FAI container ou's
2437                         my @sql_list;
2438                         my $timestamp= &get_time();
2439                         foreach my $ou (@{$mesg->{entries}}) {
2440                                 my $tmp_classes= resolve_fai_classes($ou->dn, $ldap_handle, $session_id);
2441                                 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
2442                                         my @tmp_array=get_fai_release_entries($tmp_classes);
2443                                         if(@tmp_array) {
2444                                                 foreach my $entry (@tmp_array) {
2445                                                         if(defined($entry) && ref($entry) eq 'HASH') {
2446                                                                 my $sql= 
2447                                                                 "INSERT INTO $table_name "
2448                                                                 ."(timestamp, fai_release, class, type, state) VALUES ("
2449                                                                 .$timestamp.","
2450                                                                 ."'".$entry->{'release'}."',"
2451                                                                 ."'".$entry->{'class'}."',"
2452                                                                 ."'".$entry->{'type'}."',"
2453                                                                 ."'".$entry->{'state'}."')";
2454                                                                 push @sql_list, $sql;
2455                                                         }
2456                                                 }
2457                                         }
2458                                 }
2459                         }
2461                         daemon_log("$session_id DEBUG: create_fai_release_db: Inserting ".scalar @sql_list." entries to DB",8);
2462                         if(@sql_list) {
2463                                 unshift @sql_list, "VACUUM";
2464                                 unshift @sql_list, "DELETE FROM $table_name";
2465                                 $fai_release_db->exec_statementlist(\@sql_list);
2466                         }
2467                         daemon_log("$session_id DEBUG: create_fai_release_db: Done with inserting",7);
2468                 } else {
2469                         daemon_log("$session_id INFO: create_fai_release_db: error: " . $mesg->code, 5);
2470                 }
2471                 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
2472         }
2473         #&release_ldap_handle($ldap_handle);
2474         return $result;
2477 sub get_fai_types {
2478         my $tmp_classes = shift || return undef;
2479         my @result;
2481         foreach my $type(keys %{$tmp_classes}) {
2482                 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
2483                         my $entry = {
2484                                 type => $type,
2485                                 state => $tmp_classes->{$type}[0],
2486                         };
2487                         push @result, $entry;
2488                 }
2489         }
2491         return @result;
2494 sub get_fai_state {
2495         my $result = "";
2496         my $tmp_classes = shift || return $result;
2498         foreach my $type(keys %{$tmp_classes}) {
2499                 if(defined($tmp_classes->{$type}[0])) {
2500                         $result = $tmp_classes->{$type}[0];
2501                         
2502                 # State is equal for all types in class
2503                         last;
2504                 }
2505         }
2507         return $result;
2510 sub resolve_fai_classes {
2511         my ($fai_base, $ldap_handle, $session_id) = @_;
2512         if (not defined $session_id) { $session_id = 0; }
2513         my $result;
2514         my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
2515         my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
2516         my $fai_classes;
2518         daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base",7);
2519         my $mesg= $ldap_handle->search(
2520                 base   => $fai_base,
2521                 scope  => 'sub',
2522                 attrs  => ['cn','objectClass','FAIstate'],
2523                 filter => $fai_filter,
2524         );
2525         daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries",7);
2527         if($mesg->{'resultCode'} == 0 &&
2528                 $mesg->count != 0) {
2529                 foreach my $entry (@{$mesg->{entries}}) {
2530                         if($entry->exists('cn')) {
2531                                 my $tmp_dn= $entry->dn();
2532                                 $tmp_dn= substr( $tmp_dn, 0, length($tmp_dn)
2533                                         - length($fai_base) - 1 );
2535                                 # Skip classname and ou dn parts for class
2536                                 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?)$/;
2538                                 # Skip classes without releases
2539                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2540                                         next;
2541                                 }
2543                                 my $tmp_cn= $entry->get_value('cn');
2544                                 my $tmp_state= $entry->get_value('FAIstate');
2546                                 my $tmp_type;
2547                                 # Get FAI type
2548                                 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
2549                                         if(grep $_ eq $oclass, @possible_fai_classes) {
2550                                                 $tmp_type= $oclass;
2551                                                 last;
2552                                         }
2553                                 }
2555                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2556                                         # A Subrelease
2557                                         my @sub_releases = split(/,/, $tmp_release);
2559                                         # Walk through subreleases and build hash tree
2560                                         my $hash;
2561                                         while(my $tmp_sub_release = pop @sub_releases) {
2562                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2563                                         }
2564                                         eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
2565                                 } else {
2566                                         # A branch, no subrelease
2567                                         push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
2568                                 }
2569                         } elsif (!$entry->exists('cn')) {
2570                                 my $tmp_dn= $entry->dn();
2571                                 $tmp_dn= substr( $tmp_dn, 0, length($tmp_dn)
2572                                         - length($fai_base) - 1 );
2573                                 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?)$/;
2575                                 # Skip classes without releases
2576                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2577                                         next;
2578                                 }
2580                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2581                                         # A Subrelease
2582                                         my @sub_releases= split(/,/, $tmp_release);
2584                                         # Walk through subreleases and build hash tree
2585                                         my $hash;
2586                                         while(my $tmp_sub_release = pop @sub_releases) {
2587                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2588                                         }
2589                                         # Remove the last two characters
2590                                         chop($hash);
2591                                         chop($hash);
2593                                         eval('$fai_classes->'.$hash.'= {}');
2594                                 } else {
2595                                         # A branch, no subrelease
2596                                         if(!exists($fai_classes->{$tmp_release})) {
2597                                                 $fai_classes->{$tmp_release} = {};
2598                                         }
2599                                 }
2600                         }
2601                 }
2603                 # The hash is complete, now we can honor the copy-on-write based missing entries
2604                 foreach my $release (keys %$fai_classes) {
2605                         $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
2606                 }
2607         }
2608         return $result;
2611 sub apply_fai_inheritance {
2612        my $fai_classes = shift || return {};
2613        my $tmp_classes;
2615        # Get the classes from the branch
2616        foreach my $class (keys %{$fai_classes}) {
2617                # Skip subreleases
2618                if($class =~ /^ou=.*$/) {
2619                        next;
2620                } else {
2621                        $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
2622                }
2623        }
2625        # Apply to each subrelease
2626        foreach my $subrelease (keys %{$fai_classes}) {
2627                if($subrelease =~ /ou=/) {
2628                        foreach my $tmp_class (keys %{$tmp_classes}) {
2629                                if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
2630                                        $fai_classes->{$subrelease}->{$tmp_class} =
2631                                        deep_copy($tmp_classes->{$tmp_class});
2632                                } else {
2633                                        foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
2634                                                if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
2635                                                        $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
2636                                                        deep_copy($tmp_classes->{$tmp_class}->{$type});
2637                                                }
2638                                        }
2639                                }
2640                        }
2641                }
2642        }
2644        # Find subreleases in deeper levels
2645        foreach my $subrelease (keys %{$fai_classes}) {
2646                if($subrelease =~ /ou=/) {
2647                        foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
2648                                if($subsubrelease =~ /ou=/) {
2649                                        apply_fai_inheritance($fai_classes->{$subrelease});
2650                                }
2651                        }
2652                }
2653        }
2655        return $fai_classes;
2658 sub get_fai_release_entries {
2659         my $tmp_classes = shift || return;
2660         my $parent = shift || "";
2661         my @result = shift || ();
2663         foreach my $entry (keys %{$tmp_classes}) {
2664                 if(defined($entry)) {
2665                         if($entry =~ /^ou=.*$/) {
2666                                 my $release_name = $entry;
2667                                 $release_name =~ s/ou=//g;
2668                                 if(length($parent)>0) {
2669                                         $release_name = $parent."/".$release_name;
2670                                 }
2671                                 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2672                                 foreach my $bufentry(@bufentries) {
2673                                         push @result, $bufentry;
2674                                 }
2675                         } else {
2676                                 my @types = get_fai_types($tmp_classes->{$entry});
2677                                 foreach my $type (@types) {
2678                                         push @result, 
2679                                         {
2680                                                 'class' => $entry,
2681                                                 'type' => $type->{'type'},
2682                                                 'release' => $parent,
2683                                                 'state' => $type->{'state'},
2684                                         };
2685                                 }
2686                         }
2687                 }
2688         }
2690         return @result;
2693 sub deep_copy {
2694         my $this = shift;
2695         if (not ref $this) {
2696                 $this;
2697         } elsif (ref $this eq "ARRAY") {
2698                 [map deep_copy($_), @$this];
2699         } elsif (ref $this eq "HASH") {
2700                 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2701         } else { die "what type is $_?" }
2705 sub session_run_result {
2706     my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];    
2707     $kernel->sig(CHLD => "child_reap");
2710 sub session_run_debug {
2711     my $result = $_[ARG0];
2712     print STDERR "$result\n";
2715 sub session_run_done {
2716     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2717     delete $heap->{task}->{$task_id};
2718         if (exists $heap->{ldap_handle}->{$task_id}) {
2719                 &release_ldap_handle($heap->{ldap_handle}->{$task_id});
2720         }
2721         delete $heap->{ldap_handle}->{$task_id};
2725 sub create_sources_list {
2726         my $session_id = shift;
2727         my $result="/tmp/gosa_si_tmp_sources_list";
2729         # Remove old file
2730         if(stat($result)) {
2731                 unlink($result);
2732                 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7); 
2733         }
2735         my $fh;
2736         open($fh, ">$result");
2737         if (not defined $fh) {
2738                 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7); 
2739                 return undef;
2740         }
2741         if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2742                 my $ldap_handle = &get_ldap_handle();
2743                 my $mesg=$ldap_handle->search(
2744                         base    => $main::ldap_server_dn,
2745                         scope   => 'base',
2746                         attrs   => 'FAIrepository',
2747                         filter  => 'objectClass=FAIrepositoryServer'
2748                 );
2749                 &release_ldap_handle($ldap_handle);
2750                 if($mesg->count) {
2751                         foreach my $entry(@{$mesg->{'entries'}}) {
2752                                 foreach my $value(@{$entry->get_value('FAIrepository', asref => 1)}) {
2753                                         my ($server, $tag, $release, $sections)= split /\|/, $value;
2754                                         my $line = "deb $server $release";
2755                                         $sections =~ s/,/ /g;
2756                                         $line.= " $sections";
2757                                         print $fh $line."\n";
2758                                 }
2759                         }
2760                 }
2761         } else {
2762                 if (defined $main::ldap_server_dn){
2763                         &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1); 
2764                 } else {
2765                         &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2766                 }
2767         }
2768         close($fh);
2770         return $result;
2774 sub run_create_packages_list_db {
2775     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2776         my $session_id = $session->ID;
2777         my $task = POE::Wheel::Run->new(
2778                                         Priority => +20,
2779                                         Program => sub {&create_packages_list_db(undef, $session_id)},
2780                                         StdoutEvent  => "session_run_result",
2781                                         StderrEvent  => "session_run_debug",
2782                                         CloseEvent   => "session_run_done",
2783                                         );
2784         $heap->{task}->{ $task->ID } = $task;
2788 sub create_packages_list_db {
2789         my ($sources_file, $session_id) = @_;
2790         
2791         # it should not be possible to trigger a recreation of packages_list_db
2792         # while packages_list_db is under construction, so set flag packages_list_under_construction
2793         # which is tested befor recreation can be started
2794         if (-r $packages_list_under_construction) {
2795                 daemon_log("$session_id WARNING: packages_list_db is right now under construction, please wait until this process is finished", 3);
2796                 return;
2797         } else {
2798                 daemon_log("$session_id INFO: create_packages_list_db: start", 5); 
2799                 # set packages_list_under_construction to true
2800                 system("touch $packages_list_under_construction");
2801                 @packages_list_statements=();
2802         }
2804         if (not defined $session_id) { $session_id = 0; }
2806         if (not defined $sources_file) { 
2807                 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5); 
2808                 $sources_file = &create_sources_list($session_id);
2809         }
2811         if (not defined $sources_file) {
2812                 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1); 
2813                 unlink($packages_list_under_construction);
2814                 return;
2815         }
2817         my $line;
2819         open(CONFIG, "<$sources_file") or do {
2820                 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2821                 unlink($packages_list_under_construction);
2822                 return;
2823         };
2825         # Read lines
2826         while ($line = <CONFIG>){
2827                 # Unify
2828                 chop($line);
2829                 $line =~ s/^\s+//;
2830                 $line =~ s/^\s+/ /;
2832                 # Strip comments
2833                 $line =~ s/#.*$//g;
2835                 # Skip empty lines
2836                 if ($line =~ /^\s*$/){
2837                         next;
2838                 }
2840                 # Interpret deb line
2841                 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2842                         my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2843                         my $section;
2844                         foreach $section (split(' ', $sections)){
2845                                 &parse_package_info( $baseurl, $dist, $section, $session_id );
2846                         }
2847                 }
2848         }
2850         close (CONFIG);
2852         if(keys(%repo_dirs)) {
2853                 find(\&cleanup_and_extract, keys( %repo_dirs ));
2854                 &main::strip_packages_list_statements();
2855                 $packages_list_db->exec_statementlist(\@packages_list_statements);
2856         }
2857         unlink($packages_list_under_construction);
2858         daemon_log("$session_id INFO: create_packages_list_db: finished", 5); 
2859         return;
2862 # This function should do some intensive task to minimize the db-traffic
2863 sub strip_packages_list_statements {
2864         my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2865         my @new_statement_list=();
2866         my $hash;
2867         my $insert_hash;
2868         my $update_hash;
2869         my $delete_hash;
2870         my $known_packages_hash;
2871         my $local_timestamp=get_time();
2873         foreach my $existing_entry (@existing_entries) {
2874                 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
2875         }
2877         foreach my $statement (@packages_list_statements) {
2878                 if($statement =~ /^INSERT/i) {
2879                         # Assign the values from the insert statement
2880                         my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~ 
2881                         /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
2882                         if(exists($hash->{$distribution}->{$package}->{$version})) {
2883                                 # If section or description has changed, update the DB
2884                                 if( 
2885                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or 
2886                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
2887                                 ) {
2888                                         @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
2889                                 } else {
2890                                         # package is already present in database. cache this knowledge for later use
2891                                         @{$known_packages_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2892                                 }
2893                         } else {
2894                                 # Insert a non-existing entry to db
2895                                 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2896                         }
2897                 } elsif ($statement =~ /^UPDATE/i) {
2898                         my ($template,$package,$version) = ($1,$2,$3) if $statement =~
2899                         /^update\s+?$main::packages_list_tn\s+?set\s+?template\s*?=\s*?'(.*?)'\s+?where\s+?package\s*?=\s*?'(.*?)'\s+?and\s+?version\s*?=\s*?'(.*?)'\s*?;$/si;
2900                         foreach my $distribution (keys %{$hash}) {
2901                                 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
2902                                         # update the insertion hash to execute only one query per package (insert instead insert+update)
2903                                         @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
2904                                 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
2905                                         if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
2906                                                 my $section;
2907                                                 my $description;
2908                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
2909                                                         length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
2910                                                         $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
2911                                                 }
2912                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2913                                                         $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
2914                                                 }
2915                                                 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2916                                         }
2917                                 }
2918                         }
2919                 }
2920         }
2922         # Check for orphaned entries
2923         foreach my $existing_entry (@existing_entries) {
2924                 my $distribution= @{$existing_entry}[0];
2925                 my $package= @{$existing_entry}[1];
2926                 my $version= @{$existing_entry}[2];
2927                 my $section= @{$existing_entry}[3];
2929                 if(
2930                         exists($insert_hash->{$distribution}->{$package}->{$version}) ||
2931                         exists($update_hash->{$distribution}->{$package}->{$version}) ||
2932                         exists($known_packages_hash->{$distribution}->{$package}->{$version})
2933                 ) {
2934                         next;
2935                 } else {
2936                         # Insert entry to delete hash
2937                         @{$delete_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section);
2938                 }
2939         }
2941         # unroll the insert hash
2942         foreach my $distribution (keys %{$insert_hash}) {
2943                 foreach my $package (keys %{$insert_hash->{$distribution}}) {
2944                         foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
2945                                 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
2946                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
2947                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
2948                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
2949                                 ."'$local_timestamp')";
2950                         }
2951                 }
2952         }
2954         # unroll the update hash
2955         foreach my $distribution (keys %{$update_hash}) {
2956                 foreach my $package (keys %{$update_hash->{$distribution}}) {
2957                         foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
2958                                 my $set = "";
2959                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
2960                                         $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
2961                                 }
2962                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2963                                         $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
2964                                 }
2965                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
2966                                         $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
2967                                 }
2968                                 if(defined($set) and length($set) > 0) {
2969                                         $set .= "timestamp = '$local_timestamp'";
2970                                 } else {
2971                                         next;
2972                                 }
2973                                 push @new_statement_list, 
2974                                 "UPDATE $main::packages_list_tn SET $set WHERE"
2975                                 ." distribution = '$distribution'"
2976                                 ." AND package = '$package'"
2977                                 ." AND version = '$version'";
2978                         }
2979                 }
2980         }
2981         
2982         # unroll the delete hash
2983         foreach my $distribution (keys %{$delete_hash}) {
2984                 foreach my $package (keys %{$delete_hash->{$distribution}}) {
2985                         foreach my $version (keys %{$delete_hash->{$distribution}->{$package}}) {
2986                                 my $section = @{$delete_hash->{$distribution}->{$package}->{$version}}[3];
2987                                 push @new_statement_list, "DELETE FROM $main::packages_list_tn WHERE distribution='$distribution' AND package='$package' AND version='$version' AND section='$section'";
2988                         }
2989                 }
2990         }
2992         unshift(@new_statement_list, "VACUUM");
2994         @packages_list_statements = @new_statement_list;
2998 sub parse_package_info {
2999     my ($baseurl, $dist, $section, $session_id)= @_;
3000     my ($package);
3001     if (not defined $session_id) { $session_id = 0; }
3002     my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
3003     $repo_dirs{ "${repo_path}/pool" } = 1;
3005     foreach $package ("Packages.gz"){
3006         daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
3007         get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
3008         parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
3009     }
3010     
3014 sub get_package {
3015     my ($url, $dest, $session_id)= @_;
3016     if (not defined $session_id) { $session_id = 0; }
3018     my $tpath = dirname($dest);
3019     -d "$tpath" || mkpath "$tpath";
3021     # This is ugly, but I've no time to take a look at "how it works in perl"
3022     if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
3023         system("gunzip -cd '$dest' > '$dest.in'");
3024         daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 5);
3025         unlink($dest);
3026         daemon_log("$session_id DEBUG: delete file '$dest'", 5); 
3027     } else {
3028         daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' into '$dest' failed!", 1);
3029     }
3030     return 0;
3034 sub parse_package {
3035     my ($path, $dist, $srv_path, $session_id)= @_;
3036     if (not defined $session_id) { $session_id = 0;}
3037     my ($package, $version, $section, $description);
3038     my $PACKAGES;
3039     my $timestamp = &get_time();
3041     if(not stat("$path.in")) {
3042         daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
3043         return;
3044     }
3046     open($PACKAGES, "<$path.in");
3047     if(not defined($PACKAGES)) {
3048         daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1); 
3049         return;
3050     }
3052     # Read lines
3053     while (<$PACKAGES>){
3054         my $line = $_;
3055         # Unify
3056         chop($line);
3058         # Use empty lines as a trigger
3059         if ($line =~ /^\s*$/){
3060             my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
3061             push(@packages_list_statements, $sql);
3062             $package = "none";
3063             $version = "none";
3064             $section = "none";
3065             $description = "none"; 
3066             next;
3067         }
3069         # Trigger for package name
3070         if ($line =~ /^Package:\s/){
3071             ($package)= ($line =~ /^Package: (.*)$/);
3072             next;
3073         }
3075         # Trigger for version
3076         if ($line =~ /^Version:\s/){
3077             ($version)= ($line =~ /^Version: (.*)$/);
3078             next;
3079         }
3081         # Trigger for description
3082         if ($line =~ /^Description:\s/){
3083             ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
3084             next;
3085         }
3087         # Trigger for section
3088         if ($line =~ /^Section:\s/){
3089             ($section)= ($line =~ /^Section: (.*)$/);
3090             next;
3091         }
3093         # Trigger for filename
3094         if ($line =~ /^Filename:\s/){
3095             my ($filename) = ($line =~ /^Filename: (.*)$/);
3096             store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
3097             next;
3098         }
3099     }
3101     close( $PACKAGES );
3102     unlink( "$path.in" );
3106 sub store_fileinfo {
3107     my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
3109     my %fileinfo = (
3110         'package' => $package,
3111         'dist' => $dist,
3112         'version' => $vers,
3113     );
3115     $repo_files{ "${srvdir}/$file" } = \%fileinfo;
3119 sub cleanup_and_extract {
3120         my $fileinfo = $repo_files{ $File::Find::name };
3122         if( defined $fileinfo ) {
3123                 my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
3124                 my $sql;
3125                 my $package = $fileinfo->{ 'package' };
3126                 my $newver = $fileinfo->{ 'version' };
3128                 mkpath($dir);
3129                 system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
3131                 if( -f "$dir/DEBIAN/templates" ) {
3133                         daemon_log("0 DEBUG: Found debconf templates in '$package' - $newver", 7);
3135                         my $tmpl= ""; {
3136                                 local $/=undef;
3137                                 open FILE, "$dir/DEBIAN/templates";
3138                                 $tmpl = &encode_base64(<FILE>);
3139                                 close FILE;
3140                         }
3141                         rmtree("$dir/DEBIAN/templates");
3143                         $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
3144                         push @packages_list_statements, $sql;
3145                 }
3146         }
3148         return;
3152 sub register_at_foreign_servers {   
3153     my ($kernel) = $_[KERNEL];
3155     # hole alle bekannten server aus known_server_db
3156     my $server_sql = "SELECT * FROM $known_server_tn";
3157     my $server_res = $known_server_db->exec_statement($server_sql);
3159     # no entries in known_server_db
3160     if (not ref(@$server_res[0]) eq "ARRAY") { 
3161         # TODO
3162     }
3164     # detect already connected clients
3165     my $client_sql = "SELECT * FROM $known_clients_tn"; 
3166     my $client_res = $known_clients_db->exec_statement($client_sql);
3168     # send my server details to all other gosa-si-server within the network
3169     foreach my $hit (@$server_res) {
3170         my $hostname = @$hit[0];
3171         my $hostkey = &create_passwd;
3173         # add already connected clients to registration message 
3174         my $myhash = &create_xml_hash('new_server', $server_address, $hostname);
3175         &add_content2xml_hash($myhash, 'key', $hostkey);
3176         map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res);
3178         # add locally loaded gosa-si modules to registration message
3179         my $loaded_modules = {};
3180         while (my ($package, $pck_info) = each %$known_modules) {
3181                                                 next if ((!defined(@$pck_info[2])) || (!(ref (@$pck_info[2]) eq 'HASH')));
3182                                                 foreach my $act_module (keys(%{@$pck_info[2]})) {
3183                                                         $loaded_modules->{$act_module} = ""; 
3184                                                 }
3185         }
3187         map(&add_content2xml_hash($myhash, "loaded_modules", $_), keys(%$loaded_modules));
3189         # add macaddress to registration message
3190         my ($host_ip, $host_port) = split(/:/, $hostname);
3191         my $local_ip = &get_local_ip_for_remote_ip($host_ip);
3192         my $network_interface= &get_interface_for_ip($local_ip);
3193         my $host_mac = &get_mac_for_interface($network_interface);
3194         &add_content2xml_hash($myhash, 'macaddress', $host_mac);
3195         
3196         # build registration message and send it
3197         my $foreign_server_msg = &create_xml_string($myhash);
3198         my $error = &send_msg_to_target($foreign_server_msg, $hostname, $ServerPackages_key, "new_server", 0); 
3199     }
3200     
3201     $kernel->delay_set("register_at_foreign_servers", $foreign_servers_register_delay); 
3202     return;
3206 #==== MAIN = main ==============================================================
3207 #  parse commandline options
3208 Getopt::Long::Configure( "bundling" );
3209 GetOptions("h|help" => \&usage,
3210         "c|config=s" => \$cfg_file,
3211         "f|foreground" => \$foreground,
3212         "v|verbose+" => \$verbose,
3213         "no-arp+" => \$no_arp,
3214            );
3216 #  read and set config parameters
3217 &check_cmdline_param ;
3218 &read_configfile($cfg_file, %cfg_defaults);
3219 &check_pid;
3221 $SIG{CHLD} = 'IGNORE';
3223 # forward error messages to logfile
3224 if( ! $foreground ) {
3225   open( STDIN,  '+>/dev/null' );
3226   open( STDOUT, '+>&STDIN'    );
3227   open( STDERR, '+>&STDIN'    );
3230 # Just fork, if we are not in foreground mode
3231 if( ! $foreground ) { 
3232     chdir '/'                 or die "Can't chdir to /: $!";
3233     $pid = fork;
3234     setsid                    or die "Can't start a new session: $!";
3235     umask 0;
3236 } else { 
3237     $pid = $$; 
3240 # Do something useful - put our PID into the pid_file
3241 if( 0 != $pid ) {
3242     open( LOCK_FILE, ">$pid_file" );
3243     print LOCK_FILE "$pid\n";
3244     close( LOCK_FILE );
3245     if( !$foreground ) { 
3246         exit( 0 ) 
3247     };
3250 # parse head url and revision from svn
3251 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
3252 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
3253 $server_headURL = defined $1 ? $1 : 'unknown' ;
3254 $server_revision = defined $2 ? $2 : 'unknown' ;
3255 if ($server_headURL =~ /\/tag\// || 
3256         $server_headURL =~ /\/branches\// ) {
3257     $server_status = "stable"; 
3258 } else {
3259     $server_status = "developmental" ;
3261 # Prepare log file and set permissions
3262 $root_uid = getpwnam('root');
3263 $adm_gid = getgrnam('adm');
3264 open(FH, ">>$log_file");
3265 close FH;
3266 chmod(0440, $log_file);
3267 chown($root_uid, $adm_gid, $log_file);
3268 chown($root_uid, $adm_gid, "/var/lib/gosa-si");
3270 daemon_log(" ", 1);
3271 daemon_log("$0 started!", 1);
3272 daemon_log("status: $server_status", 1);
3273 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1); 
3275 # Create a pool of LDAP handles
3276 $ldap_factory =  ResourcePool::Factory::Net::LDAP->new($ldap_uri, version => $ldap_version);
3277 $ldap_factory->bind($ldap_admin_dn, password=>$ldap_admin_password);
3278 $ldap_pool = ResourcePool->new($ldap_factory,
3279                 Max         => 10,
3280                 #MaxTry      => 1,
3281                 #SleepOnFail    => [0, 0, 1, 1],
3282                 PreCreate       => 5,
3283 );
3286 # Buildup data bases
3288     no strict "refs";
3290     if ($db_module eq "DBmysql") {
3291         # connect to incoming_db
3292         $incoming_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3294         # connect to gosa-si job queue
3295         $job_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3297         # connect to known_clients_db
3298         $known_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3300         # connect to foreign_clients_db
3301         $foreign_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3303         # connect to known_server_db
3304         $known_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3306         # connect to login_usr_db
3307         $login_users_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3309         # connect to fai_server_db 
3310         $fai_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3312         # connect to fai_release_db
3313         $fai_release_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3315         # connect to packages_list_db
3316         $packages_list_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3318         # connect to messaging_db
3319         $messaging_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3321     } elsif ($db_module eq "DBsqlite") {
3322         # connect to incoming_db
3323         unlink($incoming_file_name);
3324         $incoming_db = GOSA::DBsqlite->new($incoming_file_name);
3325         
3326         # connect to gosa-si job queue
3327         unlink($job_queue_file_name);  ## just for debugging
3328         $job_db = GOSA::DBsqlite->new($job_queue_file_name);
3329         chmod(0640, $job_queue_file_name);
3330         chown($root_uid, $adm_gid, $job_queue_file_name);
3331         
3332         # connect to known_clients_db
3333         unlink($known_clients_file_name);   ## just for debugging
3334         $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
3335         chmod(0640, $known_clients_file_name);
3336         chown($root_uid, $adm_gid, $known_clients_file_name);
3337         
3338         # connect to foreign_clients_db
3339         unlink($foreign_clients_file_name);
3340         $foreign_clients_db = GOSA::DBsqlite->new($foreign_clients_file_name);
3341         chmod(0640, $foreign_clients_file_name);
3342         chown($root_uid, $adm_gid, $foreign_clients_file_name);
3343         
3344         # connect to known_server_db
3345         unlink($known_server_file_name);
3346         $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
3347         chmod(0640, $known_server_file_name);
3348         chown($root_uid, $adm_gid, $known_server_file_name);
3349         
3350         # connect to login_usr_db
3351         unlink($login_users_file_name);
3352         $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
3353         chmod(0640, $login_users_file_name);
3354         chown($root_uid, $adm_gid, $login_users_file_name);
3355         
3356         # connect to fai_server_db
3357         unlink($fai_server_file_name);
3358         $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
3359         chmod(0640, $fai_server_file_name);
3360         chown($root_uid, $adm_gid, $fai_server_file_name);
3361         
3362         # connect to fai_release_db
3363         unlink($fai_release_file_name);
3364         $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
3365         chmod(0640, $fai_release_file_name);
3366         chown($root_uid, $adm_gid, $fai_release_file_name);
3367         
3368         # connect to packages_list_db
3369         #unlink($packages_list_file_name);
3370         unlink($packages_list_under_construction);
3371         $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
3372         chmod(0640, $packages_list_file_name);
3373         chown($root_uid, $adm_gid, $packages_list_file_name);
3374         
3375         # connect to messaging_db
3376         unlink($messaging_file_name);
3377         $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
3378         chmod(0640, $messaging_file_name);
3379         chown($root_uid, $adm_gid, $messaging_file_name);
3380     }
3384 # Creating tables
3385 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
3386 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
3387 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
3388 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
3389 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
3390 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
3391 $foreign_clients_db->create_table($foreign_clients_tn, \@foreign_clients_col_names);
3392 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
3393 $incoming_db->create_table($incoming_tn, \@incoming_col_names);
3394 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
3396 # create xml object used for en/decrypting
3397 $xml = new XML::Simple();
3400 # foreign servers 
3401 my @foreign_server_list;
3403 # add foreign server from cfg file
3404 if ($foreign_server_string ne "") {
3405     my @cfg_foreign_server_list = split(",", $foreign_server_string);
3406     foreach my $foreign_server (@cfg_foreign_server_list) {
3407         push(@foreign_server_list, $foreign_server);
3408     }
3410     daemon_log("0 INFO: found foreign server in config file: ".join(", ", @foreign_server_list), 5);
3413 # Perform a DNS lookup for server registration if flag is true
3414 if ($dns_lookup eq "true") {
3415     # Add foreign server from dns
3416     my @tmp_servers;
3417     if (not $server_domain) {
3418         # Try our DNS Searchlist
3419         for my $domain(get_dns_domains()) {
3420             chomp($domain);
3421             my ($tmp_domains, $error_string) = &get_server_addresses($domain);
3422             if(@$tmp_domains) {
3423                 for my $tmp_server(@$tmp_domains) {
3424                     push @tmp_servers, $tmp_server;
3425                 }
3426             }
3427         }
3428         if(@tmp_servers && length(@tmp_servers)==0) {
3429             daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3430         }
3431     } else {
3432         @tmp_servers = &get_server_addresses($server_domain);
3433         if( 0 == @tmp_servers ) {
3434             daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3435         }
3436     }
3438     daemon_log("0 INFO: found foreign server via DNS ".join(", ", @tmp_servers), 5);    
3440     foreach my $server (@tmp_servers) { 
3441         unshift(@foreign_server_list, $server); 
3442     }
3443 } else {
3444     daemon_log("0 INFO: DNS lookup for server registration is disabled", 5);
3448 # eliminate duplicate entries
3449 @foreign_server_list = &del_doubles(@foreign_server_list);
3450 my $all_foreign_server = join(", ", @foreign_server_list);
3451 daemon_log("0 INFO: found foreign server in config file and DNS: '$all_foreign_server'", 5);
3453 # add all found foreign servers to known_server
3454 my $cur_timestamp = &get_time();
3455 foreach my $foreign_server (@foreign_server_list) {
3457         # do not add myself to known_server_db
3458         if (&is_local($foreign_server)) { next; }
3459         ######################################
3461     my $res = $known_server_db->add_dbentry( {table=>$known_server_tn, 
3462             primkey=>['hostname'],
3463             hostname=>$foreign_server,
3464             macaddress=>"",
3465             status=>'not_yet_registered',
3466             hostkey=>"none",
3467             loaded_modules => "none", 
3468             timestamp=>$cur_timestamp,
3469             } );
3473 # Import all modules
3474 &import_modules;
3476 # Check wether all modules are gosa-si valid passwd check
3477 &password_check;
3479 # Prepare for using Opsi 
3480 if ($opsi_enabled eq "true") {
3481     use JSON::RPC::Client;
3482     use XML::Quote qw(:all);
3483     $opsi_url= "https://".$opsi_admin.":".$opsi_password."@".$opsi_server.":4447/rpc";
3484     $opsi_client = new JSON::RPC::Client;
3488 POE::Component::Server::TCP->new(
3489         Alias => "TCP_SERVER",
3490         Port => $server_port,
3491         ClientInput => sub {
3492                 my ($kernel, $input, $heap, $session) = @_[KERNEL, ARG0, HEAP, SESSION];
3493         my $session_id = $session->ID;
3494         my $remote_ip = $heap->{'remote_ip'};
3495                 push(@msgs_to_decrypt, $input);
3496         &daemon_log("$session_id DEBUG: incoming message from '$remote_ip'", 7);
3497                 $kernel->yield("msg_to_decrypt");
3498         },
3499         InlineStates => {
3500                 msg_to_decrypt => \&msg_to_decrypt,
3501                 next_task => \&next_task,
3502                 task_result => \&handle_task_result,
3503                 task_done   => \&handle_task_done,
3504                 task_debug  => \&handle_task_debug,
3505                 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3506         }
3507 );
3509 daemon_log("0 INFO: start socket for incoming xml messages at port '$server_port' ", 1);
3511 # create session for repeatedly checking the job queue for jobs
3512 POE::Session->create(
3513         inline_states => {
3514                 _start => \&session_start,
3515         register_at_foreign_servers => \&register_at_foreign_servers,
3516         sig_handler => \&sig_handler,
3517         next_task => \&next_task,
3518         task_result => \&handle_task_result,
3519         task_done   => \&handle_task_done,
3520         task_debug  => \&handle_task_debug,
3521         watch_for_next_tasks => \&watch_for_next_tasks,
3522         watch_for_new_messages => \&watch_for_new_messages,
3523         watch_for_delivery_messages => \&watch_for_delivery_messages,
3524         watch_for_done_messages => \&watch_for_done_messages,
3525                 watch_for_new_jobs => \&watch_for_new_jobs,
3526         watch_for_modified_jobs => \&watch_for_modified_jobs,
3527         watch_for_done_jobs => \&watch_for_done_jobs,
3528         watch_for_opsi_jobs => \&watch_for_opsi_jobs,
3529         watch_for_old_known_clients => \&watch_for_old_known_clients,
3530         create_packages_list_db => \&run_create_packages_list_db,
3531         create_fai_server_db => \&run_create_fai_server_db,
3532         create_fai_release_db => \&run_create_fai_release_db,
3533                 recreate_packages_db => \&run_recreate_packages_db,
3534         session_run_result => \&session_run_result,
3535         session_run_debug => \&session_run_debug,
3536         session_run_done => \&session_run_done,
3537         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3538         }
3539 );
3542 POE::Kernel->run();
3543 exit;