Code

Don't open filehandle in daemon_log if we don't want to write anything.
[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, $max_ldap_handle, $precreate_ldap_handle);
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         "max-ldap-handle"               => [\$max_ldap_handle, 10],
254         "precreate-ldap-handle" => [\$precreate_ldap_handle, 5],
255     "gosa-unit-tag"         => [\$gosa_unit_tag, ""],
256     "max-clients"           => [\$max_clients, 10],
257     "wol-password"          => [\$wake_on_lan_passwd, ""],
258         "mysql-username"        => [\$mysql_username, "gosa_si"],
259         "mysql-password"        => [\$mysql_password, ""],
260         "mysql-database"        => [\$mysql_database, "gosa_si"],
261         "mysql-host"            => [\$mysql_host, "127.0.0.1"],
262     },
263 "GOsaPackages" => {
264     "job-queue" => [\$job_queue_file_name, '/var/lib/gosa-si/jobs.db'],
265     "job-queue-loop-delay" => [\$job_queue_loop_delay, 3],
266     "messaging-db-loop-delay" => [\$messaging_db_loop_delay, 3],
267     "key" => [\$GosaPackages_key, "none"],
268                 "new-systems-ou" => [\$new_systems_ou, 'ou=workstations,ou=systems'],
269     },
270 "ClientPackages" => {
271     "key" => [\$ClientPackages_key, "none"],
272     "user-date-of-expiry" => [\$logged_in_user_date_of_expiry, 600],
273     },
274 "ServerPackages"=> {
275     "address"      => [\$foreign_server_string, ""],
276     "dns-lookup"            => [\$dns_lookup, "true"],
277     "domain"  => [\$server_domain, ""],
278     "key"     => [\$ServerPackages_key, "none"],
279     "key-lifetime" => [\$foreign_servers_register_delay, 120],
280     "job-synchronization-enabled" => [\$job_synchronization, "true"],
281     "synchronization-loop" => [\$modified_jobs_loop_delay, 5],
282     },
283 "ArpHandler" => {
284     "enabled"   => [\$arp_enabled, "true"],
285     "interface" => [\$arp_interface, "all"],
286         },
287 "Opsi" => {
288     "enabled"  => [\$opsi_enabled, "false"], 
289     "server"   => [\$opsi_server, "localhost"],
290     "admin"    => [\$opsi_admin, "opsi-admin"],
291     "password" => [\$opsi_password, "secret"],
292    },
294 );
297 #===  FUNCTION  ================================================================
298 #         NAME:  usage
299 #   PARAMETERS:  nothing
300 #      RETURNS:  nothing
301 #  DESCRIPTION:  print out usage text to STDERR
302 #===============================================================================
303 sub usage {
304     print STDERR << "EOF" ;
305 usage: $prg [-hvf] [-c config]
307            -h        : this (help) message
308            -c <file> : config file
309            -f        : foreground, process will not be forked to background
310            -v        : be verbose (multiple to increase verbosity)
311            -no-arp   : starts $prg without connection to arp module
312  
313 EOF
314     print "\n" ;
318 #===  FUNCTION  ================================================================
319 #         NAME:  logging
320 #   PARAMETERS:  level - string - default 'info'
321 #                msg - string -
322 #                facility - string - default 'LOG_DAEMON'
323 #      RETURNS:  nothing
324 #  DESCRIPTION:  function for logging
325 #===============================================================================
326 sub daemon_log {
327     # log into log_file
328     my( $msg, $level ) = @_;
329     if(not defined $msg) { return }
330     if(not defined $level) { $level = 1 }
331                 if($level > $verbose) { return }
332     if(defined $log_file){
333         my $open_log_fh = sysopen(LOG_HANDLE, $log_file, O_WRONLY | O_CREAT | O_APPEND , 0440);
334         if(not $open_log_fh) {
335             print STDERR "cannot open $log_file: $!";
336             return;
337         }
338         # check owner and group of log_file and update settings if necessary
339         my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = stat($log_file);
340         if((not $uid eq $root_uid) || (not $gid eq $adm_gid)) {
341             chown($root_uid, $adm_gid, $log_file);
342                 }
344         chomp($msg);
345         #$msg =~s/\n//g;   # no newlines are allowed in log messages, this is important for later log parsing
346         if($level <= $verbose){
347             my ($seconds, $minutes, $hours, $monthday, $month,
348                     $year, $weekday, $yearday, $sommertime) = localtime(time);
349             $hours = $hours < 10 ? $hours = "0".$hours : $hours;
350             $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
351             $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
352             my @monthnames = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
353             $month = $monthnames[$month];
354             $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
355             $year+=1900;
356             my $name = $prg;
358             my $log_msg = "$month $monthday $hours:$minutes:$seconds $name $msg\n";
359                         flock(LOG_HANDLE, LOCK_EX);
360                         seek(LOG_HANDLE, 0, 2);
361             print LOG_HANDLE $log_msg;
362                         flock(LOG_HANDLE, LOCK_UN);
363             if( $foreground ) { 
364                 print STDERR $log_msg;
365             }
366         }
367         close( LOG_HANDLE );
368     }
372 #===  FUNCTION  ================================================================
373 #         NAME:  check_cmdline_param
374 #   PARAMETERS:  nothing
375 #      RETURNS:  nothing
376 #  DESCRIPTION:  validates commandline parameter
377 #===============================================================================
378 sub check_cmdline_param () {
379     my $err_config;
380     my $err_counter = 0;
381         if(not defined($cfg_file)) {
382                 $cfg_file = "/etc/gosa-si/server.conf";
383                 if(! -r $cfg_file) {
384                         $err_config = "please specify a config file";
385                         $err_counter += 1;
386                 }
387     }
388     if( $err_counter > 0 ) {
389         &usage( "", 1 );
390         if( defined( $err_config)) { print STDERR "$err_config\n"}
391         print STDERR "\n";
392         exit( -1 );
393     }
397 #===  FUNCTION  ================================================================
398 #         NAME:  check_pid
399 #   PARAMETERS:  nothing
400 #      RETURNS:  nothing
401 #  DESCRIPTION:  handels pid processing
402 #===============================================================================
403 sub check_pid {
404     $pid = -1;
405     # Check, if we are already running
406     if( open(LOCK_FILE, "<$pid_file") ) {
407         $pid = <LOCK_FILE>;
408         if( defined $pid ) {
409             chomp( $pid );
410             if( -f "/proc/$pid/stat" ) {
411                 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
412                 if( $stat ) {
413                                         print STDERR "\nERROR: Already running!\n";
414                     close( LOCK_FILE );
415                     exit -1;
416                 }
417             }
418         }
419         close( LOCK_FILE );
420         unlink( $pid_file );
421     }
423     # create a syslog msg if it is not to possible to open PID file
424     if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
425         my($msg) = "Couldn't obtain lockfile '$pid_file' ";
426         if (open(LOCK_FILE, '<', $pid_file)
427                 && ($pid = <LOCK_FILE>))
428         {
429             chomp($pid);
430             $msg .= "(PID $pid)\n";
431         } else {
432             $msg .= "(unable to read PID)\n";
433         }
434         if( ! ($foreground) ) {
435             openlog( $0, "cons,pid", "daemon" );
436             syslog( "warning", $msg );
437             closelog();
438         }
439         else {
440             print( STDERR " $msg " );
441         }
442         exit( -1 );
443     }
446 #===  FUNCTION  ================================================================
447 #         NAME:  import_modules
448 #   PARAMETERS:  module_path - string - abs. path to the directory the modules 
449 #                are stored
450 #      RETURNS:  nothing
451 #  DESCRIPTION:  each file in module_path which ends with '.pm' and activation 
452 #                state is on is imported by "require 'file';"
453 #===============================================================================
454 sub import_modules {
455     daemon_log(" ", 1);
457     if (not -e $modules_path) {
458         daemon_log("0 ERROR: cannot find directory or directory is not readable: $modules_path", 1);   
459     }
461     opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
463         my $ldap_handle = &get_ldap_handle;
464     while (defined (my $file = readdir (DIR))) {
465         if (not $file =~ /(\S*?).pm$/) {
466             next;
467         }
468                 my $mod_name = $1;
470         # ArpHandler switch
471         if( $file =~ /ArpHandler.pm/ ) {
472             if( $arp_enabled eq "false" ) { next; }
473         }
474         
475         eval { require $file; };
476         if ($@) {
477             daemon_log("0 ERROR: gosa-si-server could not load module $file", 1);
478             daemon_log("$@", 1);
479             exit;
480                 } else {
481                         my $info = eval($mod_name.'::get_module_info($ldap_handle)');
482                         # Only load module if get_module_info() returns a non-null object
483                         if( $info ) {
484                                 my ($input_address, $input_key, $event_hash) = @{$info};
485                                 $known_modules->{$mod_name} = $info;
486                                 daemon_log("0 INFO: module $mod_name loaded", 5);
487                         }
488                 }
489     }   
490         &release_ldap_handle($ldap_handle); 
491     close (DIR);
494 #===  FUNCTION  ================================================================
495 #         NAME:  password_check
496 #   PARAMETERS:  nothing
497 #      RETURNS:  nothing
498 #  DESCRIPTION:  escalates an critical error if two modules exist which are avaialable by 
499 #                the same password
500 #===============================================================================
501 sub password_check {
502     my $passwd_hash = {};
503     while (my ($mod_name, $mod_info) = each %$known_modules) {
504         my $mod_passwd = @$mod_info[1];
505         if (not defined $mod_passwd) { next; }
506         if (not exists $passwd_hash->{$mod_passwd}) {
507             $passwd_hash->{$mod_passwd} = $mod_name;
509         # escalates critical error
510         } else {
511             &daemon_log("0 ERROR: two loaded modules do have the same password. Please modify the 'key'-parameter in config file");
512             &daemon_log("0 ERROR: module='$mod_name' and module='".$passwd_hash->{$mod_passwd}."'");
513             exit( -1 );
514         }
515     }
520 #===  FUNCTION  ================================================================
521 #         NAME:  sig_int_handler
522 #   PARAMETERS:  signal - string - signal arose from system
523 #      RETURNS:  nothing
524 #  DESCRIPTION:  handels tasks to be done befor signal becomes active
525 #===============================================================================
526 sub sig_int_handler {
527     my ($signal) = @_;
529 #       if (defined($ldap_handle)) {
530 #               $ldap_handle->disconnect;
531 #       }
532     # TODO alle verbliebenden ldap verbindungen aus allen heaps beenden
533     
535     daemon_log("shutting down gosa-si-server", 1);
536     system("kill `ps -C gosa-si-server -o pid=`");
538 $SIG{INT} = \&sig_int_handler;
541 sub check_key_and_xml_validity {
542     my ($crypted_msg, $module_key, $session_id) = @_;
543     my $msg;
544     my $msg_hash;
545     my $error_string;
546     eval{
547         $msg = &decrypt_msg($crypted_msg, $module_key);
549         if ($msg =~ /<xml>/i){
550             $msg =~ s/\s+/ /g;  # just for better daemon_log
551             daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 9);
552             $msg_hash = $xml->XMLin($msg, ForceArray=>1);
554             ##############
555             # check header
556             if( not exists $msg_hash->{'header'} ) { die "no header specified"; }
557             my $header_l = $msg_hash->{'header'};
558             if( (1 > @{$header_l}) || ( ( 'HASH' eq ref @{$header_l}[0]) && (1 > keys %{@{$header_l}[0]}) ) ) { die 'empty header tag'; }
559             if( 1 < @{$header_l} ) { die 'more than one header specified'; }
560             my $header = @{$header_l}[0];
561             if( 0 == length $header) { die 'empty string in header tag'; }
563             ##############
564             # check source
565             if( not exists $msg_hash->{'source'} ) { die "no source specified"; }
566             my $source_l = $msg_hash->{'source'};
567             if( (1 > @{$source_l}) || ( ( 'HASH' eq ref @{$source_l}[0]) && (1 > keys %{@{$source_l}[0]}) ) ) { die 'empty source tag'; }
568             if( 1 < @{$source_l} ) { die 'more than one source specified'; }
569             my $source = @{$source_l}[0];
570             if( 0 == length $source) { die 'source error'; }
572             ##############
573             # check target
574             if( not exists $msg_hash->{'target'} ) { die "no target specified"; }
575             my $target_l = $msg_hash->{'target'};
576             if( (1 > @{$target_l}) || ( ('HASH' eq ref @{$target_l}[0]) && (1 > keys %{@{$target_l}[0]}) ) ) { die 'empty target tag'; }
577         }
578     };
579     if($@) {
580         daemon_log("$session_id ERROR: do not understand the message: $@", 1);
581         $msg = undef;
582         $msg_hash = undef;
583     }
585     return ($msg, $msg_hash);
589 sub check_outgoing_xml_validity {
590     my ($msg, $session_id) = @_;
592     my $msg_hash;
593     eval{
594         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
596         ##############
597         # check header
598         my $header_l = $msg_hash->{'header'};
599         if( 1 != @{$header_l} ) {
600             die 'no or more than one headers specified';
601         }
602         my $header = @{$header_l}[0];
603         if( 0 == length $header) {
604             die 'header has length 0';
605         }
607         ##############
608         # check source
609         my $source_l = $msg_hash->{'source'};
610         if( 1 != @{$source_l} ) {
611             die 'no or more than 1 sources specified';
612         }
613         my $source = @{$source_l}[0];
614         if( 0 == length $source) {
615             die 'source has length 0';
616         }
618                                 # Check if source contains hostname instead of ip address
619                                 if($source =~ /^[a-z][a-z0-9\.]+:\d+$/i) {
620                                                 my ($hostname,$port) = split(/:/, $source);
621                                                 my $ip_address = inet_ntoa(scalar gethostbyname($hostname));
622                                                 if(defined($ip_address) && $ip_address =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/ && $port =~ /^\d+$/) {
623                                                         # Write ip address to $source variable
624                                                         $source = "$ip_address:$port";
625                                                 }
626                                 }
627         unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
628                 $source =~ /^GOSA$/i) {
629             die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
630         }
631         
632         ##############
633         # check target  
634         my $target_l = $msg_hash->{'target'};
635         if( 0 == @{$target_l} ) {
636             die "no targets specified";
637         }
638         foreach my $target (@$target_l) {
639             if( 0 == length $target) {
640                 die "target has length 0";
641             }
642             unless( $target =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
643                     $target =~ /^GOSA$/i ||
644                     $target =~ /^\*$/ ||
645                     $target =~ /KNOWN_SERVER/i ||
646                     $target =~ /JOBDB/i ||
647                     $target =~ /^([0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2})$/i ){
648                 die "target '$target' is not a complete ip-address with port or a valid target name or a mac-address";
649             }
650         }
651     };
652     if($@) {
653         daemon_log("$session_id ERROR: outgoing msg is not gosa-si envelope conform: $@", 1);
654         daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 1);
655         $msg_hash = undef;
656     }
658     return ($msg_hash);
662 sub input_from_known_server {
663     my ($input, $remote_ip, $session_id) = @_ ;  
664     my ($msg, $msg_hash, $module);
666     my $sql_statement= "SELECT * FROM known_server";
667     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
669     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
670         my $host_name = $hit->{hostname};
671         if( not $host_name =~ "^$remote_ip") {
672             next;
673         }
674         my $host_key = $hit->{hostkey};
675         daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 7);
676         daemon_log("$session_id DEBUG: input_from_known_server: host_key: $host_key", 7);
678         # check if module can open msg envelope with module key
679         my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
680         if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
681             daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 7);
682             daemon_log("$@", 8);
683             next;
684         }
685         else {
686             $msg = $tmp_msg;
687             $msg_hash = $tmp_msg_hash;
688             $module = "ServerPackages";
689             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 7);
690             last;
691         }
692     }
694     if( (!$msg) || (!$msg_hash) || (!$module) ) {
695         daemon_log("$session_id DEBUG: Incoming message is not from a known server", 7);
696     }
697   
698     return ($msg, $msg_hash, $module);
702 sub input_from_known_client {
703     my ($input, $remote_ip, $session_id) = @_ ;  
704     my ($msg, $msg_hash, $module);
706     my $sql_statement= "SELECT * FROM known_clients";
707     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
708     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
709         my $host_name = $hit->{hostname};
710         if( not $host_name =~ /^$remote_ip:\d*$/) {
711                 next;
712                 }
713         my $host_key = $hit->{hostkey};
714         &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 7);
715         &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 7);
717         # check if module can open msg envelope with module key
718         ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
720         if( (!$msg) || (!$msg_hash) ) {
721             &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 7);
722             &daemon_log("$@", 8);
723             next;
724         }
725         else {
726             $module = "ClientPackages";
727             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 7);
728             last;
729         }
730     }
732     if( (!$msg) || (!$msg_hash) || (!$module) ) {
733         &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 7);
734     }
736     return ($msg, $msg_hash, $module);
740 sub input_from_unknown_host {
741         no strict "refs";
742         my ($input, $session_id) = @_ ;
743         my ($msg, $msg_hash, $module);
744         my $error_string;
746         my %act_modules = %$known_modules;
748         while( my ($mod, $info) = each(%act_modules)) {
750                 # check a key exists for this module
751                 my $module_key = ${$mod."_key"};
752                 if( not defined $module_key ) {
753                         if( $mod eq 'ArpHandler' ) {
754                                 next;
755                         }
756                         daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
757                         next;
758                 }
759                 daemon_log("$session_id DEBUG: $mod: $module_key", 7);
761                 # check if module can open msg envelope with module key
762                 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
763                 if( (not defined $msg) || (not defined $msg_hash) ) {
764                         next;
765                 } else {
766                         $module = $mod;
767             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 7);
768                         last;
769                 }
770         }
772         if( (!$msg) || (!$msg_hash) || (!$module)) {
773                 daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 7);
774         }
776         return ($msg, $msg_hash, $module);
780 sub create_ciphering {
781     my ($passwd) = @_;
782         if((!defined($passwd)) || length($passwd)==0) {
783                 $passwd = "";
784         }
785     $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
786     my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
787     my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
788     $my_cipher->set_iv($iv);
789     return $my_cipher;
793 sub encrypt_msg {
794     my ($msg, $key) = @_;
795     my $my_cipher = &create_ciphering($key);
796     my $len;
797     {
798             use bytes;
799             $len= 16-length($msg)%16;
800     }
801     $msg = "\0"x($len).$msg;
802     $msg = $my_cipher->encrypt($msg);
803     chomp($msg = &encode_base64($msg));
804     # there are no newlines allowed inside msg
805     $msg=~ s/\n//g;
806     return $msg;
810 sub decrypt_msg {
812     my ($msg, $key) = @_ ;
813     $msg = &decode_base64($msg);
814     my $my_cipher = &create_ciphering($key);
815     $msg = $my_cipher->decrypt($msg); 
816     $msg =~ s/\0*//g;
817     return $msg;
821 sub get_encrypt_key {
822     my ($target) = @_ ;
823     my $encrypt_key;
824     my $error = 0;
826     # target can be in known_server
827     if( not defined $encrypt_key ) {
828         my $sql_statement= "SELECT * FROM known_server WHERE hostname='$target'";
829         my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
830         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
831             my $host_name = $hit->{hostname};
832             if( $host_name ne $target ) {
833                 next;
834             }
835             $encrypt_key = $hit->{hostkey};
836             last;
837         }
838     }
840     # target can be in known_client
841     if( not defined $encrypt_key ) {
842         my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$target'";
843         my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
844         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
845             my $host_name = $hit->{hostname};
846             if( $host_name ne $target ) {
847                 next;
848             }
849             $encrypt_key = $hit->{hostkey};
850             last;
851         }
852     }
854     return $encrypt_key;
858 #===  FUNCTION  ================================================================
859 #         NAME:  open_socket
860 #   PARAMETERS:  PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
861 #                [PeerPort] string necessary if port not appended by PeerAddr
862 #      RETURNS:  socket IO::Socket::INET
863 #  DESCRIPTION:  open a socket to PeerAddr
864 #===============================================================================
865 sub open_socket {
866     my ($PeerAddr, $PeerPort) = @_ ;
867     if(defined($PeerPort)){
868         $PeerAddr = $PeerAddr.":".$PeerPort;
869     }
870     my $socket;
871     $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
872             Porto => "tcp",
873             Type => SOCK_STREAM,
874             Timeout => 5,
875             );
876     if(not defined $socket) {
877         return;
878     }
879 #    &daemon_log("DEBUG: open_socket: $PeerAddr", 7);
880     return $socket;
884 sub send_msg_to_target {
885     my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
886     my $error = 0;
887     my $header;
888     my $timestamp = &get_time();
889     my $new_status;
890     my $act_status;
891     my ($sql_statement, $res);
892   
893     if( $msg_header ) {
894         $header = "'$msg_header'-";
895     } else {
896         $header = "";
897     }
899         # Patch the source ip
900         if($msg =~ /<source>0\.0\.0\.0:\d*?<\/source>/) {
901                 my $remote_ip = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
902                 $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$remote_ip:$2<\/source>/s;
903         }
905     # encrypt xml msg
906     my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
908     # opensocket
909     my $socket = &open_socket($address);
910     if( !$socket ) {
911         daemon_log("$session_id WARNING: cannot send ".$header."msg to $address , host not reachable", 3);
912         $error++;
913     }
914     
915     if( $error == 0 ) {
916         # send xml msg
917         print $socket $crypted_msg."\n";
919         daemon_log("$session_id INFO: send ".$header."msg to $address", 5);
920         daemon_log("$session_id DEBUG: message:\n$msg", 9);
921         
922     }
924     # close socket in any case
925     if( $socket ) {
926         close $socket;
927     }
929     if( $error > 0 ) { $new_status = "down"; }
930     else { $new_status = $msg_header; }
933     # known_clients
934     $sql_statement = "SELECT * FROM $known_clients_tn WHERE hostname='$address'";
935     $res = $known_clients_db->select_dbentry($sql_statement);
936     if( keys(%$res) == 1) {
937         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
938         if ($act_status eq "down" && $new_status eq "down") {
939             $sql_statement = "DELETE FROM known_clients WHERE hostname='$address'";
940             $res = $known_clients_db->del_dbentry($sql_statement);
941             daemon_log("$session_id WARNING: failed 2x to send msg to host '$address', delete host from known_clients", 3);
942         } else { 
943             $sql_statement = "UPDATE known_clients SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
944             $res = $known_clients_db->update_dbentry($sql_statement);
945             if($new_status eq "down"){
946                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
947             } else {
948                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
949             }
950         }
951     }
953     # known_server
954     $sql_statement = "SELECT * FROM $known_server_tn WHERE hostname='$address'";
955     $res = $known_server_db->select_dbentry($sql_statement);
956     if( keys(%$res) == 1) {
957         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
958         if ($act_status eq "down" && $new_status eq "down") {
959             $sql_statement = "DELETE FROM known_server WHERE hostname='$address'";
960             $res = $known_server_db->del_dbentry($sql_statement);
961             daemon_log("$session_id WARNING: failed 2x to send a message to host '$address', delete host from known_server", 3);
962         } 
963         else { 
964             $sql_statement = "UPDATE known_server SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
965             $res = $known_server_db->update_dbentry($sql_statement);
966             if($new_status eq "down"){
967                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
968             } else {
969                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
970             }
971         }
972     }
973     return $error; 
977 sub update_jobdb_status_for_send_msgs {
978     my ($session_id, $answer, $error) = @_;
979     &daemon_log("$session_id DEBUG: try to update job status", 7); 
980     if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
981         my $jobdb_id = $1;
982     
983         $answer =~ /<header>(.*)<\/header>/;
984         my $job_header = $1;
986         $answer =~ /<target>(.*)<\/target>/;
987         my $job_target = $1;
988             
989         # Sending msg failed
990         if( $error ) {
992             # Set jobs to done, jobs do not need to deliver their message in any case
993             if (($job_header eq "trigger_action_localboot")
994                     ||($job_header eq "trigger_action_lock")
995                     ||($job_header eq "trigger_action_halt") 
996                     ) {
997                 my $sql_statement = "UPDATE $job_queue_tn SET status='done' WHERE id=$jobdb_id";
998                 &daemon_log("$session_id DEBUG: $sql_statement", 7); 
999                 my $res = $job_db->update_dbentry($sql_statement);
1000                 
1001             # Reactivate jobs, jobs need to deliver their message
1002             } elsif (($job_header eq "trigger_action_activate")
1003                     ||($job_header eq "trigger_action_update")
1004                     ||($job_header eq "trigger_action_reinstall") 
1005                     ||($job_header eq "trigger_activate_new")
1006                     ) {
1007                 &reactivate_job_with_delay($session_id, $job_target, $job_header, 30 );
1009             # For all other messages
1010             } else {
1011                 my $sql_statement = "UPDATE $job_queue_tn ".
1012                     "SET status='error', result='can not deliver msg, please consult log file' ".
1013                     "WHERE id=$jobdb_id";
1014                 &daemon_log("$session_id DEBUG: $sql_statement", 7); 
1015                 my $res = $job_db->update_dbentry($sql_statement);
1016             }
1018         # Sending msg was successful
1019         } else {
1020             # Set jobs localboot, lock, activate, halt, reboot and wake to done
1021             # jobs reinstall, update, inst_update do themself setting to done
1022             if (($job_header eq "trigger_action_localboot")
1023                     ||($job_header eq "trigger_action_lock")
1024                     ||($job_header eq "trigger_action_activate")
1025                     ||($job_header eq "trigger_action_halt") 
1026                     ||($job_header eq "trigger_action_reboot")
1027                     ||($job_header eq "trigger_action_wake")
1028                     ||($job_header eq "trigger_wake")
1029                     ) {
1031                 my $sql_statement = "UPDATE $job_queue_tn ".
1032                     "SET status='done' ".
1033                     "WHERE id=$jobdb_id AND status='processed'";
1034                 &daemon_log("$session_id DEBUG: $sql_statement", 7); 
1035                 my $res = $job_db->update_dbentry($sql_statement);
1036             } else { 
1037                 &daemon_log("$session_id DEBUG: sending message succeed but cannot update job status.", 7); 
1038             } 
1039         } 
1040     } else { 
1041         &daemon_log("$session_id DEBUG: cannot update job status, msg has no jobdb_id-tag: $answer", 7); 
1042     }
1045 sub reactivate_job_with_delay {
1046     my ($session_id, $target, $header, $delay) = @_ ;
1047     # 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
1048     
1049     if (not defined $delay) { $delay = 30 } ;
1050     my $delay_timestamp = &calc_timestamp(&get_time(), "plus", $delay);
1052     my $sql = "UPDATE $job_queue_tn Set timestamp='$delay_timestamp', status='waiting' WHERE (macaddress LIKE 'target' AND headertag='$header')"; 
1053     my $res = $job_db->update_dbentry($sql);
1054     daemon_log("$session_id INFO: '$header'-job will be reactivated at '$delay_timestamp' ".
1055             "cause client '$target' is currently not available", 5);
1056     daemon_log("$session_id $sql", 7);                             
1057     return;
1061 sub sig_handler {
1062         my ($kernel, $signal) = @_[KERNEL, ARG0] ;
1063         daemon_log("0 INFO got signal '$signal'", 1); 
1064         $kernel->sig_handled();
1065         return;
1069 sub msg_to_decrypt {
1070         my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
1071         my $session_id = $session->ID;
1072         my ($msg, $msg_hash, $module);
1073         my $error = 0;
1075         # fetch new msg out of @msgs_to_decrypt
1076         my $tmp_next_msg = shift @msgs_to_decrypt;
1077     my ($next_msg, $msg_source) = split(/;/, $tmp_next_msg);
1079         # msg is from a new client or gosa
1080         ($msg, $msg_hash, $module) = &input_from_unknown_host($next_msg, $session_id);
1082         # msg is from a gosa-si-server
1083         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1084                 ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $heap->{'remote_ip'}, $session_id);
1085         }
1086         # msg is from a gosa-si-client
1087         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1088                 ($msg, $msg_hash, $module) = &input_from_known_client($next_msg, $heap->{'remote_ip'}, $session_id);
1089         }
1090         # an error occurred
1091         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1092                 # if an incoming msg could not be decrypted (maybe a wrong key), send client a ping. If the client
1093                 # could not understand a msg from its server the client cause a re-registering process
1094         my $remote_ip = $heap->{'remote_ip'};
1095         my $remote_port = $heap->{'remote_port'};
1096         my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source><target>$msg_source</target></xml>";
1097         my ($test_error, $test_error_string) = &send_msg_to_target($ping_msg, "$msg_source", "dummy-key", "gosa_ping", $session_id);
1099                 daemon_log("$session_id WARNING cannot understand incoming msg, send 'ping'-msg to all host with ip '".$heap->{remote_ip}.
1100                         "' to cause a re-registering of the client if necessary", 3);
1101                 $error++;
1102         }
1105         my $header;
1106         my $target;
1107         my $source;
1108         my $done = 0;
1109         my $sql;
1110         my $res;
1112         # check whether this message should be processed here
1113         if ($error == 0) {
1114                 $header = @{$msg_hash->{'header'}}[0];
1115                 $target = @{$msg_hash->{'target'}}[0];
1116                 $source = @{$msg_hash->{'source'}}[0];
1117                 my $not_found_in_known_clients_db = 0;
1118                 my $not_found_in_known_server_db = 0;
1119                 my $not_found_in_foreign_clients_db = 0;
1120                 my $local_address;
1121                 my $local_mac;
1122                 my ($target_ip, $target_port) = split(':', $target);
1124                 # Determine the local ip address if target is an ip address
1125                 if ($target =~ /^\d+\.\d+\.\d+\.\d+:\d+$/) {
1126                         $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1127                 } else {
1128                         $local_address = $server_address;
1129                 }
1131                 # Determine the local mac address if target is a mac address
1132                 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) {
1133                         my $loc_ip = &get_local_ip_for_remote_ip($heap->{'remote_ip'});
1134                         my $network_interface= &get_interface_for_ip($loc_ip);
1135                         $local_mac = &get_mac_for_interface($network_interface);
1136                 } else {
1137                         $local_mac = $server_mac_address;
1138                 }
1140                 # target and source is equal to GOSA -> process here
1141                 if (not $done) {
1142                         if ($target eq "GOSA" && $source eq "GOSA") {
1143                                 $done = 1;                    
1144                                 &daemon_log("$session_id DEBUG: target and source is 'GOSA' -> process here", 7);
1145                         }
1146                 }
1148                 # target is own address without forward_to_gosa-tag -> process here
1149                 if (not $done) {
1150                         #if ((($target eq $local_address) || ($target eq $local_mac) ) && (not exists $msg_hash->{'forward_to_gosa'})) {
1151                         if (($target eq $local_address) && (not exists $msg_hash->{'forward_to_gosa'})) {
1152                                 $done = 1;
1153                                 if ($source eq "GOSA") {
1154                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1155                                 }
1156                                 &daemon_log("$session_id DEBUG: target is own address without forward_to_gosa-tag -> process here", 7);
1157                         }
1158                 }
1160                 # target is a client address in known_clients -> process here
1161                 if (not $done) {
1162                         $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')"; 
1163                         $res = $known_clients_db->select_dbentry($sql);
1164                         if (keys(%$res) > 0) {
1165                                 $done = 1; 
1166                                 my $hostname = $res->{1}->{'hostname'};
1167                                 $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1168                                 my $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1169                                 if ($source eq "GOSA") {
1170                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1171                                 }
1172                                 &daemon_log("$session_id DEBUG: target is a client address in known_clients -> process here", 7);
1174                         } else {
1175                                 $not_found_in_known_clients_db = 1;
1176                         }
1177                 }
1179                 # target ist own address with forward_to_gosa-tag not pointing to myself -> process here
1180                 if (not $done) {
1181                         my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1182                         my $gosa_at;
1183                         my $gosa_session_id;
1184                         if (($target eq $local_address) && (defined $forward_to_gosa)){
1185                                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1186                                 if ($gosa_at ne $local_address) {
1187                                         $done = 1;
1188                                         &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag not pointing to myself -> process here", 7); 
1189                                 }
1190                         }
1191                 }
1193                 # if message should be processed here -> add message to incoming_db
1194                 if ($done) {
1195                         # if a job or a gosa message comes from a foreign server, fake module to GosaPackages
1196                         # so gosa-si-server knows how to process this kind of messages
1197                         if ($header =~ /^gosa_/ || $header =~ /^job_/) {
1198                                 $module = "GosaPackages";
1199                         }
1201                         my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1202                                         primkey=>[],
1203                                         headertag=>$header,
1204                                         targettag=>$target,
1205                                         xmlmessage=>&encode_base64($msg),
1206                                         timestamp=>&get_time,
1207                                         module=>$module,
1208                                         sessionid=>$session_id,
1209                                 } );
1211                 }
1213                 # target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa
1214                 if (not $done) {
1215                         my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1216                         my $gosa_at;
1217                         my $gosa_session_id;
1218                         if (($target eq $local_address) && (defined $forward_to_gosa)){
1219                                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1220                                 if ($gosa_at eq $local_address) {
1221                                         my $session_reference = $kernel->ID_id_to_session($gosa_session_id);
1222                                         if( defined $session_reference ) {
1223                                                 $heap = $session_reference->get_heap();
1224                                         }
1225                                         if(exists $heap->{'client'}) {
1226                                                 $msg = &encrypt_msg($msg, $GosaPackages_key);
1227                                                 $heap->{'client'}->put($msg);
1228                                                 &daemon_log("$session_id INFO: incoming '$header' message forwarded to GOsa", 5); 
1229                                         }
1230                                         $done = 1;
1231                                         &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa", 7);
1232                                 }
1233                         }
1235                 }
1237                 # target is a client address in foreign_clients -> forward to registration server
1238                 if (not $done) {
1239                         $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1240                         $res = $foreign_clients_db->select_dbentry($sql);
1241                         if (keys(%$res) > 0) {
1242                                 my $hostname = $res->{1}->{'hostname'};
1243                                 my ($host_ip, $host_port) = split(/:/, $hostname);
1244                                 my $local_address =  &get_local_ip_for_remote_ip($host_ip).":$server_port";
1245                                 my $regserver = $res->{1}->{'regserver'};
1246                                 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$regserver'"; 
1247                                 my $res = $known_server_db->select_dbentry($sql);
1248                                 if (keys(%$res) > 0) {
1249                                         my $regserver_key = $res->{1}->{'hostkey'};
1250                                         $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1251                                         $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1252                                         if ($source eq "GOSA") {
1253                                                 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1254                                         }
1255                                         my $error= &send_msg_to_target($msg, $regserver, $regserver_key, $header, $session_id);
1256                                         if ($error) {
1257                                                 &daemon_log("$session_id ERROR: some problems (error=$error) occurred while trying to send msg to registration server: $msg", 1); 
1258                                         }
1259                                 }
1260                                 $done = 1;
1261                                 &daemon_log("$session_id DEBUG: target is a client address in foreign_clients -> forward to registration server", 7);
1262                         } else {
1263                                 $not_found_in_foreign_clients_db = 1;
1264                         }
1265                 }
1267                 # target is a server address -> forward to server
1268                 if (not $done) {
1269                         $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1270                         $res = $known_server_db->select_dbentry($sql);
1271                         if (keys(%$res) > 0) {
1272                                 my $hostkey = $res->{1}->{'hostkey'};
1274                                 if ($source eq "GOSA") {
1275                                         $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1276                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1278                                 }
1280                                 &send_msg_to_target($msg, $target, $hostkey, $header, $session_id);
1281                                 $done = 1;
1282                                 &daemon_log("$session_id DEBUG: target is a server address -> forward to server", 7);
1283                         } else {
1284                                 $not_found_in_known_server_db = 1;
1285                         }
1286                 }
1289                 # target is not in foreign_clients_db, known_server_db or known_clients_db, maybe it is a complete new one -> process here
1290                 if ( $not_found_in_foreign_clients_db 
1291                         && $not_found_in_known_server_db
1292                         && $not_found_in_known_clients_db) {
1293                         &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);
1294             if ($header =~ /^gosa_/ || $header =~ /^job_/) { 
1295                 $module = "GosaPackages"; 
1296             }
1297                         my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1298                                         primkey=>[],
1299                                         headertag=>$header,
1300                                         targettag=>$target,
1301                                         xmlmessage=>&encode_base64($msg),
1302                                         timestamp=>&get_time,
1303                                         module=>$module,
1304                                         sessionid=>$session_id,
1305                                 } );
1306                         $done = 1;
1307                 }
1310                 if (not $done) {
1311                         daemon_log("$session_id ERROR: do not know what to do with this message: $msg", 1);
1312                         if ($source eq "GOSA") {
1313                                 my %data = ('error_msg' => &encode_base64($msg), 'error_string' => "Do not know what to do with this message!");
1314                                 my $error_msg = &build_msg("error", $local_address, "GOSA", \%data ); 
1316                                 my $session_reference = $kernel->ID_id_to_session($session_id);
1317                                 if( defined $session_reference ) {
1318                                         $heap = $session_reference->get_heap();
1319                                 }
1320                                 if(exists $heap->{'client'}) {
1321                                         $error_msg = &encrypt_msg($error_msg, $GosaPackages_key);
1322                                         $heap->{'client'}->put($error_msg);
1323                                 }
1324                         }
1325                 }
1327         }
1329         return;
1333 sub next_task {
1334     my ($session, $heap, $task, $ldap_handle) = @_[SESSION, HEAP, ARG0, ARG1];
1335     my $running_task = POE::Wheel::Run->new(
1336             Program => sub { process_task($session, $heap, $task, $ldap_handle) },
1337             StdioFilter => POE::Filter::Reference->new(),
1338             StdoutEvent  => "task_result",
1339             StderrEvent  => "task_debug",
1340             CloseEvent   => "task_done",
1341             );
1342     $heap->{task}->{ $running_task->ID } = $running_task;
1343         $heap->{ldap_handle}->{$running_task->ID} = $ldap_handle;
1346 sub handle_task_result {
1347     my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
1348     my $client_answer = $result->{'answer'};
1349     if( $client_answer =~ s/session_id=(\d+)$// ) {
1350         my $session_id = $1;
1351         if( defined $session_id ) {
1352             my $session_reference = $kernel->ID_id_to_session($session_id);
1353             if( defined $session_reference ) {
1354                 $heap = $session_reference->get_heap();
1355             }
1356         }
1358         if(exists $heap->{'client'}) {
1359             $heap->{'client'}->put($client_answer);
1360         }
1361     }
1362     $kernel->sig(CHLD => "child_reap");
1365 sub handle_task_debug {
1366     my $result = $_[ARG0];
1367     print STDERR "$result\n";
1370 sub handle_task_done {
1371     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1372     delete $heap->{task}->{$task_id};
1373         if (exists $heap->{ldap_handle}->{$task_id}) {
1374                 &release_ldap_handle($heap->{ldap_handle}->{$task_id});
1375         }
1378 sub process_task {
1379     no strict "refs";
1380     #CHECK: Not @_[...]?
1381     my ($session, $heap, $task, $ldap_handle) = @_;
1382     my $error = 0;
1383     my $answer_l;
1384     my ($answer_header, @answer_target_l, $answer_source);
1385     my $client_answer = "";
1387     # prepare all variables needed to process message
1388     #my $msg = $task->{'xmlmessage'};
1389     my $msg = &decode_base64($task->{'xmlmessage'});
1390     my $incoming_id = $task->{'id'};
1391     my $module = $task->{'module'};
1392     my $header =  $task->{'headertag'};
1393     my $session_id = $task->{'sessionid'};
1394                 my $msg_hash;
1395                 eval {
1396         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
1397                 }; 
1398                 daemon_log("ERROR: XML failure '$@'") if ($@);
1399     my $source = @{$msg_hash->{'source'}}[0];
1400     
1401     # set timestamp of incoming client uptodate, so client will not 
1402     # be deleted from known_clients because of expiration
1403     my $cur_time = &get_time();
1404     my $sql = "UPDATE $known_clients_tn SET timestamp='$cur_time' WHERE hostname='$source'"; 
1405     my $res = $known_clients_db->exec_statement($sql);
1407     ######################
1408     # process incoming msg
1409     if( $error == 0) {
1410         daemon_log("$session_id INFO: Incoming msg (session_id=$session_id) with header '".@{$msg_hash->{'header'}}[0]."'", 5); 
1411         daemon_log("$session_id DEBUG: Processing module ".$module, 7);
1412         $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id, $ldap_handle);
1414         if ( 0 < @{$answer_l} ) {
1415             my $answer_str = join("\n", @{$answer_l});
1416             while ($answer_str =~ /<header>(\w+)<\/header>/g) {
1417                 daemon_log("$session_id INFO: got answer message with header '$1'", 5);
1418             }
1419             daemon_log("$session_id DEBUG: $module: got answer from module: \n".$answer_str,9);
1420         } else {
1421             daemon_log("$session_id DEBUG: $module: got no answer from module!" ,7);
1422         }
1424     }
1425     if( !$answer_l ) { $error++ };
1427     ########
1428     # answer
1429     if( $error == 0 ) {
1431         foreach my $answer ( @{$answer_l} ) {
1432             # check outgoing msg to xml validity
1433             my $answer_hash = &check_outgoing_xml_validity($answer, $session_id);
1434             if( not defined $answer_hash ) { next; }
1435             
1436             $answer_header = @{$answer_hash->{'header'}}[0];
1437             @answer_target_l = @{$answer_hash->{'target'}};
1438             $answer_source = @{$answer_hash->{'source'}}[0];
1440             # deliver msg to all targets 
1441             foreach my $answer_target ( @answer_target_l ) {
1443                 # targets of msg are all gosa-si-clients in known_clients_db
1444                 if( $answer_target eq "*" ) {
1445                     # answer is for all clients
1446                     my $sql_statement= "SELECT * FROM known_clients";
1447                     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
1448                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1449                         my $host_name = $hit->{hostname};
1450                         my $host_key = $hit->{hostkey};
1451                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1452                         &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1453                     }
1454                 }
1456                 # targets of msg are all gosa-si-server in known_server_db
1457                 elsif( $answer_target eq "KNOWN_SERVER" ) {
1458                     # answer is for all server in known_server
1459                     my $sql_statement= "SELECT * FROM $known_server_tn";
1460                     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
1461                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1462                         my $host_name = $hit->{hostname};
1463                         my $host_key = $hit->{hostkey};
1464                         $answer =~ s/<target>\S+<\/target>/<target>$host_name<\/target>/g;
1465                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1466                         &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1467                     }
1468                 }
1470                 # target of msg is GOsa
1471                                 elsif( $answer_target eq "GOSA" ) {
1472                                         my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1473                                         my $add_on = "";
1474                     if( defined $session_id ) {
1475                         $add_on = ".session_id=$session_id";
1476                     }
1477                     # answer is for GOSA and has to returned to connected client
1478                     my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1479                     $client_answer = $gosa_answer.$add_on;
1480                 }
1482                 # target of msg is job queue at this host
1483                 elsif( $answer_target eq "JOBDB") {
1484                     $answer =~ /<header>(\S+)<\/header>/;   
1485                     my $header;
1486                     if( defined $1 ) { $header = $1; }
1487                     my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1488                     &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1489                 }
1491                 # Target of msg is a mac address
1492                 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 ) {
1493                     daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients and foreign_clients", 5);
1495                     # Looking for macaddress in known_clients
1496                     my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1497                     my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1498                     my $found_ip_flag = 0;
1499                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1500                         my $host_name = $hit->{hostname};
1501                         my $host_key = $hit->{hostkey};
1502                         $answer =~ s/$answer_target/$host_name/g;
1503                         daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1504                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1505                         &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1506                         $found_ip_flag++ ;
1507                     }   
1509                     # Looking for macaddress in foreign_clients
1510                     if ($found_ip_flag == 0) {
1511                         my $sql = "SELECT * FROM $foreign_clients_tn WHERE macaddress LIKE '$answer_target'";
1512                         my $res = $foreign_clients_db->select_dbentry($sql);
1513                         while( my ($hit_num, $hit) = each %{ $res } ) {
1514                             my $host_name = $hit->{hostname};
1515                             my $reg_server = $hit->{regserver};
1516                             daemon_log("$session_id INFO: found host '$host_name' with mac '$answer_target', registered at '$reg_server'", 5);
1517                             
1518                             # Fetch key for reg_server
1519                             my $reg_server_key;
1520                             my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$reg_server'";
1521                             my $res = $known_server_db->select_dbentry($sql);
1522                             if (exists $res->{1}) {
1523                                 $reg_server_key = $res->{1}->{'hostkey'}; 
1524                             } else {
1525                                 daemon_log("$session_id ERROR: cannot find hostkey for '$host_name' in '$known_server_tn'", 1); 
1526                                 daemon_log("$session_id ERROR: unable to forward answer to correct registration server, processing is aborted!", 1); 
1527                                 $reg_server_key = undef;
1528                             }
1530                             # Send answer to server where client is registered
1531                             if (defined $reg_server_key) {
1532                                 $answer =~ s/$answer_target/$host_name/g;
1533                                 my $error = &send_msg_to_target($answer, $reg_server, $reg_server_key, $answer_header, $session_id);
1534                                 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1535                                 $found_ip_flag++ ;
1536                             }
1537                         }
1538                     }
1540                     # No mac to ip matching found
1541                     if( $found_ip_flag == 0) {
1542                         daemon_log("$session_id WARNING: no host found in known_clients or foreign_clients with mac address '$answer_target'", 3);
1543                         &reactivate_job_with_delay($session_id, $answer_target, $answer_header, 30);
1544                     }
1546                 # Answer is for one specific host   
1547                 } else {
1548                     # get encrypt_key
1549                     my $encrypt_key = &get_encrypt_key($answer_target);
1550                     if( not defined $encrypt_key ) {
1551                         # unknown target
1552                         daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1553                         next;
1554                     }
1555                     my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1556                     &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1557                 }
1558             }
1559         }
1560     }
1562     my $filter = POE::Filter::Reference->new();
1563     my %result = ( 
1564             status => "seems ok to me",
1565             answer => $client_answer,
1566             );
1568     my $output = $filter->put( [ \%result ] );
1569     print @$output;
1574 sub session_start {
1575     my ($kernel) = $_[KERNEL];
1576     $global_kernel = $kernel;
1577     $kernel->yield('register_at_foreign_servers');
1578         $kernel->yield('create_fai_server_db', $fai_server_tn );
1579         $kernel->yield('create_fai_release_db', $fai_release_tn );
1580     $kernel->yield('watch_for_next_tasks');
1581         $kernel->sig(USR1 => "sig_handler");
1582         $kernel->sig(USR2 => "recreate_packages_db");
1583         $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1584         $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay); 
1585     $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay); 
1586         $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1587     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1588         $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1589     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1591     # Start opsi check
1592     if ($opsi_enabled eq "true") {
1593         $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay); 
1594     }
1599 sub watch_for_done_jobs {
1600         #CHECK: $heap for what?
1601         my ($kernel,$heap) = @_[KERNEL, HEAP];
1603         my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((status='done') AND (modified='0'))";
1604         my $res = $job_db->select_dbentry( $sql_statement );
1606         while( my ($id, $hit) = each %{$res} ) {
1607                 my $jobdb_id = $hit->{id};
1608                 my $sql_statement = "DELETE FROM $job_queue_tn WHERE id=$jobdb_id"; 
1609                 my $res = $job_db->del_dbentry($sql_statement); 
1610         }
1612         $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1616 sub watch_for_opsi_jobs {
1617     my ($kernel) = $_[KERNEL];
1619     # 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 
1620     # opsi install job is to parse the xml message. There is still the correct header.
1621     my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((xmlmessage LIKE '%opsi_install_client</header>%') AND (status='processing') AND (siserver='localhost'))";
1622         my $res = $job_db->select_dbentry( $sql_statement );
1624     # Ask OPSI for an update of the running jobs
1625     while (my ($id, $hit) = each %$res ) {
1626         # Determine current parameters of the job
1627         my $hostId = $hit->{'plainname'};
1628         my $macaddress = $hit->{'macaddress'};
1629         my $progress = $hit->{'progress'};
1631         my $result= {};
1632         
1633         # For hosts, only return the products that are or get installed
1634         my $callobj;
1635         $callobj = {
1636             method  => 'getProductStates_hash',
1637             params  => [ $hostId ],
1638             id  => 1,
1639         };
1640         
1641         my $hres = $opsi_client->call($opsi_url, $callobj);
1642         #my ($hres_err, $hres_err_string) = &check_opsi_res($hres);
1643         if (not &check_opsi_res($hres)) {
1644             my $htmp= $hres->result->{$hostId};
1645         
1646             # Check state != not_installed or action == setup -> load and add
1647             my $products= 0;
1648             my $installed= 0;
1649             my $installing = 0;
1650             my $error= 0;  
1651             my @installed_list;
1652             my @error_list;
1653             my $act_status = "none";
1654             foreach my $product (@{$htmp}){
1656                 if ($product->{'installationStatus'} ne "not_installed" or
1657                         $product->{'actionRequest'} eq "setup"){
1659                     # Increase number of products for this host
1660                     $products++;
1661         
1662                     if ($product->{'installationStatus'} eq "failed"){
1663                         $result->{$product->{'productId'}}= "error";
1664                         unshift(@error_list, $product->{'productId'});
1665                         $error++;
1666                     }
1667                     if ($product->{'installationStatus'} eq "installed" && $product->{'actionRequest'}  eq "none"){
1668                         $result->{$product->{'productId'}}= "installed";
1669                         unshift(@installed_list, $product->{'productId'});
1670                         $installed++;
1671                     }
1672                     if ($product->{'installationStatus'} eq "installing"){
1673                         $result->{$product->{'productId'}}= "installing";
1674                         $installing++;
1675                         $act_status = "installing - ".$product->{'productId'};
1676                     }
1677                 }
1678             }
1679         
1680             # Estimate "rough" progress, avoid division by zero
1681             if ($products == 0) {
1682                 $result->{'progress'}= 0;
1683             } else {
1684                 $result->{'progress'}= int($installed * 100 / $products);
1685             }
1687             # Set updates in job queue
1688             if ((not $error) && (not $installing) && ($installed)) {
1689                 $act_status = "installed - ".join(", ", @installed_list);
1690             }
1691             if ($error) {
1692                 $act_status = "error - ".join(", ", @error_list);
1693             }
1694             if ($progress ne $result->{'progress'} ) {
1695                 # Updating progress and result 
1696                 my $update_statement = "UPDATE $job_queue_tn SET modified='1', progress='".$result->{'progress'}."', result='$act_status' WHERE macaddress LIKE '$macaddress' AND siserver='localhost'";
1697                 my $update_res = $job_db->update_dbentry($update_statement);
1698             }
1699             if ($progress eq 100) { 
1700                 # Updateing status
1701                 my $done_statement = "UPDATE $job_queue_tn SET modified='1', ";
1702                 if ($error) {
1703                     $done_statement .= "status='error'";
1704                 } else {
1705                     $done_statement .= "status='done'";
1706                 }
1707                 $done_statement .= " WHERE macaddress LIKE '$macaddress' AND siserver='localhost'";
1708                 my $done_res = $job_db->update_dbentry($done_statement);
1709             }
1712         }
1713     }
1715     $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay);
1719 # If a job got an update or was modified anyway, send to all other si-server an update message of this jobs.
1720 sub watch_for_modified_jobs {
1721     my ($kernel,$heap) = @_[KERNEL, HEAP];
1723     my $sql_statement = "SELECT * FROM $job_queue_tn WHERE (modified='1')"; 
1724     my $res = $job_db->select_dbentry( $sql_statement );
1725     
1726     # if db contains no jobs which should be update, do nothing
1727     if (keys %$res != 0) {
1729         if ($job_synchronization  eq "true") {
1730             # make out of the db result a gosa-si message   
1731             my $update_msg = &db_res2si_msg ($res, "foreign_job_updates", "KNOWN_SERVER", "MY_LOCAL_ADDRESS");
1732  
1733             # update all other SI-server
1734             &inform_all_other_si_server($update_msg);
1735         }
1737         # set jobs all jobs to modified = 0, wait until the next modification for updates of other si-server
1738         $sql_statement = "UPDATE $job_queue_tn SET modified='0' ";
1739         $res = $job_db->update_dbentry($sql_statement);
1740     }
1742     $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay);
1746 sub watch_for_new_jobs {
1747         if($watch_for_new_jobs_in_progress == 0) {
1748                 $watch_for_new_jobs_in_progress = 1;
1749                 my ($kernel,$heap) = @_[KERNEL, HEAP];
1751                 # check gosa job queue for jobs with executable timestamp
1752                 my $timestamp = &get_time();
1753                 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE siserver='localhost' AND status='waiting' AND (CAST(timestamp AS UNSIGNED)) < $timestamp ORDER BY timestamp";
1754                 my $res = $job_db->exec_statement( $sql_statement );
1756                 # Merge all new jobs that would do the same actions
1757                 my @drops;
1758                 my $hits;
1759                 foreach my $hit (reverse @{$res} ) {
1760                         my $macaddress= lc @{$hit}[8];
1761                         my $headertag= @{$hit}[5];
1762                         if(
1763                                 defined($hits->{$macaddress}) &&
1764                                 defined($hits->{$macaddress}->{$headertag}) &&
1765                                 defined($hits->{$macaddress}->{$headertag}[0])
1766                         ) {
1767                                 push @drops, "DELETE FROM $job_queue_tn WHERE id = $hits->{$macaddress}->{$headertag}[0]";
1768                         }
1769                         $hits->{$macaddress}->{$headertag}= $hit;
1770                 }
1772                 # Delete new jobs with a matching job in state 'processing'
1773                 foreach my $macaddress (keys %{$hits}) {
1774                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1775                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1776                                 if(defined($jobdb_id)) {
1777                                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND headertag='$jobdb_headertag' AND status='processing'";
1778                                         my $res = $job_db->exec_statement( $sql_statement );
1779                                         foreach my $hit (@{$res}) {
1780                                                 push @drops, "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1781                                         }
1782                                 } else {
1783                                         daemon_log("J ERROR: Job without id exists for macaddress $macaddress!", 1);
1784                                 }
1785                         }
1786                 }
1788                 # Commit deletion
1789                 $job_db->exec_statementlist(\@drops);
1791                 # Look for new jobs that could be executed
1792                 foreach my $macaddress (keys %{$hits}) {
1794                         # Look if there is an executing job
1795                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='processing'";
1796                         my $res = $job_db->exec_statement( $sql_statement );
1798                         # Skip new jobs for host if there is a processing job
1799                         if(defined($res) and defined @{$res}[0]) {
1800                                 # Prevent race condition if there is a trigger_activate job waiting and a goto-activation job processing
1801                                 my $row = @{$res}[0] if (ref $res eq 'ARRAY');
1802                                 if(@{$row}[5] eq 'trigger_action_reinstall') {
1803                                         my $sql_statement_2 =  "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='waiting' AND headertag = 'trigger_activate_new'"; 
1804                                         my $res_2 = $job_db->exec_statement( $sql_statement_2 );
1805                                         if(defined($res_2) and defined @{$res_2}[0]) {
1806                                                 # Set status from goto-activation to 'waiting' and update timestamp
1807                                                 $job_db->exec_statement("UPDATE $job_queue_tn SET status='waiting' WHERE macaddress LIKE '$macaddress' AND headertag = 'trigger_action_reinstall'");
1808                                                 $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'");
1809                                         }
1810                                 }
1811                                 next;
1812                         }
1814                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1815                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1816                                 if(defined($jobdb_id)) {
1817                                         my $job_msg = @{$hits->{$macaddress}->{$jobdb_headertag}}[7];
1819                                         daemon_log("J DEBUG: its time to execute $job_msg", 7);
1820                                         my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1821                                         my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1823                                         # expect macaddress is unique!!!!!!
1824                                         my $target = $res_hash->{1}->{hostname};
1826                                         # change header
1827                                         $job_msg =~ s/<header>job_/<header>gosa_/;
1829                                         # add sqlite_id
1830                                         $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1832                                         $job_msg =~ /<header>(\S+)<\/header>/;
1833                                         my $header = $1 ;
1834                                         my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");                    
1836                                         # update status in job queue to ...
1837                     # ... 'processing', for jobs: 'reinstall', 'update'
1838                     if (($header =~ /gosa_trigger_action_reinstall/) 
1839                             || ($header =~ /gosa_trigger_activate_new/)
1840                             || ($header =~ /gosa_trigger_action_update/)) {
1841                         my $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
1842                         my $dbres = $job_db->update_dbentry($sql_statement);
1843                     }
1845                     # ... 'done', for all other jobs, they are no longer needed in the jobqueue
1846                     else {
1847                         my $sql_statement = "UPDATE $job_queue_tn SET status='done' WHERE id=$jobdb_id";
1848                         my $dbres = $job_db->update_dbentry($sql_statement);
1849                     }
1850                 
1852                                         # We don't want parallel processing
1853                                         last;
1854                                 }
1855                         }
1856                 }
1858                 $watch_for_new_jobs_in_progress = 0;
1859                 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1860         }
1864 sub watch_for_new_messages {
1865     my ($kernel,$heap) = @_[KERNEL, HEAP];
1866     my @coll_user_msg;   # collection list of outgoing messages
1867     
1868     # check messaging_db for new incoming messages with executable timestamp
1869     my $timestamp = &get_time();
1870     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS UNSIGNED))<$timestamp AND flag='n' AND direction='in' )";
1871     my $res = $messaging_db->exec_statement( $sql_statement );
1872         foreach my $hit (@{$res}) {
1874         # create outgoing messages
1875         my $message_to = @{$hit}[3];
1876         # translate message_to to plain login name
1877         my @message_to_l = split(/,/, $message_to);  
1878                 my %receiver_h; 
1879                 foreach my $receiver (@message_to_l) {
1880                         if ($receiver =~ /^u_([\s\S]*)$/) {
1881                                 $receiver_h{$1} = 0;
1882                         } elsif ($receiver =~ /^g_([\s\S]*)$/) {
1883                                 my $group_name = $1;
1884                                 # fetch all group members from ldap and add them to receiver hash
1885                                 my $ldap_handle = &get_ldap_handle();
1886                                 if (defined $ldap_handle) {
1887                                                 my $mesg = $ldap_handle->search(
1888                                                                                 base => $ldap_base,
1889                                                                                 scope => 'sub',
1890                                                                                 attrs => ['memberUid'],
1891                                                                                 filter => "cn=$group_name",
1892                                                                                 );
1893                                                 &release_ldap_handle($ldap_handle);
1894                                                 if ($mesg->count) {
1895                                                                 my @entries = $mesg->entries;
1896                                                                 foreach my $entry (@entries) {
1897                                                                                 my @receivers= $entry->get_value("memberUid");
1898                                                                                 foreach my $receiver (@receivers) { 
1899                                                                                                 $receiver_h{$receiver} = 0;
1900                                                                                 }
1901                                                                 }
1902                                                 } 
1903                                                 # translating errors ?
1904                                                 if ($mesg->code) {
1905                                                                 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: $mesg->error", 1);
1906                                                 }
1907                                 # ldap handle error ?           
1908                                 } else {
1909                                         daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: no ldap handle available", 1);
1910                                 }
1911                         } else {
1912                                 my $sbjct = &encode_base64(@{$hit}[1]);
1913                                 my $msg = &encode_base64(@{$hit}[7]);
1914                                 &daemon_log("M WARNING: unknown receiver '$receiver' for a user-message '$sbjct - $msg'", 3); 
1915                         }
1916                 }
1917                 my @receiver_l = keys(%receiver_h);
1919         my $message_id = @{$hit}[0];
1921         #add each outgoing msg to messaging_db
1922         my $receiver;
1923         foreach $receiver (@receiver_l) {
1924             my $sql_statement = "INSERT INTO $messaging_tn (id, subject, message_from, message_to, flag, direction, delivery_time, message, timestamp) ".
1925                 "VALUES ('".
1926                 $message_id."', '".    # id
1927                 @{$hit}[1]."', '".     # subject
1928                 @{$hit}[2]."', '".     # message_from
1929                 $receiver."', '".      # message_to
1930                 "none"."', '".         # flag
1931                 "out"."', '".          # direction
1932                 @{$hit}[6]."', '".     # delivery_time
1933                 @{$hit}[7]."', '".     # message
1934                 $timestamp."'".     # timestamp
1935                 ")";
1936             &daemon_log("M DEBUG: $sql_statement", 1);
1937             my $res = $messaging_db->exec_statement($sql_statement);
1938             &daemon_log("M INFO: message '".@{$hit}[0]."' is prepared for delivery to receiver '$receiver'", 5);
1939         }
1941         # set incoming message to flag d=deliverd
1942         $sql_statement = "UPDATE $messaging_tn SET flag='p' WHERE id='$message_id'"; 
1943         &daemon_log("M DEBUG: $sql_statement", 7);
1944         $res = $messaging_db->update_dbentry($sql_statement);
1945         &daemon_log("M INFO: message '$message_id' is set to flag 'p' (processed)", 5);
1946     }
1948     $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay); 
1949     return;
1952 sub watch_for_delivery_messages {
1953     my ($kernel, $heap) = @_[KERNEL, HEAP];
1955     # select outgoing messages
1956     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( flag='p' AND direction='out' )";
1957     #&daemon_log("0 DEBUG: $sql", 7);
1958     my $res = $messaging_db->exec_statement( $sql_statement );
1959     
1960     # build out msg for each    usr
1961     foreach my $hit (@{$res}) {
1962         my $receiver = @{$hit}[3];
1963         my $msg_id = @{$hit}[0];
1964         my $subject = @{$hit}[1];
1965         my $message = @{$hit}[7];
1967         # resolve usr -> host where usr is logged in
1968         my $sql = "SELECT * FROM $login_users_tn WHERE (user='$receiver')"; 
1969         #&daemon_log("0 DEBUG: $sql", 7);
1970         my $res = $login_users_db->exec_statement($sql);
1972         # receiver is logged in nowhere
1973         if (not ref(@$res[0]) eq "ARRAY") { next; }    
1975         # receiver ist logged in at a client registered at local server
1976                 my $send_succeed = 0;
1977                 foreach my $hit (@$res) {
1978                                 my $receiver_host = @$hit[0];
1979                 my $delivered2host = 0;
1980                                 &daemon_log("M DEBUG: user '$receiver' is logged in at host '$receiver_host'", 7);
1982                                 # Looking for host in know_clients_db 
1983                                 my $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$receiver_host')";
1984                                 my $res = $known_clients_db->exec_statement($sql);
1986                 # Host is known in known_clients_db
1987                 if (ref(@$res[0]) eq "ARRAY") {
1988                     my $receiver_key = @{@{$res}[0]}[2];
1989                     my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
1990                     my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
1991                     my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0); 
1992                     if ($error == 0 ) {
1993                         $send_succeed++ ;
1994                         $delivered2host++ ;
1995                         &daemon_log("M DEBUG: send message for user '$receiver' to host '$receiver_host'", 7); 
1996                     } else {
1997                         &daemon_log("M DEBUG: cannot send message for user '$receiver' to host '$receiver_host'", 7); 
1998                     }
1999                 }
2000                 
2001                 # Message already send, do not need to do anything more, otherwise ...
2002                 if ($delivered2host) { next;}
2003     
2004                 # ...looking for host in foreign_clients_db
2005                 $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$receiver_host')";
2006                 $res = $foreign_clients_db->exec_statement($sql);
2007   
2008                                 # Host is known in foreign_clients_db 
2009                                 if (ref(@$res[0]) eq "ARRAY") { 
2010                     my $registration_server = @{@{$res}[0]}[2];
2011                     
2012                     # Fetch encryption key for registration server
2013                     my $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$registration_server')";
2014                     my $res = $known_server_db->exec_statement($sql);
2015                     if (ref(@$res[0]) eq "ARRAY") { 
2016                         my $registration_server_key = @{@{$res}[0]}[3];
2017                         my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
2018                         my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
2019                         my $error = &send_msg_to_target($out_msg, $registration_server, $registration_server_key, "usr_msg", 0); 
2020                         if ($error == 0 ) {
2021                             $send_succeed++ ;
2022                             $delivered2host++ ;
2023                             &daemon_log("M DEBUG: send message for user '$receiver' to server '$registration_server'", 7); 
2024                         } else {
2025                             &daemon_log("M ERROR: cannot send message for user '$receiver' to server '$registration_server'", 1); 
2026                         }
2028                     } else {
2029                         &daemon_log("M ERROR: host '$receiver_host' is reported to be ".
2030                                 "registrated at server '$registration_server', ".
2031                                 "but no data available in known_server_db ", 1); 
2032                     }
2033                 }
2034                 
2035                 if (not $delivered2host) {
2036                     &daemon_log("M ERROR: unable to send user message to host '$receiver_host'", 1);
2037                 }
2038                 }
2040                 if ($send_succeed) {
2041                                 # set outgoing msg at db to deliverd
2042                                 my $sql = "UPDATE $messaging_tn SET flag='d' WHERE (id='$msg_id' AND direction='out' AND message_to='$receiver')"; 
2043                                 my $res = $messaging_db->exec_statement($sql); 
2044                 &daemon_log("M INFO: send message for user '$receiver' to logged in hosts", 5);
2045                 } else {
2046             &daemon_log("M WARNING: failed to deliver message for user '$receiver'", 3); 
2047         }
2048         }
2050     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay); 
2051     return;
2055 sub watch_for_done_messages {
2056     my ($kernel,$heap) = @_[KERNEL, HEAP];
2058     my $sql = "SELECT * FROM $messaging_tn WHERE (flag='p' AND direction='in')"; 
2059     #&daemon_log("0 DEBUG: $sql", 7);
2060     my $res = $messaging_db->exec_statement($sql); 
2062     foreach my $hit (@{$res}) {
2063         my $msg_id = @{$hit}[0];
2065         my $sql = "SELECT * FROM $messaging_tn WHERE (id='$msg_id' AND direction='out' AND (NOT flag='s'))"; 
2066         #&daemon_log("0 DEBUG: $sql", 7); 
2067         my $res = $messaging_db->exec_statement($sql);
2069         # not all usr msgs have been seen till now
2070         if ( ref(@$res[0]) eq "ARRAY") { next; }
2071         
2072         $sql = "DELETE FROM $messaging_tn WHERE (id='$msg_id')"; 
2073         #&daemon_log("0 DEBUG: $sql", 7);
2074         $res = $messaging_db->exec_statement($sql);
2075     
2076     }
2078     $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay); 
2079     return;
2083 sub watch_for_old_known_clients {
2084     my ($kernel,$heap) = @_[KERNEL, HEAP];
2086     my $sql_statement = "SELECT * FROM $known_clients_tn";
2087     my $res = $known_clients_db->select_dbentry( $sql_statement );
2089     my $cur_time = int(&get_time());
2091     while ( my ($hit_num, $hit) = each %$res) {
2092         my $expired_timestamp = int($hit->{'timestamp'});
2093         $expired_timestamp =~ /(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/;
2094         my $dt = DateTime->new( year   => $1,
2095                 month  => $2,
2096                 day    => $3,
2097                 hour   => $4,
2098                 minute => $5,
2099                 second => $6,
2100                 );
2102         $dt->add( seconds => 2 * int($hit->{'keylifetime'}) );
2103         $expired_timestamp = $dt->ymd('').$dt->hms('');
2104         if ($cur_time > $expired_timestamp) {
2105             my $hostname = $hit->{'hostname'};
2106             my $del_sql = "DELETE FROM $known_clients_tn WHERE hostname='$hostname'"; 
2107             my $del_res = $known_clients_db->exec_statement($del_sql);
2109             &main::daemon_log("0 INFO: timestamp '".$hit->{'timestamp'}."' of client '$hostname' is expired('$expired_timestamp'), client will be deleted from known_clients_db", 5);
2110         }
2112     }
2114     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
2118 sub watch_for_next_tasks {
2119     my ($kernel,$heap) = @_[KERNEL, HEAP];
2121     my $sql = "SELECT * FROM $incoming_tn";
2122     my $res = $incoming_db->select_dbentry($sql);
2123     
2124     while ( my ($hit_num, $hit) = each %$res) {
2125         my $headertag = $hit->{'headertag'};
2126         if ($headertag =~ /^answer_(\d+)/) {
2127             # do not start processing, this message is for a still running POE::Wheel
2128             next;
2129         }
2130         my $message_id = $hit->{'id'};
2131         my $session_id = $hit->{'sessionid'};
2132         &daemon_log("$session_id DEBUG: start processing for message with incoming id: '$message_id'", 7);
2134                 my $ldap_handle = &get_ldap_handle();
2135                 if (not defined $ldap_handle) { next; }
2136         $kernel->yield('next_task', $hit, $ldap_handle);
2138         my $sql = "DELETE FROM $incoming_tn WHERE id=$message_id";
2139         my $res = $incoming_db->exec_statement($sql);
2140     }
2142     $kernel->delay_set('watch_for_next_tasks', 1); 
2146 sub get_ldap_handle {
2147         my ($session_id) = @_;
2148         my $heap;
2150         if (not defined $session_id ) { $session_id = 0 };
2151         if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
2153         (my $package, my $file, my $row, my $subroutine, my $hasArgs, my $wantArray, my $evalText, my $isRequire) = caller(1);
2154         my $caller_text = "subroutin $subroutine";
2155         if ($subroutine eq "(eval)") {
2156                 $caller_text = "eval block within file '$file' for '$evalText'"; 
2157         }
2158         daemon_log("$session_id INFO: new ldap handle for $caller_text required", 9);
2160         my $ldap_handle = $ldap_pool->get();
2161         
2162         if (not defined $ldap_handle) {
2163                 daemon_log("$session_id ERROR: ldap handle for $caller_text not available", 1);
2164         }
2165         return $ldap_handle;
2168 sub release_ldap_handle {
2169         my ($ldap_handle) = @_ ;
2170         $ldap_pool->free($ldap_handle);
2171         return;
2175 sub change_fai_state {
2176     my ($st, $targets, $session_id) = @_;
2177     $session_id = 0 if not defined $session_id;
2178     # Set FAI state to localboot
2179     my %mapActions= (
2180         reboot    => '',
2181         update    => 'softupdate',
2182         localboot => 'localboot',
2183         reinstall => 'install',
2184         rescan    => '',
2185         wake      => '',
2186         memcheck  => 'memcheck',
2187         sysinfo   => 'sysinfo',
2188         install   => 'install',
2189     );
2191     # Return if this is unknown
2192     if (!exists $mapActions{ $st }){
2193         daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1); 
2194       return;
2195     }
2197     my $state= $mapActions{ $st };
2199     #if( defined($ldap_handle) ) {
2201       # Build search filter for hosts
2202         my $search= "(&(objectClass=GOhard)";
2203         foreach (@{$targets}){
2204             $search.= "(macAddress=$_)";
2205         }
2206         $search.= ")";
2208       # If there's any host inside of the search string, procress them
2209         if (!($search =~ /macAddress/)){
2210             daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);    
2211             return;
2212         }
2214         my $ldap_handle = &get_ldap_handle($session_id);
2215       # Perform search for Unit Tag
2216       my $mesg = $ldap_handle->search(
2217           base   => $ldap_base,
2218           scope  => 'sub',
2219           attrs  => ['dn', 'FAIstate', 'objectClass'],
2220           filter => "$search"
2221           );
2223           if ($mesg->count) {
2224                   my @entries = $mesg->entries;
2225                   if (0 == @entries) {
2226                                   daemon_log("$session_id ERROR: ldap search failed: ldap_base=$ldap_base, filter=$search", 1); 
2227                   }
2229                   foreach my $entry (@entries) {
2230                           # Only modify entry if it is not set to '$state'
2231                           if ($entry->get_value("FAIstate") ne "$state"){
2232                                   daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
2233                                   my $result;
2234                                   my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
2235                                   if (exists $tmp{'FAIobject'}){
2236                                           if ($state eq ''){
2237                                                   $result= $ldap_handle->modify($entry->dn, changes => [ delete => [ FAIstate => [] ] ]);
2238                                           } else {
2239                                                   $result= $ldap_handle->modify($entry->dn, changes => [ replace => [ FAIstate => $state ] ]);
2240                                           }
2241                                   } elsif ($state ne ''){
2242                                           $result= $ldap_handle->modify($entry->dn, changes => [ add => [ objectClass => 'FAIobject' ], add => [ FAIstate => $state ] ]);
2243                                   }
2245                                   # Errors?
2246                                   if ($result->code){
2247                                           daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2248                                   }
2249                           } else {
2250                                   daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 7); 
2251                           }  
2252                   }
2253           } else {
2254                 daemon_log("$session_id ERROR: LDAP search failed: ldap_base=$ldap_base, filter=$search", 1);
2255           }
2256           &release_ldap_handle($ldap_handle);             
2258     # if no ldap handle defined
2259     #} else {
2260     #    daemon_log("$session_id ERROR: no LDAP handle defined for update FAIstate", 1); 
2261     #}
2263         return;
2267 sub change_goto_state {
2268     my ($st, $targets, $session_id) = @_;
2269     $session_id = 0  if not defined $session_id;
2271     # Switch on or off?
2272     my $state= $st eq 'active' ? 'active': 'locked';
2274     my $ldap_handle = &get_ldap_handle($session_id);
2275     if( defined($ldap_handle) ) {
2277       # Build search filter for hosts
2278       my $search= "(&(objectClass=GOhard)";
2279       foreach (@{$targets}){
2280         $search.= "(macAddress=$_)";
2281       }
2282       $search.= ")";
2284       # If there's any host inside of the search string, procress them
2285       if (!($search =~ /macAddress/)){
2286         return;
2287       }
2289       # Perform search for Unit Tag
2290       my $mesg = $ldap_handle->search(
2291           base   => $ldap_base,
2292           scope  => 'sub',
2293           attrs  => ['dn', 'gotoMode'],
2294           filter => "$search"
2295           );
2297       if ($mesg->count) {
2298         my @entries = $mesg->entries;
2299         foreach my $entry (@entries) {
2301           # Only modify entry if it is not set to '$state'
2302           if ($entry->get_value("gotoMode") ne $state){
2304             daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
2305             my $result;
2306             $result= $ldap_handle->modify($entry->dn, changes => [replace => [ gotoMode => $state ] ]);
2308             # Errors?
2309             if ($result->code){
2310               &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2311             }
2313           }
2314         }
2315       } else {
2316                 daemon_log("$session_id ERROR: LDAP search failed in function change_goto_state: ldap_base=$ldap_base, filter=$search", 1);
2317           }
2319     }
2320         &release_ldap_handle($ldap_handle);
2321         return;
2325 sub run_recreate_packages_db {
2326     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2327     my $session_id = $session->ID;
2328         &main::daemon_log("$session_id INFO: Recreating FAI Packages DB ('$fai_release_tn', '$fai_server_tn', '$packages_list_tn')", 5);
2329         $kernel->yield('create_fai_release_db', $fai_release_tn);
2330         $kernel->yield('create_fai_server_db', $fai_server_tn);
2331         return;
2335 sub run_create_fai_server_db {
2336     my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2337     my $session_id = $session->ID;
2338         my $ldap_handle = &get_ldap_handle();
2339         if (not defined $ldap_handle) {
2340                 $kernel->delay_set('create_fai_server_db', 1, $table_name);
2341                 return;
2342         }
2343     my $task = POE::Wheel::Run->new(
2344             Program => sub { &create_fai_server_db($table_name,$kernel, undef, $session_id, $ldap_handle) },
2345             StdoutEvent  => "session_run_result",
2346             StderrEvent  => "session_run_debug",
2347             CloseEvent   => "session_run_done",
2348             );
2350     $heap->{task}->{ $task->ID } = $task;
2351         $heap->{ldap_handle}->{$task->ID} = $ldap_handle;
2352     return;
2356 sub create_fai_server_db {
2357         my ($table_name, $kernel, $dont_create_packages_list, $session_id, $ldap_handle) = @_;
2358         my $result;
2360         if (not defined $session_id) { $session_id = 0; }
2361         if(defined($ldap_handle)) {
2362                 daemon_log("$session_id INFO: create_fai_server_db: start", 5);
2363                 my $mesg= $ldap_handle->search(
2364                         base   => $ldap_base,
2365                         scope  => 'sub',
2366                         attrs  => ['FAIrepository', 'gosaUnitTag'],
2367                         filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
2368                 );
2369                 if($mesg->{'resultCode'} == 0 &&
2370                         $mesg->count != 0) {
2371                         foreach my $entry (@{$mesg->{entries}}) {
2372                                 if($entry->exists('FAIrepository')) {
2373                                         # Add an entry for each Repository configured for server
2374                                         foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
2375                                                 my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
2376                                                 my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
2377                                                 $result= $fai_server_db->add_dbentry( { 
2378                                                                 table => $table_name,
2379                                                                 primkey => ['server', 'fai_release', 'tag'],
2380                                                                 server => $tmp_url,
2381                                                                 fai_release => $tmp_release,
2382                                                                 sections => $tmp_sections,
2383                                                                 tag => (length($tmp_tag)>0)?$tmp_tag:"",
2384                                                         } );
2385                                         }
2386                                 }
2387                         }
2388                 }
2389                 daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
2391                 # TODO: Find a way to post the 'create_packages_list_db' event
2392                 if(not defined($dont_create_packages_list)) {
2393                         &create_packages_list_db(undef, $session_id);
2394                 }
2395         }       
2397         return $result;
2401 sub run_create_fai_release_db {
2402         my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2403         my $session_id = $session->ID;
2404         my $ldap_handle = &get_ldap_handle();
2405         if (not defined $ldap_handle) {
2406                 $kernel->delay_set('create_fai_release_db', 1, $table_name);
2407                 return;
2408         }
2409         my $task = POE::Wheel::Run->new(
2410                 Program => sub { &create_fai_release_db($table_name, $session_id, $ldap_handle) },
2411                 StdoutEvent  => "session_run_result",
2412                 StderrEvent  => "session_run_debug",
2413                 CloseEvent   => "session_run_done",
2414         );
2416         $heap->{task}->{ $task->ID } = $task;
2417         $heap->{ldap_handle}->{$task->ID} = $ldap_handle;
2418         return;
2422 sub create_fai_release_db {
2423         my ($table_name, $session_id, $ldap_handle) = @_;
2424         my $result;
2426         # used for logging
2427         if (not defined $session_id) { $session_id = 0; }
2429         #my $ldap_handle = &get_ldap_handle();
2430         if(defined($ldap_handle)) {
2431                 daemon_log("$session_id INFO: create_fai_release_db: start",5);
2432                 my $mesg= $ldap_handle->search(
2433                         base   => $ldap_base,
2434                         scope  => 'sub',
2435                         attrs  => [],
2436                         filter => "(&(objectClass=organizationalUnit)(ou=fai))",
2437                 );
2438                 if(($mesg->code == 0) && ($mesg->count != 0))
2439                 {
2440                         daemon_log("$session_id DEBUG: create_fai_release_db: count " . $mesg->count,8);
2442                         # Walk through all possible FAI container ou's
2443                         my @sql_list;
2444                         my $timestamp= &get_time();
2445                         foreach my $ou (@{$mesg->{entries}}) {
2446                                 my $tmp_classes= resolve_fai_classes($ou->dn, $ldap_handle, $session_id);
2447                                 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
2448                                         my @tmp_array=get_fai_release_entries($tmp_classes);
2449                                         if(@tmp_array) {
2450                                                 foreach my $entry (@tmp_array) {
2451                                                         if(defined($entry) && ref($entry) eq 'HASH') {
2452                                                                 my $sql= 
2453                                                                 "INSERT INTO $table_name "
2454                                                                 ."(timestamp, fai_release, class, type, state) VALUES ("
2455                                                                 .$timestamp.","
2456                                                                 ."'".$entry->{'release'}."',"
2457                                                                 ."'".$entry->{'class'}."',"
2458                                                                 ."'".$entry->{'type'}."',"
2459                                                                 ."'".$entry->{'state'}."')";
2460                                                                 push @sql_list, $sql;
2461                                                         }
2462                                                 }
2463                                         }
2464                                 }
2465                         }
2467                         daemon_log("$session_id DEBUG: create_fai_release_db: Inserting ".scalar @sql_list." entries to DB",8);
2468                         if(@sql_list) {
2469                                 unshift @sql_list, "VACUUM";
2470                                 unshift @sql_list, "DELETE FROM $table_name";
2471                                 $fai_release_db->exec_statementlist(\@sql_list);
2472                         }
2473                         daemon_log("$session_id DEBUG: create_fai_release_db: Done with inserting",7);
2474                 } else {
2475                         daemon_log("$session_id INFO: create_fai_release_db: error: " . $mesg->code, 5);
2476                 }
2477                 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
2478         }
2479         #&release_ldap_handle($ldap_handle);
2480         return $result;
2483 sub get_fai_types {
2484         my $tmp_classes = shift || return undef;
2485         my @result;
2487         foreach my $type(keys %{$tmp_classes}) {
2488                 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
2489                         my $entry = {
2490                                 type => $type,
2491                                 state => $tmp_classes->{$type}[0],
2492                         };
2493                         push @result, $entry;
2494                 }
2495         }
2497         return @result;
2500 sub get_fai_state {
2501         my $result = "";
2502         my $tmp_classes = shift || return $result;
2504         foreach my $type(keys %{$tmp_classes}) {
2505                 if(defined($tmp_classes->{$type}[0])) {
2506                         $result = $tmp_classes->{$type}[0];
2507                         
2508                 # State is equal for all types in class
2509                         last;
2510                 }
2511         }
2513         return $result;
2516 sub resolve_fai_classes {
2517         my ($fai_base, $ldap_handle, $session_id) = @_;
2518         if (not defined $session_id) { $session_id = 0; }
2519         my $result;
2520         my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
2521         my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
2522         my $fai_classes;
2524         daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base",7);
2525         my $mesg= $ldap_handle->search(
2526                 base   => $fai_base,
2527                 scope  => 'sub',
2528                 attrs  => ['cn','objectClass','FAIstate'],
2529                 filter => $fai_filter,
2530         );
2531         daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries",7);
2533         if($mesg->{'resultCode'} == 0 &&
2534                 $mesg->count != 0) {
2535                 foreach my $entry (@{$mesg->{entries}}) {
2536                         if($entry->exists('cn')) {
2537                                 my $tmp_dn= $entry->dn();
2538                                 $tmp_dn= substr( $tmp_dn, 0, length($tmp_dn)
2539                                         - length($fai_base) - 1 );
2541                                 # Skip classname and ou dn parts for class
2542                                 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?)$/;
2544                                 # Skip classes without releases
2545                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2546                                         next;
2547                                 }
2549                                 my $tmp_cn= $entry->get_value('cn');
2550                                 my $tmp_state= $entry->get_value('FAIstate');
2552                                 my $tmp_type;
2553                                 # Get FAI type
2554                                 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
2555                                         if(grep $_ eq $oclass, @possible_fai_classes) {
2556                                                 $tmp_type= $oclass;
2557                                                 last;
2558                                         }
2559                                 }
2561                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2562                                         # A Subrelease
2563                                         my @sub_releases = split(/,/, $tmp_release);
2565                                         # Walk through subreleases and build hash tree
2566                                         my $hash;
2567                                         while(my $tmp_sub_release = pop @sub_releases) {
2568                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2569                                         }
2570                                         eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
2571                                 } else {
2572                                         # A branch, no subrelease
2573                                         push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
2574                                 }
2575                         } elsif (!$entry->exists('cn')) {
2576                                 my $tmp_dn= $entry->dn();
2577                                 $tmp_dn= substr( $tmp_dn, 0, length($tmp_dn)
2578                                         - length($fai_base) - 1 );
2579                                 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?)$/;
2581                                 # Skip classes without releases
2582                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2583                                         next;
2584                                 }
2586                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2587                                         # A Subrelease
2588                                         my @sub_releases= split(/,/, $tmp_release);
2590                                         # Walk through subreleases and build hash tree
2591                                         my $hash;
2592                                         while(my $tmp_sub_release = pop @sub_releases) {
2593                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2594                                         }
2595                                         # Remove the last two characters
2596                                         chop($hash);
2597                                         chop($hash);
2599                                         eval('$fai_classes->'.$hash.'= {}');
2600                                 } else {
2601                                         # A branch, no subrelease
2602                                         if(!exists($fai_classes->{$tmp_release})) {
2603                                                 $fai_classes->{$tmp_release} = {};
2604                                         }
2605                                 }
2606                         }
2607                 }
2609                 # The hash is complete, now we can honor the copy-on-write based missing entries
2610                 foreach my $release (keys %$fai_classes) {
2611                         $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
2612                 }
2613         }
2614         return $result;
2617 sub apply_fai_inheritance {
2618        my $fai_classes = shift || return {};
2619        my $tmp_classes;
2621        # Get the classes from the branch
2622        foreach my $class (keys %{$fai_classes}) {
2623                # Skip subreleases
2624                if($class =~ /^ou=.*$/) {
2625                        next;
2626                } else {
2627                        $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
2628                }
2629        }
2631        # Apply to each subrelease
2632        foreach my $subrelease (keys %{$fai_classes}) {
2633                if($subrelease =~ /ou=/) {
2634                        foreach my $tmp_class (keys %{$tmp_classes}) {
2635                                if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
2636                                        $fai_classes->{$subrelease}->{$tmp_class} =
2637                                        deep_copy($tmp_classes->{$tmp_class});
2638                                } else {
2639                                        foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
2640                                                if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
2641                                                        $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
2642                                                        deep_copy($tmp_classes->{$tmp_class}->{$type});
2643                                                }
2644                                        }
2645                                }
2646                        }
2647                }
2648        }
2650        # Find subreleases in deeper levels
2651        foreach my $subrelease (keys %{$fai_classes}) {
2652                if($subrelease =~ /ou=/) {
2653                        foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
2654                                if($subsubrelease =~ /ou=/) {
2655                                        apply_fai_inheritance($fai_classes->{$subrelease});
2656                                }
2657                        }
2658                }
2659        }
2661        return $fai_classes;
2664 sub get_fai_release_entries {
2665         my $tmp_classes = shift || return;
2666         my $parent = shift || "";
2667         my @result = shift || ();
2669         foreach my $entry (keys %{$tmp_classes}) {
2670                 if(defined($entry)) {
2671                         if($entry =~ /^ou=.*$/) {
2672                                 my $release_name = $entry;
2673                                 $release_name =~ s/ou=//g;
2674                                 if(length($parent)>0) {
2675                                         $release_name = $parent."/".$release_name;
2676                                 }
2677                                 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2678                                 foreach my $bufentry(@bufentries) {
2679                                         push @result, $bufentry;
2680                                 }
2681                         } else {
2682                                 my @types = get_fai_types($tmp_classes->{$entry});
2683                                 foreach my $type (@types) {
2684                                         push @result, 
2685                                         {
2686                                                 'class' => $entry,
2687                                                 'type' => $type->{'type'},
2688                                                 'release' => $parent,
2689                                                 'state' => $type->{'state'},
2690                                         };
2691                                 }
2692                         }
2693                 }
2694         }
2696         return @result;
2699 sub deep_copy {
2700         my $this = shift;
2701         if (not ref $this) {
2702                 $this;
2703         } elsif (ref $this eq "ARRAY") {
2704                 [map deep_copy($_), @$this];
2705         } elsif (ref $this eq "HASH") {
2706                 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2707         } else { die "what type is $_?" }
2711 sub session_run_result {
2712     my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];    
2713     $kernel->sig(CHLD => "child_reap");
2716 sub session_run_debug {
2717     my $result = $_[ARG0];
2718     print STDERR "$result\n";
2721 sub session_run_done {
2722     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2723     delete $heap->{task}->{$task_id};
2724         if (exists $heap->{ldap_handle}->{$task_id}) {
2725                 &release_ldap_handle($heap->{ldap_handle}->{$task_id});
2726         }
2727         delete $heap->{ldap_handle}->{$task_id};
2731 sub create_sources_list {
2732         my $session_id = shift;
2733         my $result="/tmp/gosa_si_tmp_sources_list";
2735         # Remove old file
2736         if(stat($result)) {
2737                 unlink($result);
2738                 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7); 
2739         }
2741         my $fh;
2742         open($fh, ">$result");
2743         if (not defined $fh) {
2744                 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7); 
2745                 return undef;
2746         }
2747         if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2748                 my $ldap_handle = &get_ldap_handle();
2749                 my $mesg=$ldap_handle->search(
2750                         base    => $main::ldap_server_dn,
2751                         scope   => 'base',
2752                         attrs   => 'FAIrepository',
2753                         filter  => 'objectClass=FAIrepositoryServer'
2754                 );
2755                 &release_ldap_handle($ldap_handle);
2756                 if($mesg->count) {
2757                         foreach my $entry(@{$mesg->{'entries'}}) {
2758                                 foreach my $value(@{$entry->get_value('FAIrepository', asref => 1)}) {
2759                                         my ($server, $tag, $release, $sections)= split /\|/, $value;
2760                                         my $line = "deb $server $release";
2761                                         $sections =~ s/,/ /g;
2762                                         $line.= " $sections";
2763                                         print $fh $line."\n";
2764                                 }
2765                         }
2766                 }
2767         } else {
2768                 if (defined $main::ldap_server_dn){
2769                         &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1); 
2770                 } else {
2771                         &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2772                 }
2773         }
2774         close($fh);
2776         return $result;
2780 sub run_create_packages_list_db {
2781     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2782         my $session_id = $session->ID;
2783         my $task = POE::Wheel::Run->new(
2784                                         Priority => +20,
2785                                         Program => sub {&create_packages_list_db(undef, $session_id)},
2786                                         StdoutEvent  => "session_run_result",
2787                                         StderrEvent  => "session_run_debug",
2788                                         CloseEvent   => "session_run_done",
2789                                         );
2790         $heap->{task}->{ $task->ID } = $task;
2794 sub create_packages_list_db {
2795         my ($sources_file, $session_id) = @_;
2796         
2797         # it should not be possible to trigger a recreation of packages_list_db
2798         # while packages_list_db is under construction, so set flag packages_list_under_construction
2799         # which is tested befor recreation can be started
2800         if (-r $packages_list_under_construction) {
2801                 daemon_log("$session_id WARNING: packages_list_db is right now under construction, please wait until this process is finished", 3);
2802                 return;
2803         } else {
2804                 daemon_log("$session_id INFO: create_packages_list_db: start", 5); 
2805                 # set packages_list_under_construction to true
2806                 system("touch $packages_list_under_construction");
2807                 @packages_list_statements=();
2808         }
2810         if (not defined $session_id) { $session_id = 0; }
2812         if (not defined $sources_file) { 
2813                 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5); 
2814                 $sources_file = &create_sources_list($session_id);
2815         }
2817         if (not defined $sources_file) {
2818                 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1); 
2819                 unlink($packages_list_under_construction);
2820                 return;
2821         }
2823         my $line;
2825         open(CONFIG, "<$sources_file") or do {
2826                 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2827                 unlink($packages_list_under_construction);
2828                 return;
2829         };
2831         # Read lines
2832         while ($line = <CONFIG>){
2833                 # Unify
2834                 chop($line);
2835                 $line =~ s/^\s+//;
2836                 $line =~ s/^\s+/ /;
2838                 # Strip comments
2839                 $line =~ s/#.*$//g;
2841                 # Skip empty lines
2842                 if ($line =~ /^\s*$/){
2843                         next;
2844                 }
2846                 # Interpret deb line
2847                 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2848                         my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2849                         my $section;
2850                         foreach $section (split(' ', $sections)){
2851                                 &parse_package_info( $baseurl, $dist, $section, $session_id );
2852                         }
2853                 }
2854         }
2856         close (CONFIG);
2858         if(keys(%repo_dirs)) {
2859                 find(\&cleanup_and_extract, keys( %repo_dirs ));
2860                 &main::strip_packages_list_statements();
2861                 $packages_list_db->exec_statementlist(\@packages_list_statements);
2862         }
2863         unlink($packages_list_under_construction);
2864         daemon_log("$session_id INFO: create_packages_list_db: finished", 5); 
2865         return;
2868 # This function should do some intensive task to minimize the db-traffic
2869 sub strip_packages_list_statements {
2870         my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2871         my @new_statement_list=();
2872         my $hash;
2873         my $insert_hash;
2874         my $update_hash;
2875         my $delete_hash;
2876         my $known_packages_hash;
2877         my $local_timestamp=get_time();
2879         foreach my $existing_entry (@existing_entries) {
2880                 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
2881         }
2883         foreach my $statement (@packages_list_statements) {
2884                 if($statement =~ /^INSERT/i) {
2885                         # Assign the values from the insert statement
2886                         my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~ 
2887                         /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
2888                         if(exists($hash->{$distribution}->{$package}->{$version})) {
2889                                 # If section or description has changed, update the DB
2890                                 if( 
2891                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or 
2892                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
2893                                 ) {
2894                                         @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
2895                                 } else {
2896                                         # package is already present in database. cache this knowledge for later use
2897                                         @{$known_packages_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2898                                 }
2899                         } else {
2900                                 # Insert a non-existing entry to db
2901                                 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2902                         }
2903                 } elsif ($statement =~ /^UPDATE/i) {
2904                         my ($template,$package,$version) = ($1,$2,$3) if $statement =~
2905                         /^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;
2906                         foreach my $distribution (keys %{$hash}) {
2907                                 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
2908                                         # update the insertion hash to execute only one query per package (insert instead insert+update)
2909                                         @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
2910                                 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
2911                                         if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
2912                                                 my $section;
2913                                                 my $description;
2914                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
2915                                                         length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
2916                                                         $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
2917                                                 }
2918                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2919                                                         $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
2920                                                 }
2921                                                 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2922                                         }
2923                                 }
2924                         }
2925                 }
2926         }
2928         # Check for orphaned entries
2929         foreach my $existing_entry (@existing_entries) {
2930                 my $distribution= @{$existing_entry}[0];
2931                 my $package= @{$existing_entry}[1];
2932                 my $version= @{$existing_entry}[2];
2933                 my $section= @{$existing_entry}[3];
2935                 if(
2936                         exists($insert_hash->{$distribution}->{$package}->{$version}) ||
2937                         exists($update_hash->{$distribution}->{$package}->{$version}) ||
2938                         exists($known_packages_hash->{$distribution}->{$package}->{$version})
2939                 ) {
2940                         next;
2941                 } else {
2942                         # Insert entry to delete hash
2943                         @{$delete_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section);
2944                 }
2945         }
2947         # unroll the insert hash
2948         foreach my $distribution (keys %{$insert_hash}) {
2949                 foreach my $package (keys %{$insert_hash->{$distribution}}) {
2950                         foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
2951                                 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
2952                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
2953                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
2954                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
2955                                 ."'$local_timestamp')";
2956                         }
2957                 }
2958         }
2960         # unroll the update hash
2961         foreach my $distribution (keys %{$update_hash}) {
2962                 foreach my $package (keys %{$update_hash->{$distribution}}) {
2963                         foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
2964                                 my $set = "";
2965                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
2966                                         $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
2967                                 }
2968                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2969                                         $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
2970                                 }
2971                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
2972                                         $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
2973                                 }
2974                                 if(defined($set) and length($set) > 0) {
2975                                         $set .= "timestamp = '$local_timestamp'";
2976                                 } else {
2977                                         next;
2978                                 }
2979                                 push @new_statement_list, 
2980                                 "UPDATE $main::packages_list_tn SET $set WHERE"
2981                                 ." distribution = '$distribution'"
2982                                 ." AND package = '$package'"
2983                                 ." AND version = '$version'";
2984                         }
2985                 }
2986         }
2987         
2988         # unroll the delete hash
2989         foreach my $distribution (keys %{$delete_hash}) {
2990                 foreach my $package (keys %{$delete_hash->{$distribution}}) {
2991                         foreach my $version (keys %{$delete_hash->{$distribution}->{$package}}) {
2992                                 my $section = @{$delete_hash->{$distribution}->{$package}->{$version}}[3];
2993                                 push @new_statement_list, "DELETE FROM $main::packages_list_tn WHERE distribution='$distribution' AND package='$package' AND version='$version' AND section='$section'";
2994                         }
2995                 }
2996         }
2998         unshift(@new_statement_list, "VACUUM");
3000         @packages_list_statements = @new_statement_list;
3004 sub parse_package_info {
3005     my ($baseurl, $dist, $section, $session_id)= @_;
3006     my ($package);
3007     if (not defined $session_id) { $session_id = 0; }
3008     my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
3009     $repo_dirs{ "${repo_path}/pool" } = 1;
3011     foreach $package ("Packages.gz"){
3012         daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
3013         get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
3014         parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
3015     }
3016     
3020 sub get_package {
3021     my ($url, $dest, $session_id)= @_;
3022     if (not defined $session_id) { $session_id = 0; }
3024     my $tpath = dirname($dest);
3025     -d "$tpath" || mkpath "$tpath";
3027     # This is ugly, but I've no time to take a look at "how it works in perl"
3028     if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
3029         system("gunzip -cd '$dest' > '$dest.in'");
3030         daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 5);
3031         unlink($dest);
3032         daemon_log("$session_id DEBUG: delete file '$dest'", 5); 
3033     } else {
3034         daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' into '$dest' failed!", 1);
3035     }
3036     return 0;
3040 sub parse_package {
3041     my ($path, $dist, $srv_path, $session_id)= @_;
3042     if (not defined $session_id) { $session_id = 0;}
3043     my ($package, $version, $section, $description);
3044     my $PACKAGES;
3045     my $timestamp = &get_time();
3047     if(not stat("$path.in")) {
3048         daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
3049         return;
3050     }
3052     open($PACKAGES, "<$path.in");
3053     if(not defined($PACKAGES)) {
3054         daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1); 
3055         return;
3056     }
3058     # Read lines
3059     while (<$PACKAGES>){
3060         my $line = $_;
3061         # Unify
3062         chop($line);
3064         # Use empty lines as a trigger
3065         if ($line =~ /^\s*$/){
3066             my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
3067             push(@packages_list_statements, $sql);
3068             $package = "none";
3069             $version = "none";
3070             $section = "none";
3071             $description = "none"; 
3072             next;
3073         }
3075         # Trigger for package name
3076         if ($line =~ /^Package:\s/){
3077             ($package)= ($line =~ /^Package: (.*)$/);
3078             next;
3079         }
3081         # Trigger for version
3082         if ($line =~ /^Version:\s/){
3083             ($version)= ($line =~ /^Version: (.*)$/);
3084             next;
3085         }
3087         # Trigger for description
3088         if ($line =~ /^Description:\s/){
3089             ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
3090             next;
3091         }
3093         # Trigger for section
3094         if ($line =~ /^Section:\s/){
3095             ($section)= ($line =~ /^Section: (.*)$/);
3096             next;
3097         }
3099         # Trigger for filename
3100         if ($line =~ /^Filename:\s/){
3101             my ($filename) = ($line =~ /^Filename: (.*)$/);
3102             store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
3103             next;
3104         }
3105     }
3107     close( $PACKAGES );
3108     unlink( "$path.in" );
3112 sub store_fileinfo {
3113     my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
3115     my %fileinfo = (
3116         'package' => $package,
3117         'dist' => $dist,
3118         'version' => $vers,
3119     );
3121     $repo_files{ "${srvdir}/$file" } = \%fileinfo;
3125 sub cleanup_and_extract {
3126         my $fileinfo = $repo_files{ $File::Find::name };
3128         if( defined $fileinfo ) {
3129                 my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
3130                 my $sql;
3131                 my $package = $fileinfo->{ 'package' };
3132                 my $newver = $fileinfo->{ 'version' };
3134                 mkpath($dir);
3135                 system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
3137                 if( -f "$dir/DEBIAN/templates" ) {
3139                         daemon_log("0 DEBUG: Found debconf templates in '$package' - $newver", 7);
3141                         my $tmpl= ""; {
3142                                 local $/=undef;
3143                                 open FILE, "$dir/DEBIAN/templates";
3144                                 $tmpl = &encode_base64(<FILE>);
3145                                 close FILE;
3146                         }
3147                         rmtree("$dir/DEBIAN/templates");
3149                         $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
3150                         push @packages_list_statements, $sql;
3151                 }
3152         }
3154         return;
3158 sub register_at_foreign_servers {   
3159     my ($kernel) = $_[KERNEL];
3161     # hole alle bekannten server aus known_server_db
3162     my $server_sql = "SELECT * FROM $known_server_tn";
3163     my $server_res = $known_server_db->exec_statement($server_sql);
3165     # no entries in known_server_db
3166     if (not ref(@$server_res[0]) eq "ARRAY") { 
3167         # TODO
3168     }
3170     # detect already connected clients
3171     my $client_sql = "SELECT * FROM $known_clients_tn"; 
3172     my $client_res = $known_clients_db->exec_statement($client_sql);
3174     # send my server details to all other gosa-si-server within the network
3175     foreach my $hit (@$server_res) {
3176         my $hostname = @$hit[0];
3177         my $hostkey = &create_passwd;
3179         # add already connected clients to registration message 
3180         my $myhash = &create_xml_hash('new_server', $server_address, $hostname);
3181         &add_content2xml_hash($myhash, 'key', $hostkey);
3182         map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res);
3184         # add locally loaded gosa-si modules to registration message
3185         my $loaded_modules = {};
3186         while (my ($package, $pck_info) = each %$known_modules) {
3187                                                 next if ((!defined(@$pck_info[2])) || (!(ref (@$pck_info[2]) eq 'HASH')));
3188                                                 foreach my $act_module (keys(%{@$pck_info[2]})) {
3189                                                         $loaded_modules->{$act_module} = ""; 
3190                                                 }
3191         }
3193         map(&add_content2xml_hash($myhash, "loaded_modules", $_), keys(%$loaded_modules));
3195         # add macaddress to registration message
3196         my ($host_ip, $host_port) = split(/:/, $hostname);
3197         my $local_ip = &get_local_ip_for_remote_ip($host_ip);
3198         my $network_interface= &get_interface_for_ip($local_ip);
3199         my $host_mac = &get_mac_for_interface($network_interface);
3200         &add_content2xml_hash($myhash, 'macaddress', $host_mac);
3201         
3202         # build registration message and send it
3203         my $foreign_server_msg = &create_xml_string($myhash);
3204         my $error = &send_msg_to_target($foreign_server_msg, $hostname, $ServerPackages_key, "new_server", 0); 
3205     }
3206     
3207     $kernel->delay_set("register_at_foreign_servers", $foreign_servers_register_delay); 
3208     return;
3212 #==== MAIN = main ==============================================================
3213 #  parse commandline options
3214 Getopt::Long::Configure( "bundling" );
3215 GetOptions("h|help" => \&usage,
3216         "c|config=s" => \$cfg_file,
3217         "f|foreground" => \$foreground,
3218         "v|verbose+" => \$verbose,
3219         "no-arp+" => \$no_arp,
3220            );
3222 #  read and set config parameters
3223 &check_cmdline_param ;
3224 &read_configfile($cfg_file, %cfg_defaults);
3225 &check_pid;
3227 $SIG{CHLD} = 'IGNORE';
3229 # forward error messages to logfile
3230 if( ! $foreground ) {
3231   open( STDIN,  '+>/dev/null' );
3232   open( STDOUT, '+>&STDIN'    );
3233   open( STDERR, '+>&STDIN'    );
3236 # Just fork, if we are not in foreground mode
3237 if( ! $foreground ) { 
3238     chdir '/'                 or die "Can't chdir to /: $!";
3239     $pid = fork;
3240     setsid                    or die "Can't start a new session: $!";
3241     umask 0;
3242 } else { 
3243     $pid = $$; 
3246 # Do something useful - put our PID into the pid_file
3247 if( 0 != $pid ) {
3248     open( LOCK_FILE, ">$pid_file" );
3249     print LOCK_FILE "$pid\n";
3250     close( LOCK_FILE );
3251     if( !$foreground ) { 
3252         exit( 0 ) 
3253     };
3256 # parse head url and revision from svn
3257 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
3258 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
3259 $server_headURL = defined $1 ? $1 : 'unknown' ;
3260 $server_revision = defined $2 ? $2 : 'unknown' ;
3261 if ($server_headURL =~ /\/tag\// || 
3262         $server_headURL =~ /\/branches\// ) {
3263     $server_status = "stable"; 
3264 } else {
3265     $server_status = "developmental" ;
3267 # Prepare log file and set permissions
3268 $root_uid = getpwnam('root');
3269 $adm_gid = getgrnam('adm');
3270 open(FH, ">>$log_file");
3271 close FH;
3272 chmod(0440, $log_file);
3273 chown($root_uid, $adm_gid, $log_file);
3274 chown($root_uid, $adm_gid, "/var/lib/gosa-si");
3276 daemon_log(" ", 1);
3277 daemon_log("$0 started!", 1);
3278 daemon_log("status: $server_status", 1);
3279 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1); 
3281 # Create a pool of LDAP handles
3282 $ldap_factory =  ResourcePool::Factory::Net::LDAP->new($ldap_uri, version => $ldap_version);
3283 $ldap_factory->bind($ldap_admin_dn, password=>$ldap_admin_password);
3284 $ldap_pool = ResourcePool->new($ldap_factory,
3285                 Max         => $max_ldap_handle,
3286                 #MaxTry      => 1,
3287                 #SleepOnFail    => [0, 0, 1, 1],
3288                 PreCreate       => $precreate_ldap_handle,
3289 );
3292 # Buildup data bases
3294     no strict "refs";
3296     if ($db_module eq "DBmysql") {
3297         # connect to incoming_db
3298         $incoming_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3300         # connect to gosa-si job queue
3301         $job_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3303         # connect to known_clients_db
3304         $known_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3306         # connect to foreign_clients_db
3307         $foreign_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3309         # connect to known_server_db
3310         $known_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3312         # connect to login_usr_db
3313         $login_users_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3315         # connect to fai_server_db 
3316         $fai_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3318         # connect to fai_release_db
3319         $fai_release_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3321         # connect to packages_list_db
3322         $packages_list_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3324         # connect to messaging_db
3325         $messaging_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3327     } elsif ($db_module eq "DBsqlite") {
3328         # connect to incoming_db
3329         unlink($incoming_file_name);
3330         $incoming_db = GOSA::DBsqlite->new($incoming_file_name);
3331         
3332         # connect to gosa-si job queue
3333         $job_db = GOSA::DBsqlite->new($job_queue_file_name);
3334         chmod(0640, $job_queue_file_name);
3335         chown($root_uid, $adm_gid, $job_queue_file_name);
3336         
3337         # connect to known_clients_db
3338         $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
3339         chmod(0640, $known_clients_file_name);
3340         chown($root_uid, $adm_gid, $known_clients_file_name);
3341         
3342         # connect to foreign_clients_db
3343         unlink($foreign_clients_file_name);
3344         $foreign_clients_db = GOSA::DBsqlite->new($foreign_clients_file_name);
3345         chmod(0640, $foreign_clients_file_name);
3346         chown($root_uid, $adm_gid, $foreign_clients_file_name);
3347         
3348         # connect to known_server_db
3349         unlink($known_server_file_name);
3350         $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
3351         chmod(0640, $known_server_file_name);
3352         chown($root_uid, $adm_gid, $known_server_file_name);
3353         
3354         # connect to login_usr_db
3355         unlink($login_users_file_name);
3356         $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
3357         chmod(0640, $login_users_file_name);
3358         chown($root_uid, $adm_gid, $login_users_file_name);
3359         
3360         # connect to fai_server_db
3361         unlink($fai_server_file_name);
3362         $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
3363         chmod(0640, $fai_server_file_name);
3364         chown($root_uid, $adm_gid, $fai_server_file_name);
3365         
3366         # connect to fai_release_db
3367         unlink($fai_release_file_name);
3368         $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
3369         chmod(0640, $fai_release_file_name);
3370         chown($root_uid, $adm_gid, $fai_release_file_name);
3371         
3372         # connect to packages_list_db
3373         #unlink($packages_list_file_name);
3374         unlink($packages_list_under_construction);
3375         $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
3376         chmod(0640, $packages_list_file_name);
3377         chown($root_uid, $adm_gid, $packages_list_file_name);
3378         
3379         # connect to messaging_db
3380         unlink($messaging_file_name);
3381         $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
3382         chmod(0640, $messaging_file_name);
3383         chown($root_uid, $adm_gid, $messaging_file_name);
3384     }
3388 # Creating tables
3389 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
3390 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
3391 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
3392 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
3393 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
3394 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
3395 $foreign_clients_db->create_table($foreign_clients_tn, \@foreign_clients_col_names);
3396 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
3397 $incoming_db->create_table($incoming_tn, \@incoming_col_names);
3398 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
3400 # create xml object used for en/decrypting
3401 $xml = new XML::Simple();
3404 # foreign servers 
3405 my @foreign_server_list;
3407 # add foreign server from cfg file
3408 if ($foreign_server_string ne "") {
3409     my @cfg_foreign_server_list = split(",", $foreign_server_string);
3410     foreach my $foreign_server (@cfg_foreign_server_list) {
3411         push(@foreign_server_list, $foreign_server);
3412     }
3414     daemon_log("0 INFO: found foreign server in config file: ".join(", ", @foreign_server_list), 5);
3417 # Perform a DNS lookup for server registration if flag is true
3418 if ($dns_lookup eq "true") {
3419     # Add foreign server from dns
3420     my @tmp_servers;
3421     if (not $server_domain) {
3422         # Try our DNS Searchlist
3423         for my $domain(get_dns_domains()) {
3424             chomp($domain);
3425             my ($tmp_domains, $error_string) = &get_server_addresses($domain);
3426             if(@$tmp_domains) {
3427                 for my $tmp_server(@$tmp_domains) {
3428                     push @tmp_servers, $tmp_server;
3429                 }
3430             }
3431         }
3432         if(@tmp_servers && length(@tmp_servers)==0) {
3433             daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3434         }
3435     } else {
3436         @tmp_servers = &get_server_addresses($server_domain);
3437         if( 0 == @tmp_servers ) {
3438             daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3439         }
3440     }
3442     daemon_log("0 INFO: found foreign server via DNS ".join(", ", @tmp_servers), 5);    
3444     foreach my $server (@tmp_servers) { 
3445         unshift(@foreign_server_list, $server); 
3446     }
3447 } else {
3448     daemon_log("0 INFO: DNS lookup for server registration is disabled", 5);
3452 # eliminate duplicate entries
3453 @foreign_server_list = &del_doubles(@foreign_server_list);
3454 my $all_foreign_server = join(", ", @foreign_server_list);
3455 daemon_log("0 INFO: found foreign server in config file and DNS: '$all_foreign_server'", 5);
3457 # add all found foreign servers to known_server
3458 my $cur_timestamp = &get_time();
3459 foreach my $foreign_server (@foreign_server_list) {
3461         # do not add myself to known_server_db
3462         if (&is_local($foreign_server)) { next; }
3463         ######################################
3465     my $res = $known_server_db->add_dbentry( {table=>$known_server_tn, 
3466             primkey=>['hostname'],
3467             hostname=>$foreign_server,
3468             macaddress=>"",
3469             status=>'not_yet_registered',
3470             hostkey=>"none",
3471             loaded_modules => "none", 
3472             timestamp=>$cur_timestamp,
3473             } );
3477 # Import all modules
3478 &import_modules;
3480 # Check wether all modules are gosa-si valid passwd check
3481 &password_check;
3483 # Prepare for using Opsi 
3484 if ($opsi_enabled eq "true") {
3485     use JSON::RPC::Client;
3486     use XML::Quote qw(:all);
3487     $opsi_url= "https://".$opsi_admin.":".$opsi_password."@".$opsi_server.":4447/rpc";
3488     $opsi_client = new JSON::RPC::Client;
3492 POE::Component::Server::TCP->new(
3493         Alias => "TCP_SERVER",
3494         Port => $server_port,
3495         ClientInput => sub {
3496                 my ($kernel, $input, $heap, $session) = @_[KERNEL, ARG0, HEAP, SESSION];
3497         my $session_id = $session->ID;
3498         my $remote_ip = $heap->{'remote_ip'};
3499                 push(@msgs_to_decrypt, $input);
3500         &daemon_log("$session_id DEBUG: incoming message from '$remote_ip'", 7);
3501                 $kernel->yield("msg_to_decrypt");
3502         },
3503         InlineStates => {
3504                 msg_to_decrypt => \&msg_to_decrypt,
3505                 next_task => \&next_task,
3506                 task_result => \&handle_task_result,
3507                 task_done   => \&handle_task_done,
3508                 task_debug  => \&handle_task_debug,
3509                 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3510         }
3511 );
3513 daemon_log("0 INFO: start socket for incoming xml messages at port '$server_port' ", 1);
3515 # create session for repeatedly checking the job queue for jobs
3516 POE::Session->create(
3517         inline_states => {
3518                 _start => \&session_start,
3519         register_at_foreign_servers => \&register_at_foreign_servers,
3520         sig_handler => \&sig_handler,
3521         next_task => \&next_task,
3522         task_result => \&handle_task_result,
3523         task_done   => \&handle_task_done,
3524         task_debug  => \&handle_task_debug,
3525         watch_for_next_tasks => \&watch_for_next_tasks,
3526         watch_for_new_messages => \&watch_for_new_messages,
3527         watch_for_delivery_messages => \&watch_for_delivery_messages,
3528         watch_for_done_messages => \&watch_for_done_messages,
3529                 watch_for_new_jobs => \&watch_for_new_jobs,
3530         watch_for_modified_jobs => \&watch_for_modified_jobs,
3531         watch_for_done_jobs => \&watch_for_done_jobs,
3532         watch_for_opsi_jobs => \&watch_for_opsi_jobs,
3533         watch_for_old_known_clients => \&watch_for_old_known_clients,
3534         create_packages_list_db => \&run_create_packages_list_db,
3535         create_fai_server_db => \&run_create_fai_server_db,
3536         create_fai_release_db => \&run_create_fai_release_db,
3537                 recreate_packages_db => \&run_recreate_packages_db,
3538         session_run_result => \&session_run_result,
3539         session_run_debug => \&session_run_debug,
3540         session_run_done => \&session_run_done,
3541         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3542         }
3543 );
3546 POE::Kernel->run();
3547 exit;