Code

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