Code

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