Code

17483bb6973b6a7a773404e37d7529eceb31b94f
[gosa.git] / gosa-si / gosa-si-server
1 #!/usr/bin/perl
2 #===============================================================================
3 #
4 #         FILE:  gosa-sd
5 #
6 #        USAGE:  ./gosa-sd
7 #
8 #  DESCRIPTION:
9 #
10 #      OPTIONS:  ---
11 # REQUIREMENTS:  libconfig-inifiles-perl libcrypt-rijndael-perl libxml-simple-perl 
12 #                libdata-dumper-simple-perl libdbd-sqlite3-perl libnet-ldap-perl
13 #                libpoe-perl
14 #         BUGS:  ---
15 #        NOTES:
16 #       AUTHOR:   (Andreas Rettenberger), <rettenberger@gonicus.de>
17 #      COMPANY:
18 #      VERSION:  1.0
19 #      CREATED:  12.09.2007 08:54:41 CEST
20 #     REVISION:  ---
21 #===============================================================================
23 my $server_version = '$HeadURL: https://oss.gonicus.de/repositories/gosa/trunk/gosa-si/gosa-si-server $:$Rev$';
25 use strict;
26 use warnings;
27 use Getopt::Long;
28 use Config::IniFiles;
29 use POSIX;
31 use Fcntl qw/:flock/;
32 use IO::Socket::INET;
33 use IO::Handle;
34 use IO::Select;
35 use Symbol qw(qualify_to_ref);
36 use Crypt::Rijndael;
37 use MIME::Base64;
38 use Digest::MD5  qw(md5 md5_hex md5_base64);
39 use XML::Simple;
40 use Data::Dumper;
41 use Sys::Syslog qw( :DEFAULT setlogsock);
42 use Time::HiRes qw( usleep);
43 use Cwd;
44 use File::Spec;
45 use File::Basename;
46 use File::Find;
47 use File::Copy;
48 use File::Path;
49 use GOSA::GosaSupportDaemon;
50 use POE qw(Component::Server::TCP Wheel::Run Filter::Reference);
51 use Net::LDAP;
52 use Net::LDAP::Util qw(:escape);
53 use ResourcePool;
54 use ResourcePool::Factory::Net::LDAP;
56 # revision number of server and program name
57 my $server_headURL;
58 my $server_revision;
59 my $server_status;
60 our $prg= basename($0);
62 my $db_module = "DBsqlite";
63 {
64 no strict "refs";
65 require ("GOSA/".$db_module.".pm");
66 ("GOSA/".$db_module)->import;
67 daemon_log("0 INFO: importing database module '$db_module'", 1);
68 }
70 my $modules_path = "/usr/lib/gosa-si/modules";
71 use lib "/usr/lib/gosa-si/modules";
73 our $global_kernel;
74 my ($foreground, $ping_timeout);
75 my ($server);
76 my ($gosa_server, $job_queue_timeout, $job_queue_loop_delay);
77 my ($messaging_db_loop_delay);
78 my ($procid, $pid);
79 my ($arp_fifo, $ldap_pool, $ldap_factory);
80 my ($xml);
81 my $sources_list;
82 my $max_clients;
83 my %repo_files=();
84 my $repo_path;
85 my %repo_dirs=();
87 # Variables declared in config file are always set to 'our'
88 our (%cfg_defaults, $log_file, $pid_file, 
89     $server_ip, $server_port, $ClientPackages_key, $dns_lookup,
90     $arp_activ, $gosa_unit_tag,
91     $GosaPackages_key, $gosa_timeout,
92     $foreign_server_string, $server_domain, $ServerPackages_key, $foreign_servers_register_delay,
93     $wake_on_lan_passwd, $job_synchronization, $modified_jobs_loop_delay,
94     $arp_enabled, $arp_interface,
95     $opsi_enabled, $opsi_server, $opsi_admin, $opsi_password,
96                 $new_systems_ou,
97 );
99 # additional variable which should be globaly accessable
100 our $server_address;
101 our $server_mac_address;
102 our $gosa_address;
103 our $no_arp;
104 our $verbose;
105 our $forground;
106 our $cfg_file;
107 our ($ldap_uri, $ldap_base, $ldap_admin_dn, $ldap_admin_password, $ldap_server_dn, $ldap_version);
108 our ($mysql_username, $mysql_password, $mysql_database, $mysql_host);
109 our $known_modules;
110 our $root_uid;
111 our $adm_gid;
114 # specifies the verbosity of the daemon_log
115 $verbose = 0 ;
117 # if foreground is not null, script will be not forked to background
118 $foreground = 0 ;
120 # specifies the timeout seconds while checking the online status of a registrating client
121 $ping_timeout = 5;
123 $no_arp = 0;
124 my $packages_list_under_construction = "/tmp/packages_list_creation_in_progress";
125 my @packages_list_statements;
126 my $watch_for_new_jobs_in_progress = 0;
128 # holds all incoming decrypted messages
129 our $incoming_db;
130 our $incoming_tn = 'incoming';
131 my $incoming_file_name;
132 my @incoming_col_names = ("id INTEGER PRIMARY KEY",
133         "timestamp VARCHAR(14) DEFAULT 'none'", 
134         "headertag VARCHAR(255) DEFAULT 'none'",
135         "targettag VARCHAR(255) DEFAULT 'none'",
136         "xmlmessage TEXT",
137         "module VARCHAR(255) DEFAULT 'none'",
138         "sessionid VARCHAR(255) DEFAULT '0'",
139 );
141 # holds all gosa jobs
142 our $job_db;
143 our $job_queue_tn = 'jobs';
144 my $job_queue_file_name;
145 my @job_queue_col_names = ("id INTEGER PRIMARY KEY",
146         "timestamp VARCHAR(14) DEFAULT 'none'", 
147         "status VARCHAR(255) DEFAULT 'none'", 
148         "result TEXT",
149         "progress VARCHAR(255) DEFAULT 'none'",
150         "headertag VARCHAR(255) DEFAULT 'none'",
151         "targettag VARCHAR(255) DEFAULT 'none'", 
152         "xmlmessage TEXT", 
153         "macaddress VARCHAR(17) DEFAULT 'none'",
154         "plainname VARCHAR(255) DEFAULT 'none'",
155         "siserver VARCHAR(255) DEFAULT 'none'",
156         "modified INTEGER DEFAULT '0'",
157 );
159 # holds all other gosa-si-server
160 our $known_server_db;
161 our $known_server_tn = "known_server";
162 my $known_server_file_name;
163 my @known_server_col_names = ("hostname VARCHAR(255)", "macaddress VARCHAR(17)", "status VARCHAR(255)", "hostkey VARCHAR(255)", "loaded_modules TEXT", "timestamp VARCHAR(14)");
165 # holds all registrated clients
166 our $known_clients_db;
167 our $known_clients_tn = "known_clients";
168 my $known_clients_file_name;
169 my @known_clients_col_names = ("hostname VARCHAR(255)", "status VARCHAR(255)", "hostkey VARCHAR(255)", "timestamp VARCHAR(14)", "macaddress VARCHAR(17)", "events TEXT", "keylifetime VARCHAR(255)");
171 # holds all registered clients at a foreign server
172 our $foreign_clients_db;
173 our $foreign_clients_tn = "foreign_clients"; 
174 my $foreign_clients_file_name;
175 my @foreign_clients_col_names = ("hostname VARCHAR(255)", "macaddress VARCHAR(17)", "regserver VARCHAR(255)", "timestamp VARCHAR(14)");
177 # holds all logged in user at each client 
178 our $login_users_db;
179 our $login_users_tn = "login_users";
180 my $login_users_file_name;
181 my @login_users_col_names = ("client VARCHAR(255)", "user VARCHAR(255)", "timestamp VARCHAR(14)", "regserver VARCHAR(255) DEFAULT 'localhost'");
183 # holds all fai server, the debian release and tag
184 our $fai_server_db;
185 our $fai_server_tn = "fai_server"; 
186 my $fai_server_file_name;
187 our @fai_server_col_names = ("timestamp VARCHAR(14)", "server VARCHAR(255)", "fai_release VARCHAR(255)", "sections VARCHAR(255)", "tag VARCHAR(255)"); 
189 our $fai_release_db;
190 our $fai_release_tn = "fai_release"; 
191 my $fai_release_file_name;
192 our @fai_release_col_names = ("timestamp VARCHAR(14)", "fai_release VARCHAR(255)", "class VARCHAR(255)", "type VARCHAR(255)", "state VARCHAR(255)"); 
194 # holds all packages available from different repositories
195 our $packages_list_db;
196 our $packages_list_tn = "packages_list";
197 my $packages_list_file_name;
198 our @packages_list_col_names = ("distribution VARCHAR(255)", "package VARCHAR(255)", "version VARCHAR(255)", "section VARCHAR(255)", "description TEXT", "template LONGBLOB", "timestamp VARCHAR(14)");
199 my $outdir = "/tmp/packages_list_db";
200 my $arch = "i386"; 
202 # holds all messages which should be delivered to a user
203 our $messaging_db;
204 our $messaging_tn = "messaging"; 
205 our @messaging_col_names = ("id INTEGER", "subject TEXT", "message_from VARCHAR(255)", "message_to VARCHAR(255)", 
206         "flag VARCHAR(255)", "direction VARCHAR(255)", "delivery_time VARCHAR(255)", "message TEXT", "timestamp VARCHAR(14)" );
207 my $messaging_file_name;
209 # path to directory to store client install log files
210 our $client_fai_log_dir = "/var/log/fai"; 
212 # queue which stores taskes until one of the $max_children children are ready to process the task
213 #my @tasks = qw();
214 my @msgs_to_decrypt = qw();
215 my $max_children = 2;
218 # loop delay for job queue to look for opsi jobs
219 my $job_queue_opsi_delay = 10;
220 our $opsi_client;
221 our $opsi_url;
222  
223 # Lifetime of logged in user information. If no update information comes after n seconds, 
224 # the user is expeceted to be no longer logged in or the host is no longer running. Because
225 # of this, the user is deleted from login_users_db
226 our $logged_in_user_date_of_expiry = 600;
229 %cfg_defaults = (
230 "general" => {
231     "log-file" => [\$log_file, "/var/run/".$prg.".log"],
232     "pid-file" => [\$pid_file, "/var/run/".$prg.".pid"],
233     },
234 "server" => {
235     "ip"                    => [\$server_ip, "0.0.0.0"],
236     "port"                  => [\$server_port, "20081"],
237     "known-clients"         => [\$known_clients_file_name, '/var/lib/gosa-si/clients.db' ],
238     "known-servers"         => [\$known_server_file_name, '/var/lib/gosa-si/servers.db'],
239     "incoming"              => [\$incoming_file_name, '/var/lib/gosa-si/incoming.db'],
240     "login-users"           => [\$login_users_file_name, '/var/lib/gosa-si/users.db'],
241     "fai-server"            => [\$fai_server_file_name, '/var/lib/gosa-si/fai_server.db'],
242     "fai-release"           => [\$fai_release_file_name, '/var/lib/gosa-si/fai_release.db'],
243     "packages-list"         => [\$packages_list_file_name, '/var/lib/gosa-si/packages.db'],
244     "messaging"             => [\$messaging_file_name, '/var/lib/gosa-si/messaging.db'],
245     "foreign-clients"       => [\$foreign_clients_file_name, '/var/lib/gosa-si/foreign_clients.db'],
246     "source-list"           => [\$sources_list, '/etc/apt/sources.list'],
247     "repo-path"             => [\$repo_path, '/srv/www/repository'],
248     "ldap-uri"              => [\$ldap_uri, ""],
249     "ldap-base"             => [\$ldap_base, ""],
250     "ldap-admin-dn"         => [\$ldap_admin_dn, ""],
251     "ldap-admin-password"   => [\$ldap_admin_password, ""],
252         "ldap-version"                  => [\$ldap_version, 3],
253     "gosa-unit-tag"         => [\$gosa_unit_tag, ""],
254     "max-clients"           => [\$max_clients, 10],
255     "wol-password"          => [\$wake_on_lan_passwd, ""],
256         "mysql-username"        => [\$mysql_username, "gosa_si"],
257         "mysql-password"        => [\$mysql_password, ""],
258         "mysql-database"        => [\$mysql_database, "gosa_si"],
259         "mysql-host"            => [\$mysql_host, "127.0.0.1"],
260     },
261 "GOsaPackages" => {
262     "job-queue" => [\$job_queue_file_name, '/var/lib/gosa-si/jobs.db'],
263     "job-queue-loop-delay" => [\$job_queue_loop_delay, 3],
264     "messaging-db-loop-delay" => [\$messaging_db_loop_delay, 3],
265     "key" => [\$GosaPackages_key, "none"],
266                 "new-systems-ou" => [\$new_systems_ou, 'ou=workstations,ou=systems'],
267     },
268 "ClientPackages" => {
269     "key" => [\$ClientPackages_key, "none"],
270     "user-date-of-expiry" => [\$logged_in_user_date_of_expiry, 600],
271     },
272 "ServerPackages"=> {
273     "address"      => [\$foreign_server_string, ""],
274     "dns-lookup"            => [\$dns_lookup, "true"],
275     "domain"  => [\$server_domain, ""],
276     "key"     => [\$ServerPackages_key, "none"],
277     "key-lifetime" => [\$foreign_servers_register_delay, 120],
278     "job-synchronization-enabled" => [\$job_synchronization, "true"],
279     "synchronization-loop" => [\$modified_jobs_loop_delay, 5],
280     },
281 "ArpHandler" => {
282     "enabled"   => [\$arp_enabled, "true"],
283     "interface" => [\$arp_interface, "all"],
284         },
285 "Opsi" => {
286     "enabled"  => [\$opsi_enabled, "false"], 
287     "server"   => [\$opsi_server, "localhost"],
288     "admin"    => [\$opsi_admin, "opsi-admin"],
289     "password" => [\$opsi_password, "secret"],
290    },
292 );
295 #===  FUNCTION  ================================================================
296 #         NAME:  usage
297 #   PARAMETERS:  nothing
298 #      RETURNS:  nothing
299 #  DESCRIPTION:  print out usage text to STDERR
300 #===============================================================================
301 sub usage {
302     print STDERR << "EOF" ;
303 usage: $prg [-hvf] [-c config]
305            -h        : this (help) message
306            -c <file> : config file
307            -f        : foreground, process will not be forked to background
308            -v        : be verbose (multiple to increase verbosity)
309            -no-arp   : starts $prg without connection to arp module
310  
311 EOF
312     print "\n" ;
316 #===  FUNCTION  ================================================================
317 #         NAME:  logging
318 #   PARAMETERS:  level - string - default 'info'
319 #                msg - string -
320 #                facility - string - default 'LOG_DAEMON'
321 #      RETURNS:  nothing
322 #  DESCRIPTION:  function for logging
323 #===============================================================================
324 sub daemon_log {
325     # log into log_file
326     my( $msg, $level ) = @_;
327     if(not defined $msg) { return }
328     if(not defined $level) { $level = 1 }
329     if(defined $log_file){
330         my $open_log_fh = sysopen(LOG_HANDLE, $log_file, O_WRONLY | O_CREAT | O_APPEND , 0440);
331         if(not $open_log_fh) {
332             print STDERR "cannot open $log_file: $!";
333             return;
334         }
335         # check owner and group of log_file and update settings if necessary
336         my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = stat($log_file);
337         if((not $uid eq $root_uid) || (not $gid eq $adm_gid)) {
338             chown($root_uid, $adm_gid, $log_file);
339         }
341         chomp($msg);
342         #$msg =~s/\n//g;   # no newlines are allowed in log messages, this is important for later log parsing
343         if($level <= $verbose){
344             my ($seconds, $minutes, $hours, $monthday, $month,
345                     $year, $weekday, $yearday, $sommertime) = localtime(time);
346             $hours = $hours < 10 ? $hours = "0".$hours : $hours;
347             $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
348             $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
349             my @monthnames = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
350             $month = $monthnames[$month];
351             $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
352             $year+=1900;
353             my $name = $prg;
355             my $log_msg = "$month $monthday $hours:$minutes:$seconds $name $msg\n";
356                         flock(LOG_HANDLE, LOCK_EX);
357                         seek(LOG_HANDLE, 0, 2);
358             print LOG_HANDLE $log_msg;
359                         flock(LOG_HANDLE, LOCK_UN);
360             if( $foreground ) { 
361                 print STDERR $log_msg;
362             }
363         }
364         close( LOG_HANDLE );
365     }
369 #===  FUNCTION  ================================================================
370 #         NAME:  check_cmdline_param
371 #   PARAMETERS:  nothing
372 #      RETURNS:  nothing
373 #  DESCRIPTION:  validates commandline parameter
374 #===============================================================================
375 sub check_cmdline_param () {
376     my $err_config;
377     my $err_counter = 0;
378         if(not defined($cfg_file)) {
379                 $cfg_file = "/etc/gosa-si/server.conf";
380                 if(! -r $cfg_file) {
381                         $err_config = "please specify a config file";
382                         $err_counter += 1;
383                 }
384     }
385     if( $err_counter > 0 ) {
386         &usage( "", 1 );
387         if( defined( $err_config)) { print STDERR "$err_config\n"}
388         print STDERR "\n";
389         exit( -1 );
390     }
394 #===  FUNCTION  ================================================================
395 #         NAME:  check_pid
396 #   PARAMETERS:  nothing
397 #      RETURNS:  nothing
398 #  DESCRIPTION:  handels pid processing
399 #===============================================================================
400 sub check_pid {
401     $pid = -1;
402     # Check, if we are already running
403     if( open(LOCK_FILE, "<$pid_file") ) {
404         $pid = <LOCK_FILE>;
405         if( defined $pid ) {
406             chomp( $pid );
407             if( -f "/proc/$pid/stat" ) {
408                 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
409                 if( $stat ) {
410                                         daemon_log("ERROR: Already running",1);
411                     close( LOCK_FILE );
412                     exit -1;
413                 }
414             }
415         }
416         close( LOCK_FILE );
417         unlink( $pid_file );
418     }
420     # create a syslog msg if it is not to possible to open PID file
421     if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
422         my($msg) = "Couldn't obtain lockfile '$pid_file' ";
423         if (open(LOCK_FILE, '<', $pid_file)
424                 && ($pid = <LOCK_FILE>))
425         {
426             chomp($pid);
427             $msg .= "(PID $pid)\n";
428         } else {
429             $msg .= "(unable to read PID)\n";
430         }
431         if( ! ($foreground) ) {
432             openlog( $0, "cons,pid", "daemon" );
433             syslog( "warning", $msg );
434             closelog();
435         }
436         else {
437             print( STDERR " $msg " );
438         }
439         exit( -1 );
440     }
443 #===  FUNCTION  ================================================================
444 #         NAME:  import_modules
445 #   PARAMETERS:  module_path - string - abs. path to the directory the modules 
446 #                are stored
447 #      RETURNS:  nothing
448 #  DESCRIPTION:  each file in module_path which ends with '.pm' and activation 
449 #                state is on is imported by "require 'file';"
450 #===============================================================================
451 sub import_modules {
452     daemon_log(" ", 1);
454     if (not -e $modules_path) {
455         daemon_log("0 ERROR: cannot find directory or directory is not readable: $modules_path", 1);   
456     }
458     opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
460         my $ldap_handle = &get_ldap_handle;
461     while (defined (my $file = readdir (DIR))) {
462         if (not $file =~ /(\S*?).pm$/) {
463             next;
464         }
465                 my $mod_name = $1;
467         # ArpHandler switch
468         if( $file =~ /ArpHandler.pm/ ) {
469             if( $arp_enabled eq "false" ) { next; }
470         }
471         
472         eval { require $file; };
473         if ($@) {
474             daemon_log("0 ERROR: gosa-si-server could not load module $file", 1);
475             daemon_log("$@", 1);
476             exit;
477                 } else {
478                         my $info = eval($mod_name.'::get_module_info($ldap_handle)');
479                         # Only load module if get_module_info() returns a non-null object
480                         if( $info ) {
481                                 my ($input_address, $input_key, $event_hash) = @{$info};
482                                 $known_modules->{$mod_name} = $info;
483                                 daemon_log("0 INFO: module $mod_name loaded", 5);
484                         }
485                 }
486     }   
487         &release_ldap_handle($ldap_handle); 
488     close (DIR);
491 #===  FUNCTION  ================================================================
492 #         NAME:  password_check
493 #   PARAMETERS:  nothing
494 #      RETURNS:  nothing
495 #  DESCRIPTION:  escalates an critical error if two modules exist which are avaialable by 
496 #                the same password
497 #===============================================================================
498 sub password_check {
499     my $passwd_hash = {};
500     while (my ($mod_name, $mod_info) = each %$known_modules) {
501         my $mod_passwd = @$mod_info[1];
502         if (not defined $mod_passwd) { next; }
503         if (not exists $passwd_hash->{$mod_passwd}) {
504             $passwd_hash->{$mod_passwd} = $mod_name;
506         # escalates critical error
507         } else {
508             &daemon_log("0 ERROR: two loaded modules do have the same password. Please modify the 'key'-parameter in config file");
509             &daemon_log("0 ERROR: module='$mod_name' and module='".$passwd_hash->{$mod_passwd}."'");
510             exit( -1 );
511         }
512     }
517 #===  FUNCTION  ================================================================
518 #         NAME:  sig_int_handler
519 #   PARAMETERS:  signal - string - signal arose from system
520 #      RETURNS:  nothing
521 #  DESCRIPTION:  handels tasks to be done befor signal becomes active
522 #===============================================================================
523 sub sig_int_handler {
524     my ($signal) = @_;
526 #       if (defined($ldap_handle)) {
527 #               $ldap_handle->disconnect;
528 #       }
529     # TODO alle verbliebenden ldap verbindungen aus allen heaps beenden
530     
532     daemon_log("shutting down gosa-si-server", 1);
533     system("kill `ps -C gosa-si-server -o pid=`");
535 $SIG{INT} = \&sig_int_handler;
538 sub check_key_and_xml_validity {
539     my ($crypted_msg, $module_key, $session_id) = @_;
540     my $msg;
541     my $msg_hash;
542     my $error_string;
543     eval{
544         $msg = &decrypt_msg($crypted_msg, $module_key);
546         if ($msg =~ /<xml>/i){
547             $msg =~ s/\s+/ /g;  # just for better daemon_log
548             daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 9);
549             $msg_hash = $xml->XMLin($msg, ForceArray=>1);
551             ##############
552             # check header
553             if( not exists $msg_hash->{'header'} ) { die "no header specified"; }
554             my $header_l = $msg_hash->{'header'};
555             if( (1 > @{$header_l}) || ( ( 'HASH' eq ref @{$header_l}[0]) && (1 > keys %{@{$header_l}[0]}) ) ) { die 'empty header tag'; }
556             if( 1 < @{$header_l} ) { die 'more than one header specified'; }
557             my $header = @{$header_l}[0];
558             if( 0 == length $header) { die 'empty string in header tag'; }
560             ##############
561             # check source
562             if( not exists $msg_hash->{'source'} ) { die "no source specified"; }
563             my $source_l = $msg_hash->{'source'};
564             if( (1 > @{$source_l}) || ( ( 'HASH' eq ref @{$source_l}[0]) && (1 > keys %{@{$source_l}[0]}) ) ) { die 'empty source tag'; }
565             if( 1 < @{$source_l} ) { die 'more than one source specified'; }
566             my $source = @{$source_l}[0];
567             if( 0 == length $source) { die 'source error'; }
569             ##############
570             # check target
571             if( not exists $msg_hash->{'target'} ) { die "no target specified"; }
572             my $target_l = $msg_hash->{'target'};
573             if( (1 > @{$target_l}) || ( ('HASH' eq ref @{$target_l}[0]) && (1 > keys %{@{$target_l}[0]}) ) ) { die 'empty target tag'; }
574         }
575     };
576     if($@) {
577         daemon_log("$session_id ERROR: do not understand the message: $@", 1);
578         $msg = undef;
579         $msg_hash = undef;
580     }
582     return ($msg, $msg_hash);
586 sub check_outgoing_xml_validity {
587     my ($msg, $session_id) = @_;
589     my $msg_hash;
590     eval{
591         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
593         ##############
594         # check header
595         my $header_l = $msg_hash->{'header'};
596         if( 1 != @{$header_l} ) {
597             die 'no or more than one headers specified';
598         }
599         my $header = @{$header_l}[0];
600         if( 0 == length $header) {
601             die 'header has length 0';
602         }
604         ##############
605         # check source
606         my $source_l = $msg_hash->{'source'};
607         if( 1 != @{$source_l} ) {
608             die 'no or more than 1 sources specified';
609         }
610         my $source = @{$source_l}[0];
611         if( 0 == length $source) {
612             die 'source has length 0';
613         }
615                                 # Check if source contains hostname instead of ip address
616                                 if(not $source =~ /^[a-z0-9\.]+:\d+$/i) {
617                                                 my ($hostname,$port) = split(/:/, $source);
618                                                 my $ip_address = inet_ntoa(scalar gethostbyname($hostname));
619                                                 if(defined($ip_address) && $ip_address =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/ && $port =~ /^\d+$/) {
620                                                         # Write ip address to $source variable
621                                                         $source = "$ip_address:$port";
622                                                 }
623                                 }
624         unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
625                 $source =~ /^GOSA$/i) {
626             die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
627         }
628         
629         ##############
630         # check target  
631         my $target_l = $msg_hash->{'target'};
632         if( 0 == @{$target_l} ) {
633             die "no targets specified";
634         }
635         foreach my $target (@$target_l) {
636             if( 0 == length $target) {
637                 die "target has length 0";
638             }
639             unless( $target =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
640                     $target =~ /^GOSA$/i ||
641                     $target =~ /^\*$/ ||
642                     $target =~ /KNOWN_SERVER/i ||
643                     $target =~ /JOBDB/i ||
644                     $target =~ /^([0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2})$/i ){
645                 die "target '$target' is not a complete ip-address with port or a valid target name or a mac-address";
646             }
647         }
648     };
649     if($@) {
650         daemon_log("$session_id ERROR: outgoing msg is not gosa-si envelope conform: $@", 1);
651         daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 1);
652         $msg_hash = undef;
653     }
655     return ($msg_hash);
659 sub input_from_known_server {
660     my ($input, $remote_ip, $session_id) = @_ ;  
661     my ($msg, $msg_hash, $module);
663     my $sql_statement= "SELECT * FROM known_server";
664     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
666     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
667         my $host_name = $hit->{hostname};
668         if( not $host_name =~ "^$remote_ip") {
669             next;
670         }
671         my $host_key = $hit->{hostkey};
672         daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 7);
673         daemon_log("$session_id DEBUG: input_from_known_server: host_key: $host_key", 7);
675         # check if module can open msg envelope with module key
676         my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
677         if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
678             daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 7);
679             daemon_log("$@", 8);
680             next;
681         }
682         else {
683             $msg = $tmp_msg;
684             $msg_hash = $tmp_msg_hash;
685             $module = "ServerPackages";
686             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 7);
687             last;
688         }
689     }
691     if( (!$msg) || (!$msg_hash) || (!$module) ) {
692         daemon_log("$session_id DEBUG: Incoming message is not from a known server", 7);
693     }
694   
695     return ($msg, $msg_hash, $module);
699 sub input_from_known_client {
700     my ($input, $remote_ip, $session_id) = @_ ;  
701     my ($msg, $msg_hash, $module);
703     my $sql_statement= "SELECT * FROM known_clients";
704     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
705     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
706         my $host_name = $hit->{hostname};
707         if( not $host_name =~ /^$remote_ip:\d*$/) {
708                 next;
709                 }
710         my $host_key = $hit->{hostkey};
711         &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 7);
712         &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 7);
714         # check if module can open msg envelope with module key
715         ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
717         if( (!$msg) || (!$msg_hash) ) {
718             &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 7);
719             &daemon_log("$@", 8);
720             next;
721         }
722         else {
723             $module = "ClientPackages";
724             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 7);
725             last;
726         }
727     }
729     if( (!$msg) || (!$msg_hash) || (!$module) ) {
730         &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 7);
731     }
733     return ($msg, $msg_hash, $module);
737 sub input_from_unknown_host {
738         no strict "refs";
739         my ($input, $session_id) = @_ ;
740         my ($msg, $msg_hash, $module);
741         my $error_string;
743         my %act_modules = %$known_modules;
745         while( my ($mod, $info) = each(%act_modules)) {
747                 # check a key exists for this module
748                 my $module_key = ${$mod."_key"};
749                 if( not defined $module_key ) {
750                         if( $mod eq 'ArpHandler' ) {
751                                 next;
752                         }
753                         daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
754                         next;
755                 }
756                 daemon_log("$session_id DEBUG: $mod: $module_key", 7);
758                 # check if module can open msg envelope with module key
759                 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
760                 if( (not defined $msg) || (not defined $msg_hash) ) {
761                         next;
762                 } else {
763                         $module = $mod;
764             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 7);
765                         last;
766                 }
767         }
769         if( (!$msg) || (!$msg_hash) || (!$module)) {
770                 daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 7);
771         }
773         return ($msg, $msg_hash, $module);
777 sub create_ciphering {
778     my ($passwd) = @_;
779         if((!defined($passwd)) || length($passwd)==0) {
780                 $passwd = "";
781         }
782     $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
783     my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
784     my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
785     $my_cipher->set_iv($iv);
786     return $my_cipher;
790 sub encrypt_msg {
791     my ($msg, $key) = @_;
792     my $my_cipher = &create_ciphering($key);
793     my $len;
794     {
795             use bytes;
796             $len= 16-length($msg)%16;
797     }
798     $msg = "\0"x($len).$msg;
799     $msg = $my_cipher->encrypt($msg);
800     chomp($msg = &encode_base64($msg));
801     # there are no newlines allowed inside msg
802     $msg=~ s/\n//g;
803     return $msg;
807 sub decrypt_msg {
809     my ($msg, $key) = @_ ;
810     $msg = &decode_base64($msg);
811     my $my_cipher = &create_ciphering($key);
812     $msg = $my_cipher->decrypt($msg); 
813     $msg =~ s/\0*//g;
814     return $msg;
818 sub get_encrypt_key {
819     my ($target) = @_ ;
820     my $encrypt_key;
821     my $error = 0;
823     # target can be in known_server
824     if( not defined $encrypt_key ) {
825         my $sql_statement= "SELECT * FROM known_server WHERE hostname='$target'";
826         my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
827         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
828             my $host_name = $hit->{hostname};
829             if( $host_name ne $target ) {
830                 next;
831             }
832             $encrypt_key = $hit->{hostkey};
833             last;
834         }
835     }
837     # target can be in known_client
838     if( not defined $encrypt_key ) {
839         my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$target'";
840         my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
841         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
842             my $host_name = $hit->{hostname};
843             if( $host_name ne $target ) {
844                 next;
845             }
846             $encrypt_key = $hit->{hostkey};
847             last;
848         }
849     }
851     return $encrypt_key;
855 #===  FUNCTION  ================================================================
856 #         NAME:  open_socket
857 #   PARAMETERS:  PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
858 #                [PeerPort] string necessary if port not appended by PeerAddr
859 #      RETURNS:  socket IO::Socket::INET
860 #  DESCRIPTION:  open a socket to PeerAddr
861 #===============================================================================
862 sub open_socket {
863     my ($PeerAddr, $PeerPort) = @_ ;
864     if(defined($PeerPort)){
865         $PeerAddr = $PeerAddr.":".$PeerPort;
866     }
867     my $socket;
868     $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
869             Porto => "tcp",
870             Type => SOCK_STREAM,
871             Timeout => 5,
872             );
873     if(not defined $socket) {
874         return;
875     }
876 #    &daemon_log("DEBUG: open_socket: $PeerAddr", 7);
877     return $socket;
881 sub send_msg_to_target {
882     my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
883     my $error = 0;
884     my $header;
885     my $timestamp = &get_time();
886     my $new_status;
887     my $act_status;
888     my ($sql_statement, $res);
889   
890     if( $msg_header ) {
891         $header = "'$msg_header'-";
892     } else {
893         $header = "";
894     }
896         # Patch the source ip
897         if($msg =~ /<source>0\.0\.0\.0:\d*?<\/source>/) {
898                 my $remote_ip = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
899                 $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$remote_ip:$2<\/source>/s;
900         }
902     # encrypt xml msg
903     my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
905     # opensocket
906     my $socket = &open_socket($address);
907     if( !$socket ) {
908         daemon_log("$session_id WARNING: cannot send ".$header."msg to $address , host not reachable", 3);
909         $error++;
910     }
911     
912     if( $error == 0 ) {
913         # send xml msg
914         print $socket $crypted_msg."\n";
916         daemon_log("$session_id INFO: send ".$header."msg to $address", 5);
917         daemon_log("$session_id DEBUG: message:\n$msg", 9);
918         
919     }
921     # close socket in any case
922     if( $socket ) {
923         close $socket;
924     }
926     if( $error > 0 ) { $new_status = "down"; }
927     else { $new_status = $msg_header; }
930     # known_clients
931     $sql_statement = "SELECT * FROM $known_clients_tn WHERE hostname='$address'";
932     $res = $known_clients_db->select_dbentry($sql_statement);
933     if( keys(%$res) == 1) {
934         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
935         if ($act_status eq "down" && $new_status eq "down") {
936             $sql_statement = "DELETE FROM known_clients WHERE hostname='$address'";
937             $res = $known_clients_db->del_dbentry($sql_statement);
938             daemon_log("$session_id WARNING: failed 2x to send msg to host '$address', delete host from known_clients", 3);
939         } else { 
940             $sql_statement = "UPDATE known_clients SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
941             $res = $known_clients_db->update_dbentry($sql_statement);
942             if($new_status eq "down"){
943                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
944             } else {
945                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
946             }
947         }
948     }
950     # known_server
951     $sql_statement = "SELECT * FROM $known_server_tn WHERE hostname='$address'";
952     $res = $known_server_db->select_dbentry($sql_statement);
953     if( keys(%$res) == 1) {
954         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
955         if ($act_status eq "down" && $new_status eq "down") {
956             $sql_statement = "DELETE FROM known_server WHERE hostname='$address'";
957             $res = $known_server_db->del_dbentry($sql_statement);
958             daemon_log("$session_id WARNING: failed 2x to send a message to host '$address', delete host from known_server", 3);
959         } 
960         else { 
961             $sql_statement = "UPDATE known_server SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
962             $res = $known_server_db->update_dbentry($sql_statement);
963             if($new_status eq "down"){
964                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
965             } else {
966                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
967             }
968         }
969     }
970     return $error; 
974 sub update_jobdb_status_for_send_msgs {
975     my ($session_id, $answer, $error) = @_;
976     &daemon_log("$session_id DEBUG: try to update job status", 7); 
977     if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
978         my $jobdb_id = $1;
979     
980         $answer =~ /<header>(.*)<\/header>/;
981         my $job_header = $1;
983         $answer =~ /<target>(.*)<\/target>/;
984         my $job_target = $1;
985             
986         # Sending msg failed
987         if( $error ) {
989             # Set jobs to done, jobs do not need to deliver their message in any case
990             if (($job_header eq "trigger_action_localboot")
991                     ||($job_header eq "trigger_action_lock")
992                     ||($job_header eq "trigger_action_halt") 
993                     ) {
994                 my $sql_statement = "UPDATE $job_queue_tn SET status='done' WHERE id=$jobdb_id";
995                 &daemon_log("$session_id DEBUG: $sql_statement", 7); 
996                 my $res = $job_db->update_dbentry($sql_statement);
997                 
998             # Reactivate jobs, jobs need to deliver their message
999             } elsif (($job_header eq "trigger_action_activate")
1000                     ||($job_header eq "trigger_action_update")
1001                     ||($job_header eq "trigger_action_reinstall") 
1002                     ||($job_header eq "trigger_activate_new")
1003                     ) {
1004                 &reactivate_job_with_delay($session_id, $job_target, $job_header, 30 );
1006             # For all other messages
1007             } else {
1008                 my $sql_statement = "UPDATE $job_queue_tn ".
1009                     "SET status='error', result='can not deliver msg, please consult log file' ".
1010                     "WHERE id=$jobdb_id";
1011                 &daemon_log("$session_id DEBUG: $sql_statement", 7); 
1012                 my $res = $job_db->update_dbentry($sql_statement);
1013             }
1015         # Sending msg was successful
1016         } else {
1017             # Set jobs localboot, lock, activate, halt, reboot and wake to done
1018             # jobs reinstall, update, inst_update do themself setting to done
1019             if (($job_header eq "trigger_action_localboot")
1020                     ||($job_header eq "trigger_action_lock")
1021                     ||($job_header eq "trigger_action_activate")
1022                     ||($job_header eq "trigger_action_halt") 
1023                     ||($job_header eq "trigger_action_reboot")
1024                     ||($job_header eq "trigger_action_wake")
1025                     ||($job_header eq "trigger_wake")
1026                     ) {
1028                 my $sql_statement = "UPDATE $job_queue_tn ".
1029                     "SET status='done' ".
1030                     "WHERE id=$jobdb_id AND status='processed'";
1031                 &daemon_log("$session_id DEBUG: $sql_statement", 7); 
1032                 my $res = $job_db->update_dbentry($sql_statement);
1033             } else { 
1034                 &daemon_log("$session_id DEBUG: sending message succeed but cannot update job status.", 7); 
1035             } 
1036         } 
1037     } else { 
1038         &daemon_log("$session_id DEBUG: cannot update job status, msg has no jobdb_id-tag: $answer", 7); 
1039     }
1042 sub reactivate_job_with_delay {
1043     my ($session_id, $target, $header, $delay) = @_ ;
1044     # Sometimes the client is still booting or does not wake up, in this case reactivate the job (if it exists) with a delay of n sec
1045     
1046     if (not defined $delay) { $delay = 30 } ;
1047     my $delay_timestamp = &calc_timestamp(&get_time(), "plus", $delay);
1049     my $sql = "UPDATE $job_queue_tn Set timestamp='$delay_timestamp', status='waiting' WHERE (macaddress='$target' AND headertag='$header')"; 
1050     my $res = $job_db->update_dbentry($sql);
1051     daemon_log("$session_id INFO: '$header'-job will be reactivated at '$delay_timestamp' ".
1052             "cause client '$target' is currently not available", 5);
1053     daemon_log("$session_id $sql", 7);                             
1054     return;
1058 sub sig_handler {
1059         my ($kernel, $signal) = @_[KERNEL, ARG0] ;
1060         daemon_log("0 INFO got signal '$signal'", 1); 
1061         $kernel->sig_handled();
1062         return;
1066 sub msg_to_decrypt {
1067         my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
1068         my $session_id = $session->ID;
1069         my ($msg, $msg_hash, $module);
1070         my $error = 0;
1072         # fetch new msg out of @msgs_to_decrypt
1073         my $tmp_next_msg = shift @msgs_to_decrypt;
1074     my ($next_msg, $msg_source) = split(/;/, $tmp_next_msg);
1076         # msg is from a new client or gosa
1077         ($msg, $msg_hash, $module) = &input_from_unknown_host($next_msg, $session_id);
1079         # msg is from a gosa-si-server
1080         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1081                 ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $heap->{'remote_ip'}, $session_id);
1082         }
1083         # msg is from a gosa-si-client
1084         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1085                 ($msg, $msg_hash, $module) = &input_from_known_client($next_msg, $heap->{'remote_ip'}, $session_id);
1086         }
1087         # an error occurred
1088         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1089                 # if an incoming msg could not be decrypted (maybe a wrong key), send client a ping. If the client
1090                 # could not understand a msg from its server the client cause a re-registering process
1091         my $remote_ip = $heap->{'remote_ip'};
1092         my $remote_port = $heap->{'remote_port'};
1093         my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source><target>$msg_source</target></xml>";
1094         my ($test_error, $test_error_string) = &send_msg_to_target($ping_msg, "$msg_source", "dummy-key", "gosa_ping", $session_id);
1096                 daemon_log("$session_id WARNING cannot understand incoming msg, send 'ping'-msg to all host with ip '".$heap->{remote_ip}.
1097                         "' to cause a re-registering of the client if necessary", 3);
1098                 $error++;
1099         }
1102         my $header;
1103         my $target;
1104         my $source;
1105         my $done = 0;
1106         my $sql;
1107         my $res;
1109         # check whether this message should be processed here
1110         if ($error == 0) {
1111                 $header = @{$msg_hash->{'header'}}[0];
1112                 $target = @{$msg_hash->{'target'}}[0];
1113                 $source = @{$msg_hash->{'source'}}[0];
1114                 my $not_found_in_known_clients_db = 0;
1115                 my $not_found_in_known_server_db = 0;
1116                 my $not_found_in_foreign_clients_db = 0;
1117                 my $local_address;
1118                 my $local_mac;
1119                 my ($target_ip, $target_port) = split(':', $target);
1121                 # Determine the local ip address if target is an ip address
1122                 if ($target =~ /^\d+\.\d+\.\d+\.\d+:\d+$/) {
1123                         $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1124                 } else {
1125                         $local_address = $server_address;
1126                 }
1128                 # Determine the local mac address if target is a mac address
1129                 if ($target =~ /^([0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2})$/i) {
1130                         my $loc_ip = &get_local_ip_for_remote_ip($heap->{'remote_ip'});
1131                         my $network_interface= &get_interface_for_ip($loc_ip);
1132                         $local_mac = &get_mac_for_interface($network_interface);
1133                 } else {
1134                         $local_mac = $server_mac_address;
1135                 }
1137                 # target and source is equal to GOSA -> process here
1138                 if (not $done) {
1139                         if ($target eq "GOSA" && $source eq "GOSA") {
1140                                 $done = 1;                    
1141                                 &daemon_log("$session_id DEBUG: target and source is 'GOSA' -> process here", 7);
1142                         }
1143                 }
1145                 # target is own address without forward_to_gosa-tag -> process here
1146                 if (not $done) {
1147                         #if ((($target eq $local_address) || ($target eq $local_mac) ) && (not exists $msg_hash->{'forward_to_gosa'})) {
1148                         if (($target eq $local_address) && (not exists $msg_hash->{'forward_to_gosa'})) {
1149                                 $done = 1;
1150                                 if ($source eq "GOSA") {
1151                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1152                                 }
1153                                 &daemon_log("$session_id DEBUG: target is own address without forward_to_gosa-tag -> process here", 7);
1154                         }
1155                 }
1157                 # target is a client address in known_clients -> process here
1158                 if (not $done) {
1159                         $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')"; 
1160                         $res = $known_clients_db->select_dbentry($sql);
1161                         if (keys(%$res) > 0) {
1162                                 $done = 1; 
1163                                 my $hostname = $res->{1}->{'hostname'};
1164                                 $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1165                                 my $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1166                                 if ($source eq "GOSA") {
1167                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1168                                 }
1169                                 &daemon_log("$session_id DEBUG: target is a client address in known_clients -> process here", 7);
1171                         } else {
1172                                 $not_found_in_known_clients_db = 1;
1173                         }
1174                 }
1176                 # target ist own address with forward_to_gosa-tag not pointing to myself -> process here
1177                 if (not $done) {
1178                         my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1179                         my $gosa_at;
1180                         my $gosa_session_id;
1181                         if (($target eq $local_address) && (defined $forward_to_gosa)){
1182                                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1183                                 if ($gosa_at ne $local_address) {
1184                                         $done = 1;
1185                                         &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag not pointing to myself -> process here", 7); 
1186                                 }
1187                         }
1188                 }
1190                 # if message should be processed here -> add message to incoming_db
1191                 if ($done) {
1192                         # if a job or a gosa message comes from a foreign server, fake module to GosaPackages
1193                         # so gosa-si-server knows how to process this kind of messages
1194                         if ($header =~ /^gosa_/ || $header =~ /^job_/) {
1195                                 $module = "GosaPackages";
1196                         }
1198                         my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1199                                         primkey=>[],
1200                                         headertag=>$header,
1201                                         targettag=>$target,
1202                                         xmlmessage=>&encode_base64($msg),
1203                                         timestamp=>&get_time,
1204                                         module=>$module,
1205                                         sessionid=>$session_id,
1206                                 } );
1208                 }
1210                 # target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa
1211                 if (not $done) {
1212                         my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1213                         my $gosa_at;
1214                         my $gosa_session_id;
1215                         if (($target eq $local_address) && (defined $forward_to_gosa)){
1216                                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1217                                 if ($gosa_at eq $local_address) {
1218                                         my $session_reference = $kernel->ID_id_to_session($gosa_session_id);
1219                                         if( defined $session_reference ) {
1220                                                 $heap = $session_reference->get_heap();
1221                                         }
1222                                         if(exists $heap->{'client'}) {
1223                                                 $msg = &encrypt_msg($msg, $GosaPackages_key);
1224                                                 $heap->{'client'}->put($msg);
1225                                                 &daemon_log("$session_id INFO: incoming '$header' message forwarded to GOsa", 5); 
1226                                         }
1227                                         $done = 1;
1228                                         &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa", 7);
1229                                 }
1230                         }
1232                 }
1234                 # target is a client address in foreign_clients -> forward to registration server
1235                 if (not $done) {
1236                         $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1237                         $res = $foreign_clients_db->select_dbentry($sql);
1238                         if (keys(%$res) > 0) {
1239                                 my $hostname = $res->{1}->{'hostname'};
1240                                 my ($host_ip, $host_port) = split(/:/, $hostname);
1241                                 my $local_address =  &get_local_ip_for_remote_ip($host_ip).":$server_port";
1242                                 my $regserver = $res->{1}->{'regserver'};
1243                                 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$regserver'"; 
1244                                 my $res = $known_server_db->select_dbentry($sql);
1245                                 if (keys(%$res) > 0) {
1246                                         my $regserver_key = $res->{1}->{'hostkey'};
1247                                         $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1248                                         $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1249                                         if ($source eq "GOSA") {
1250                                                 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1251                                         }
1252                                         &send_msg_to_target($msg, $regserver, $regserver_key, $header, $session_id);
1253                                 }
1254                                 $done = 1;
1255                                 &daemon_log("$session_id DEBUG: target is a client address in foreign_clients -> forward to registration server", 7);
1256                         } else {
1257                                 $not_found_in_foreign_clients_db = 1;
1258                         }
1259                 }
1261                 # target is a server address -> forward to server
1262                 if (not $done) {
1263                         $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1264                         $res = $known_server_db->select_dbentry($sql);
1265                         if (keys(%$res) > 0) {
1266                                 my $hostkey = $res->{1}->{'hostkey'};
1268                                 if ($source eq "GOSA") {
1269                                         $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1270                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1272                                 }
1274                                 &send_msg_to_target($msg, $target, $hostkey, $header, $session_id);
1275                                 $done = 1;
1276                                 &daemon_log("$session_id DEBUG: target is a server address -> forward to server", 7);
1277                         } else {
1278                                 $not_found_in_known_server_db = 1;
1279                         }
1280                 }
1283                 # target is not in foreign_clients_db, known_server_db or known_clients_db, maybe it is a complete new one -> process here
1284                 if ( $not_found_in_foreign_clients_db 
1285                         && $not_found_in_known_server_db
1286                         && $not_found_in_known_clients_db) {
1287                         &daemon_log("$session_id DEBUG: target is not in foreign_clients_db, known_server_db or known_clients_db, maybe it is a complete new one -> process here", 7);
1288             if ($header =~ /^gosa_/ || $header =~ /^job_/) { 
1289                 $module = "GosaPackages"; 
1290             }
1291                         my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1292                                         primkey=>[],
1293                                         headertag=>$header,
1294                                         targettag=>$target,
1295                                         xmlmessage=>&encode_base64($msg),
1296                                         timestamp=>&get_time,
1297                                         module=>$module,
1298                                         sessionid=>$session_id,
1299                                 } );
1300                         $done = 1;
1301                 }
1304                 if (not $done) {
1305                         daemon_log("$session_id ERROR: do not know what to do with this message: $msg", 1);
1306                         if ($source eq "GOSA") {
1307                                 my %data = ('error_msg' => &encode_base64($msg), 'error_string' => "Do not know what to do with this message!");
1308                                 my $error_msg = &build_msg("error", $local_address, "GOSA", \%data ); 
1310                                 my $session_reference = $kernel->ID_id_to_session($session_id);
1311                                 if( defined $session_reference ) {
1312                                         $heap = $session_reference->get_heap();
1313                                 }
1314                                 if(exists $heap->{'client'}) {
1315                                         $error_msg = &encrypt_msg($error_msg, $GosaPackages_key);
1316                                         $heap->{'client'}->put($error_msg);
1317                                 }
1318                         }
1319                 }
1321         }
1323         return;
1327 sub next_task {
1328     my ($session, $heap, $task) = @_[SESSION, HEAP, ARG0];
1329         my $ldap_handle = &get_ldap_handle();
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 quaeue for jobs with executable timestamp
1747                 my $timestamp = &get_time();
1748                 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE 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);
2128         $kernel->yield('next_task', $hit);
2130         my $sql = "DELETE FROM $incoming_tn WHERE id=$message_id";
2131         my $res = $incoming_db->exec_statement($sql);
2132     }
2134     $kernel->delay_set('watch_for_next_tasks', 1); 
2138 sub get_ldap_handle {
2139         my ($session_id) = @_;
2140         my $heap;
2142         if (not defined $session_id ) { $session_id = 0 };
2143         if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
2145         (my $package, my $file, my $row, my $subroutine, my $hasArgs, my $wantArray, my $evalText, my $isRequire) = caller(1);
2146         my $caller_text = "subroutin $subroutine";
2147         if ($subroutine eq "(eval)") {
2148                 $caller_text = "eval block within file '$file' for '$evalText'"; 
2149         }
2150         daemon_log("$session_id INFO: new ldap handle for $caller_text required");
2152         my $ldap_handle = $ldap_pool->get();
2153         
2154         if (not defined $ldap_handle) {
2155                 daemon_log("$session_id INFO: ldap handle for $caller_text not available");
2156         }
2157         daemon_log("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
2158         return $ldap_handle;
2161 sub release_ldap_handle {
2162         my ($ldap_handle) = @_ ;
2163         $ldap_pool->free($ldap_handle);
2164         daemon_log("0 INFO: ldap handle released!\n-------------------------------------------------------------------------------");
2165         return;
2169 sub change_fai_state {
2170     my ($st, $targets, $session_id) = @_;
2171     $session_id = 0 if not defined $session_id;
2172     # Set FAI state to localboot
2173     my %mapActions= (
2174         reboot    => '',
2175         update    => 'softupdate',
2176         localboot => 'localboot',
2177         reinstall => 'install',
2178         rescan    => '',
2179         wake      => '',
2180         memcheck  => 'memcheck',
2181         sysinfo   => 'sysinfo',
2182         install   => 'install',
2183     );
2185     # Return if this is unknown
2186     if (!exists $mapActions{ $st }){
2187         daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1); 
2188       return;
2189     }
2191     my $state= $mapActions{ $st };
2193     #if( defined($ldap_handle) ) {
2195       # Build search filter for hosts
2196         my $search= "(&(objectClass=GOhard)";
2197         foreach (@{$targets}){
2198             $search.= "(macAddress=$_)";
2199         }
2200         $search.= ")";
2202       # If there's any host inside of the search string, procress them
2203         if (!($search =~ /macAddress/)){
2204             daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);    
2205             return;
2206         }
2208         my $ldap_handle = &get_ldap_handle($session_id);
2209       # Perform search for Unit Tag
2210       my $mesg = $ldap_handle->search(
2211           base   => $ldap_base,
2212           scope  => 'sub',
2213           attrs  => ['dn', 'FAIstate', 'objectClass'],
2214           filter => "$search"
2215           );
2217           if ($mesg->count) {
2218                   my @entries = $mesg->entries;
2219                   if (0 == @entries) {
2220                                   daemon_log("$session_id ERROR: ldap search failed: ldap_base=$ldap_base, filter=$search", 1); 
2221                   }
2223                   foreach my $entry (@entries) {
2224                           # Only modify entry if it is not set to '$state'
2225                           if ($entry->get_value("FAIstate") ne "$state"){
2226                                   daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
2227                                   my $result;
2228                                   my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
2229                                   if (exists $tmp{'FAIobject'}){
2230                                           if ($state eq ''){
2231                                                   $result= $ldap_handle->modify($entry->dn, changes => [ delete => [ FAIstate => [] ] ]);
2232                                           } else {
2233                                                   $result= $ldap_handle->modify($entry->dn, changes => [ replace => [ FAIstate => $state ] ]);
2234                                           }
2235                                   } elsif ($state ne ''){
2236                                           $result= $ldap_handle->modify($entry->dn, changes => [ add => [ objectClass => 'FAIobject' ], add => [ FAIstate => $state ] ]);
2237                                   }
2239                                   # Errors?
2240                                   if ($result->code){
2241                                           daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2242                                   }
2243                           } else {
2244                                   daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 7); 
2245                           }  
2246                   }
2247           } else {
2248                 daemon_log("$session_id ERROR: LDAP search failed: ldap_base=$ldap_base, filter=$search", 1);
2249           }
2250           &release_ldap_handle($ldap_handle);             
2252     # if no ldap handle defined
2253     #} else {
2254     #    daemon_log("$session_id ERROR: no LDAP handle defined for update FAIstate", 1); 
2255     #}
2257         return;
2261 sub change_goto_state {
2262     my ($st, $targets, $session_id) = @_;
2263     $session_id = 0  if not defined $session_id;
2265     # Switch on or off?
2266     my $state= $st eq 'active' ? 'active': 'locked';
2268     my $ldap_handle = &get_ldap_handle($session_id);
2269     if( defined($ldap_handle) ) {
2271       # Build search filter for hosts
2272       my $search= "(&(objectClass=GOhard)";
2273       foreach (@{$targets}){
2274         $search.= "(macAddress=$_)";
2275       }
2276       $search.= ")";
2278       # If there's any host inside of the search string, procress them
2279       if (!($search =~ /macAddress/)){
2280         return;
2281       }
2283       # Perform search for Unit Tag
2284       my $mesg = $ldap_handle->search(
2285           base   => $ldap_base,
2286           scope  => 'sub',
2287           attrs  => ['dn', 'gotoMode'],
2288           filter => "$search"
2289           );
2291       if ($mesg->count) {
2292         my @entries = $mesg->entries;
2293         foreach my $entry (@entries) {
2295           # Only modify entry if it is not set to '$state'
2296           if ($entry->get_value("gotoMode") ne $state){
2298             daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
2299             my $result;
2300             $result= $ldap_handle->modify($entry->dn, changes => [replace => [ gotoMode => $state ] ]);
2302             # Errors?
2303             if ($result->code){
2304               &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2305             }
2307           }
2308         }
2309       } else {
2310                 daemon_log("$session_id ERROR: LDAP search failed in function change_goto_state: ldap_base=$ldap_base, filter=$search", 1);
2311           }
2313     }
2314         &release_ldap_handle($ldap_handle);
2315         return;
2319 sub run_recreate_packages_db {
2320     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2321     my $session_id = $session->ID;
2322         &main::daemon_log("$session_id INFO: Recreating FAI Packages DB ('$fai_release_tn', '$fai_server_tn', '$packages_list_tn')", 5);
2323         $kernel->yield('create_fai_release_db', $fai_release_tn);
2324         $kernel->yield('create_fai_server_db', $fai_server_tn);
2325         return;
2329 sub run_create_fai_server_db {
2330     my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2331     my $session_id = $session->ID;
2332         my $ldap_handle = &get_ldap_handle();
2333         if (not defined $ldap_handle) {
2334                 $kernel->delay_set('create_fai_server_db', 1, $table_name);
2335                 return;
2336         }
2337     my $task = POE::Wheel::Run->new(
2338             Program => sub { &create_fai_server_db($table_name,$kernel, undef, $session_id, $ldap_handle) },
2339             StdoutEvent  => "session_run_result",
2340             StderrEvent  => "session_run_debug",
2341             CloseEvent   => "session_run_done",
2342             );
2344     $heap->{task}->{ $task->ID } = $task;
2345         $heap->{ldap_handle}->{$task->ID} = $ldap_handle;
2346     return;
2350 sub create_fai_server_db {
2351         my ($table_name, $kernel, $dont_create_packages_list, $session_id, $ldap_handle) = @_;
2352         my $result;
2354         if (not defined $session_id) { $session_id = 0; }
2355         if(defined($ldap_handle)) {
2356                 daemon_log("$session_id INFO: create_fai_server_db: start", 5);
2357                 my $mesg= $ldap_handle->search(
2358                         base   => $ldap_base,
2359                         scope  => 'sub',
2360                         attrs  => ['FAIrepository', 'gosaUnitTag'],
2361                         filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
2362                 );
2363                 if($mesg->{'resultCode'} == 0 &&
2364                         $mesg->count != 0) {
2365                         foreach my $entry (@{$mesg->{entries}}) {
2366                                 if($entry->exists('FAIrepository')) {
2367                                         # Add an entry for each Repository configured for server
2368                                         foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
2369                                                 my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
2370                                                 my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
2371                                                 $result= $fai_server_db->add_dbentry( { 
2372                                                                 table => $table_name,
2373                                                                 primkey => ['server', 'fai_release', 'tag'],
2374                                                                 server => $tmp_url,
2375                                                                 fai_release => $tmp_release,
2376                                                                 sections => $tmp_sections,
2377                                                                 tag => (length($tmp_tag)>0)?$tmp_tag:"",
2378                                                         } );
2379                                         }
2380                                 }
2381                         }
2382                 }
2383                 daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
2385                 # TODO: Find a way to post the 'create_packages_list_db' event
2386                 if(not defined($dont_create_packages_list)) {
2387                         &create_packages_list_db(undef, $session_id);
2388                 }
2389         }       
2391         return $result;
2395 sub run_create_fai_release_db {
2396         my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2397         my $session_id = $session->ID;
2398         my $ldap_handle = &get_ldap_handle();
2399         if (not defined $ldap_handle) {
2400                 $kernel->delay_set('create_fai_release_db', 1, $table_name);
2401                 return;
2402         }
2403         my $task = POE::Wheel::Run->new(
2404                 Program => sub { &create_fai_release_db($table_name, $session_id, $ldap_handle) },
2405                 StdoutEvent  => "session_run_result",
2406                 StderrEvent  => "session_run_debug",
2407                 CloseEvent   => "session_run_done",
2408         );
2410         $heap->{task}->{ $task->ID } = $task;
2411         $heap->{ldap_handle}->{$task->ID} = $ldap_handle;
2412         return;
2416 sub create_fai_release_db {
2417         my ($table_name, $session_id, $ldap_handle) = @_;
2418         my $result;
2420         # used for logging
2421         if (not defined $session_id) { $session_id = 0; }
2423         #my $ldap_handle = &get_ldap_handle();
2424         if(defined($ldap_handle)) {
2425                 daemon_log("$session_id INFO: create_fai_release_db: start",5);
2426                 my $mesg= $ldap_handle->search(
2427                         base   => $ldap_base,
2428                         scope  => 'sub',
2429                         attrs  => [],
2430                         filter => "(&(objectClass=organizationalUnit)(ou=fai))",
2431                 );
2432                 if(($mesg->code == 0) && ($mesg->count != 0))
2433                 {
2434                         daemon_log("$session_id DEBUG: create_fai_release_db: count " . $mesg->count,8);
2436                         # Walk through all possible FAI container ou's
2437                         my @sql_list;
2438                         my $timestamp= &get_time();
2439                         foreach my $ou (@{$mesg->{entries}}) {
2440                                 my $tmp_classes= resolve_fai_classes($ou->dn, $ldap_handle, $session_id);
2441                                 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
2442                                         my @tmp_array=get_fai_release_entries($tmp_classes);
2443                                         if(@tmp_array) {
2444                                                 foreach my $entry (@tmp_array) {
2445                                                         if(defined($entry) && ref($entry) eq 'HASH') {
2446                                                                 my $sql= 
2447                                                                 "INSERT INTO $table_name "
2448                                                                 ."(timestamp, fai_release, class, type, state) VALUES ("
2449                                                                 .$timestamp.","
2450                                                                 ."'".$entry->{'release'}."',"
2451                                                                 ."'".$entry->{'class'}."',"
2452                                                                 ."'".$entry->{'type'}."',"
2453                                                                 ."'".$entry->{'state'}."')";
2454                                                                 push @sql_list, $sql;
2455                                                         }
2456                                                 }
2457                                         }
2458                                 }
2459                         }
2461                         daemon_log("$session_id DEBUG: create_fai_release_db: Inserting ".scalar @sql_list." entries to DB",8);
2462                         if(@sql_list) {
2463                                 unshift @sql_list, "VACUUM";
2464                                 unshift @sql_list, "DELETE FROM $table_name";
2465                                 $fai_release_db->exec_statementlist(\@sql_list);
2466                         }
2467                         daemon_log("$session_id DEBUG: create_fai_release_db: Done with inserting",7);
2468                 } else {
2469                         daemon_log("$session_id INFO: create_fai_release_db: error: " . $mesg->code, 5);
2470                 }
2471                 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
2472         }
2473         #&release_ldap_handle($ldap_handle);
2474         return $result;
2477 sub get_fai_types {
2478         my $tmp_classes = shift || return undef;
2479         my @result;
2481         foreach my $type(keys %{$tmp_classes}) {
2482                 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
2483                         my $entry = {
2484                                 type => $type,
2485                                 state => $tmp_classes->{$type}[0],
2486                         };
2487                         push @result, $entry;
2488                 }
2489         }
2491         return @result;
2494 sub get_fai_state {
2495         my $result = "";
2496         my $tmp_classes = shift || return $result;
2498         foreach my $type(keys %{$tmp_classes}) {
2499                 if(defined($tmp_classes->{$type}[0])) {
2500                         $result = $tmp_classes->{$type}[0];
2501                         
2502                 # State is equal for all types in class
2503                         last;
2504                 }
2505         }
2507         return $result;
2510 sub resolve_fai_classes {
2511         my ($fai_base, $ldap_handle, $session_id) = @_;
2512         if (not defined $session_id) { $session_id = 0; }
2513         my $result;
2514         my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
2515         my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
2516         my $fai_classes;
2518         daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base",7);
2519         my $mesg= $ldap_handle->search(
2520                 base   => $fai_base,
2521                 scope  => 'sub',
2522                 attrs  => ['cn','objectClass','FAIstate'],
2523                 filter => $fai_filter,
2524         );
2525         daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries",7);
2527         if($mesg->{'resultCode'} == 0 &&
2528                 $mesg->count != 0) {
2529                 foreach my $entry (@{$mesg->{entries}}) {
2530                         if($entry->exists('cn')) {
2531                                 my $tmp_dn= $entry->dn();
2532                                 $tmp_dn= substr( $tmp_dn, 0, length($tmp_dn)
2533                                         - length($fai_base) - 1 );
2535                                 # Skip classname and ou dn parts for class
2536                                 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?)$/;
2538                                 # Skip classes without releases
2539                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2540                                         next;
2541                                 }
2543                                 my $tmp_cn= $entry->get_value('cn');
2544                                 my $tmp_state= $entry->get_value('FAIstate');
2546                                 my $tmp_type;
2547                                 # Get FAI type
2548                                 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
2549                                         if(grep $_ eq $oclass, @possible_fai_classes) {
2550                                                 $tmp_type= $oclass;
2551                                                 last;
2552                                         }
2553                                 }
2555                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2556                                         # A Subrelease
2557                                         my @sub_releases = split(/,/, $tmp_release);
2559                                         # Walk through subreleases and build hash tree
2560                                         my $hash;
2561                                         while(my $tmp_sub_release = pop @sub_releases) {
2562                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2563                                         }
2564                                         eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
2565                                 } else {
2566                                         # A branch, no subrelease
2567                                         push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
2568                                 }
2569                         } elsif (!$entry->exists('cn')) {
2570                                 my $tmp_dn= $entry->dn();
2571                                 $tmp_dn= substr( $tmp_dn, 0, length($tmp_dn)
2572                                         - length($fai_base) - 1 );
2573                                 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?)$/;
2575                                 # Skip classes without releases
2576                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2577                                         next;
2578                                 }
2580                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2581                                         # A Subrelease
2582                                         my @sub_releases= split(/,/, $tmp_release);
2584                                         # Walk through subreleases and build hash tree
2585                                         my $hash;
2586                                         while(my $tmp_sub_release = pop @sub_releases) {
2587                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2588                                         }
2589                                         # Remove the last two characters
2590                                         chop($hash);
2591                                         chop($hash);
2593                                         eval('$fai_classes->'.$hash.'= {}');
2594                                 } else {
2595                                         # A branch, no subrelease
2596                                         if(!exists($fai_classes->{$tmp_release})) {
2597                                                 $fai_classes->{$tmp_release} = {};
2598                                         }
2599                                 }
2600                         }
2601                 }
2603                 # The hash is complete, now we can honor the copy-on-write based missing entries
2604                 foreach my $release (keys %$fai_classes) {
2605                         $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
2606                 }
2607         }
2608         return $result;
2611 sub apply_fai_inheritance {
2612        my $fai_classes = shift || return {};
2613        my $tmp_classes;
2615        # Get the classes from the branch
2616        foreach my $class (keys %{$fai_classes}) {
2617                # Skip subreleases
2618                if($class =~ /^ou=.*$/) {
2619                        next;
2620                } else {
2621                        $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
2622                }
2623        }
2625        # Apply to each subrelease
2626        foreach my $subrelease (keys %{$fai_classes}) {
2627                if($subrelease =~ /ou=/) {
2628                        foreach my $tmp_class (keys %{$tmp_classes}) {
2629                                if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
2630                                        $fai_classes->{$subrelease}->{$tmp_class} =
2631                                        deep_copy($tmp_classes->{$tmp_class});
2632                                } else {
2633                                        foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
2634                                                if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
2635                                                        $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
2636                                                        deep_copy($tmp_classes->{$tmp_class}->{$type});
2637                                                }
2638                                        }
2639                                }
2640                        }
2641                }
2642        }
2644        # Find subreleases in deeper levels
2645        foreach my $subrelease (keys %{$fai_classes}) {
2646                if($subrelease =~ /ou=/) {
2647                        foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
2648                                if($subsubrelease =~ /ou=/) {
2649                                        apply_fai_inheritance($fai_classes->{$subrelease});
2650                                }
2651                        }
2652                }
2653        }
2655        return $fai_classes;
2658 sub get_fai_release_entries {
2659         my $tmp_classes = shift || return;
2660         my $parent = shift || "";
2661         my @result = shift || ();
2663         foreach my $entry (keys %{$tmp_classes}) {
2664                 if(defined($entry)) {
2665                         if($entry =~ /^ou=.*$/) {
2666                                 my $release_name = $entry;
2667                                 $release_name =~ s/ou=//g;
2668                                 if(length($parent)>0) {
2669                                         $release_name = $parent."/".$release_name;
2670                                 }
2671                                 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2672                                 foreach my $bufentry(@bufentries) {
2673                                         push @result, $bufentry;
2674                                 }
2675                         } else {
2676                                 my @types = get_fai_types($tmp_classes->{$entry});
2677                                 foreach my $type (@types) {
2678                                         push @result, 
2679                                         {
2680                                                 'class' => $entry,
2681                                                 'type' => $type->{'type'},
2682                                                 'release' => $parent,
2683                                                 'state' => $type->{'state'},
2684                                         };
2685                                 }
2686                         }
2687                 }
2688         }
2690         return @result;
2693 sub deep_copy {
2694         my $this = shift;
2695         if (not ref $this) {
2696                 $this;
2697         } elsif (ref $this eq "ARRAY") {
2698                 [map deep_copy($_), @$this];
2699         } elsif (ref $this eq "HASH") {
2700                 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2701         } else { die "what type is $_?" }
2705 sub session_run_result {
2706     my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];    
2707     $kernel->sig(CHLD => "child_reap");
2710 sub session_run_debug {
2711     my $result = $_[ARG0];
2712     print STDERR "$result\n";
2715 sub session_run_done {
2716     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2717     delete $heap->{task}->{$task_id};
2718         if (exists $heap->{ldap_handle}->{$task_id}) {
2719                 &release_ldap_handle($heap->{ldap_handle}->{$task_id});
2720         }
2721         delete $heap->{ldap_handle}->{$task_id};
2725 sub create_sources_list {
2726         my $session_id = shift;
2727         my $result="/tmp/gosa_si_tmp_sources_list";
2729         # Remove old file
2730         if(stat($result)) {
2731                 unlink($result);
2732                 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7); 
2733         }
2735         my $fh;
2736         open($fh, ">$result");
2737         if (not defined $fh) {
2738                 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7); 
2739                 return undef;
2740         }
2741         if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2742                 my $ldap_handle = &get_ldap_handle();
2743                 my $mesg=$ldap_handle->search(
2744                         base    => $main::ldap_server_dn,
2745                         scope   => 'base',
2746                         attrs   => 'FAIrepository',
2747                         filter  => 'objectClass=FAIrepositoryServer'
2748                 );
2749                 &release_ldap_handle($ldap_handle);
2750                 if($mesg->count) {
2751                         foreach my $entry(@{$mesg->{'entries'}}) {
2752                                 foreach my $value(@{$entry->get_value('FAIrepository', asref => 1)}) {
2753                                         my ($server, $tag, $release, $sections)= split /\|/, $value;
2754                                         my $line = "deb $server $release";
2755                                         $sections =~ s/,/ /g;
2756                                         $line.= " $sections";
2757                                         print $fh $line."\n";
2758                                 }
2759                         }
2760                 }
2761         } else {
2762                 if (defined $main::ldap_server_dn){
2763                         &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1); 
2764                 } else {
2765                         &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2766                 }
2767         }
2768         close($fh);
2770         return $result;
2774 sub run_create_packages_list_db {
2775     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2776         my $session_id = $session->ID;
2777         my $task = POE::Wheel::Run->new(
2778                                         Priority => +20,
2779                                         Program => sub {&create_packages_list_db(undef, $session_id)},
2780                                         StdoutEvent  => "session_run_result",
2781                                         StderrEvent  => "session_run_debug",
2782                                         CloseEvent   => "session_run_done",
2783                                         );
2784         $heap->{task}->{ $task->ID } = $task;
2788 sub create_packages_list_db {
2789         my ($sources_file, $session_id) = @_;
2790         
2791         # it should not be possible to trigger a recreation of packages_list_db
2792         # while packages_list_db is under construction, so set flag packages_list_under_construction
2793         # which is tested befor recreation can be started
2794         if (-r $packages_list_under_construction) {
2795                 daemon_log("$session_id WARNING: packages_list_db is right now under construction, please wait until this process is finished", 3);
2796                 return;
2797         } else {
2798                 daemon_log("$session_id INFO: create_packages_list_db: start", 5); 
2799                 # set packages_list_under_construction to true
2800                 system("touch $packages_list_under_construction");
2801                 @packages_list_statements=();
2802         }
2804         if (not defined $session_id) { $session_id = 0; }
2805 #       if (not defined $ldap_handle) { 
2806 #               $ldap_handle= &get_ldap_handle();
2808 #               if (not defined $ldap_handle) {
2809 #                       daemon_log("$session_id ERROR: no ldap_handle available to create_packages_list_db", 1);
2810 #                       unlink($packages_list_under_construction);
2811 #                       return;
2812 #               }
2813 #       }
2814         if (not defined $sources_file) { 
2815                 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5); 
2816                 $sources_file = &create_sources_list($session_id);
2817         }
2819         if (not defined $sources_file) {
2820                 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1); 
2821                 unlink($packages_list_under_construction);
2822                 return;
2823         }
2825         my $line;
2827         open(CONFIG, "<$sources_file") or do {
2828                 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2829                 unlink($packages_list_under_construction);
2830                 return;
2831         };
2833         # Read lines
2834         while ($line = <CONFIG>){
2835                 # Unify
2836                 chop($line);
2837                 $line =~ s/^\s+//;
2838                 $line =~ s/^\s+/ /;
2840                 # Strip comments
2841                 $line =~ s/#.*$//g;
2843                 # Skip empty lines
2844                 if ($line =~ /^\s*$/){
2845                         next;
2846                 }
2848                 # Interpret deb line
2849                 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2850                         my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2851                         my $section;
2852                         foreach $section (split(' ', $sections)){
2853                                 &parse_package_info( $baseurl, $dist, $section, $session_id );
2854                         }
2855                 }
2856         }
2858         close (CONFIG);
2860         if(keys(%repo_dirs)) {
2861                 find(\&cleanup_and_extract, keys( %repo_dirs ));
2862                 &main::strip_packages_list_statements();
2863                 $packages_list_db->exec_statementlist(\@packages_list_statements);
2864         }
2865         unlink($packages_list_under_construction);
2866         daemon_log("$session_id INFO: create_packages_list_db: finished", 5); 
2867         return;
2870 # This function should do some intensive task to minimize the db-traffic
2871 sub strip_packages_list_statements {
2872         my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2873         my @new_statement_list=();
2874         my $hash;
2875         my $insert_hash;
2876         my $update_hash;
2877         my $delete_hash;
2878         my $known_packages_hash;
2879         my $local_timestamp=get_time();
2881         foreach my $existing_entry (@existing_entries) {
2882                 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
2883         }
2885         foreach my $statement (@packages_list_statements) {
2886                 if($statement =~ /^INSERT/i) {
2887                         # Assign the values from the insert statement
2888                         my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~ 
2889                         /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
2890                         if(exists($hash->{$distribution}->{$package}->{$version})) {
2891                                 # If section or description has changed, update the DB
2892                                 if( 
2893                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or 
2894                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
2895                                 ) {
2896                                         @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
2897                                 } else {
2898                                         # package is already present in database. cache this knowledge for later use
2899                                         @{$known_packages_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2900                                 }
2901                         } else {
2902                                 # Insert a non-existing entry to db
2903                                 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2904                         }
2905                 } elsif ($statement =~ /^UPDATE/i) {
2906                         my ($template,$package,$version) = ($1,$2,$3) if $statement =~
2907                         /^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;
2908                         foreach my $distribution (keys %{$hash}) {
2909                                 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
2910                                         # update the insertion hash to execute only one query per package (insert instead insert+update)
2911                                         @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
2912                                 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
2913                                         if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
2914                                                 my $section;
2915                                                 my $description;
2916                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
2917                                                         length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
2918                                                         $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
2919                                                 }
2920                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2921                                                         $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
2922                                                 }
2923                                                 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2924                                         }
2925                                 }
2926                         }
2927                 }
2928         }
2930         # Check for orphaned entries
2931         foreach my $existing_entry (@existing_entries) {
2932                 my $distribution= @{$existing_entry}[0];
2933                 my $package= @{$existing_entry}[1];
2934                 my $version= @{$existing_entry}[2];
2935                 my $section= @{$existing_entry}[3];
2937                 if(
2938                         exists($insert_hash->{$distribution}->{$package}->{$version}) ||
2939                         exists($update_hash->{$distribution}->{$package}->{$version}) ||
2940                         exists($known_packages_hash->{$distribution}->{$package}->{$version})
2941                 ) {
2942                         next;
2943                 } else {
2944                         # Insert entry to delete hash
2945                         @{$delete_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section);
2946                 }
2947         }
2949         # unroll the insert hash
2950         foreach my $distribution (keys %{$insert_hash}) {
2951                 foreach my $package (keys %{$insert_hash->{$distribution}}) {
2952                         foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
2953                                 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
2954                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
2955                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
2956                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
2957                                 ."'$local_timestamp')";
2958                         }
2959                 }
2960         }
2962         # unroll the update hash
2963         foreach my $distribution (keys %{$update_hash}) {
2964                 foreach my $package (keys %{$update_hash->{$distribution}}) {
2965                         foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
2966                                 my $set = "";
2967                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
2968                                         $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
2969                                 }
2970                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2971                                         $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
2972                                 }
2973                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
2974                                         $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
2975                                 }
2976                                 if(defined($set) and length($set) > 0) {
2977                                         $set .= "timestamp = '$local_timestamp'";
2978                                 } else {
2979                                         next;
2980                                 }
2981                                 push @new_statement_list, 
2982                                 "UPDATE $main::packages_list_tn SET $set WHERE"
2983                                 ." distribution = '$distribution'"
2984                                 ." AND package = '$package'"
2985                                 ." AND version = '$version'";
2986                         }
2987                 }
2988         }
2989         
2990         # unroll the delete hash
2991         foreach my $distribution (keys %{$delete_hash}) {
2992                 foreach my $package (keys %{$delete_hash->{$distribution}}) {
2993                         foreach my $version (keys %{$delete_hash->{$distribution}->{$package}}) {
2994                                 my $section = @{$delete_hash->{$distribution}->{$package}->{$version}}[3];
2995                                 push @new_statement_list, "DELETE FROM $main::packages_list_tn WHERE distribution='$distribution' AND package='$package' AND version='$version' AND section='$section'";
2996                         }
2997                 }
2998         }
3000         unshift(@new_statement_list, "VACUUM");
3002         @packages_list_statements = @new_statement_list;
3006 sub parse_package_info {
3007     my ($baseurl, $dist, $section, $session_id)= @_;
3008     my ($package);
3009     if (not defined $session_id) { $session_id = 0; }
3010     my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
3011     $repo_dirs{ "${repo_path}/pool" } = 1;
3013     foreach $package ("Packages.gz"){
3014         daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
3015         get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
3016         parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
3017     }
3018     
3022 sub get_package {
3023     my ($url, $dest, $session_id)= @_;
3024     if (not defined $session_id) { $session_id = 0; }
3026     my $tpath = dirname($dest);
3027     -d "$tpath" || mkpath "$tpath";
3029     # This is ugly, but I've no time to take a look at "how it works in perl"
3030     if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
3031         system("gunzip -cd '$dest' > '$dest.in'");
3032         daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 5);
3033         unlink($dest);
3034         daemon_log("$session_id DEBUG: delete file '$dest'", 5); 
3035     } else {
3036         daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' into '$dest' failed!", 1);
3037     }
3038     return 0;
3042 sub parse_package {
3043     my ($path, $dist, $srv_path, $session_id)= @_;
3044     if (not defined $session_id) { $session_id = 0;}
3045     my ($package, $version, $section, $description);
3046     my $PACKAGES;
3047     my $timestamp = &get_time();
3049     if(not stat("$path.in")) {
3050         daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
3051         return;
3052     }
3054     open($PACKAGES, "<$path.in");
3055     if(not defined($PACKAGES)) {
3056         daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1); 
3057         return;
3058     }
3060     # Read lines
3061     while (<$PACKAGES>){
3062         my $line = $_;
3063         # Unify
3064         chop($line);
3066         # Use empty lines as a trigger
3067         if ($line =~ /^\s*$/){
3068             my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
3069             push(@packages_list_statements, $sql);
3070             $package = "none";
3071             $version = "none";
3072             $section = "none";
3073             $description = "none"; 
3074             next;
3075         }
3077         # Trigger for package name
3078         if ($line =~ /^Package:\s/){
3079             ($package)= ($line =~ /^Package: (.*)$/);
3080             next;
3081         }
3083         # Trigger for version
3084         if ($line =~ /^Version:\s/){
3085             ($version)= ($line =~ /^Version: (.*)$/);
3086             next;
3087         }
3089         # Trigger for description
3090         if ($line =~ /^Description:\s/){
3091             ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
3092             next;
3093         }
3095         # Trigger for section
3096         if ($line =~ /^Section:\s/){
3097             ($section)= ($line =~ /^Section: (.*)$/);
3098             next;
3099         }
3101         # Trigger for filename
3102         if ($line =~ /^Filename:\s/){
3103             my ($filename) = ($line =~ /^Filename: (.*)$/);
3104             store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
3105             next;
3106         }
3107     }
3109     close( $PACKAGES );
3110     unlink( "$path.in" );
3114 sub store_fileinfo {
3115     my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
3117     my %fileinfo = (
3118         'package' => $package,
3119         'dist' => $dist,
3120         'version' => $vers,
3121     );
3123     $repo_files{ "${srvdir}/$file" } = \%fileinfo;
3127 sub cleanup_and_extract {
3128         my $fileinfo = $repo_files{ $File::Find::name };
3130         if( defined $fileinfo ) {
3131                 my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
3132                 my $sql;
3133                 my $package = $fileinfo->{ 'package' };
3134                 my $newver = $fileinfo->{ 'version' };
3136                 mkpath($dir);
3137                 system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
3139                 if( -f "$dir/DEBIAN/templates" ) {
3141                         daemon_log("0 DEBUG: Found debconf templates in '$package' - $newver", 7);
3143                         my $tmpl= ""; {
3144                                 local $/=undef;
3145                                 open FILE, "$dir/DEBIAN/templates";
3146                                 $tmpl = &encode_base64(<FILE>);
3147                                 close FILE;
3148                         }
3149                         rmtree("$dir/DEBIAN/templates");
3151                         $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
3152                         push @packages_list_statements, $sql;
3153                 }
3154         }
3156         return;
3160 sub register_at_foreign_servers {   
3161     my ($kernel) = $_[KERNEL];
3163     # hole alle bekannten server aus known_server_db
3164     my $server_sql = "SELECT * FROM $known_server_tn";
3165     my $server_res = $known_server_db->exec_statement($server_sql);
3167     # no entries in known_server_db
3168     if (not ref(@$server_res[0]) eq "ARRAY") { 
3169         # TODO
3170     }
3172     # detect already connected clients
3173     my $client_sql = "SELECT * FROM $known_clients_tn"; 
3174     my $client_res = $known_clients_db->exec_statement($client_sql);
3176     # send my server details to all other gosa-si-server within the network
3177     foreach my $hit (@$server_res) {
3178         my $hostname = @$hit[0];
3179         my $hostkey = &create_passwd;
3181         # add already connected clients to registration message 
3182         my $myhash = &create_xml_hash('new_server', $server_address, $hostname);
3183         &add_content2xml_hash($myhash, 'key', $hostkey);
3184         map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res);
3186         # add locally loaded gosa-si modules to registration message
3187         my $loaded_modules = {};
3188         while (my ($package, $pck_info) = each %$known_modules) {
3189                                                 next if ((!defined(@$pck_info[2])) || (!(ref (@$pck_info[2]) eq 'HASH')));
3190                                                 foreach my $act_module (keys(%{@$pck_info[2]})) {
3191                                                         $loaded_modules->{$act_module} = ""; 
3192                                                 }
3193         }
3195         map(&add_content2xml_hash($myhash, "loaded_modules", $_), keys(%$loaded_modules));
3197         # add macaddress to registration message
3198         my ($host_ip, $host_port) = split(/:/, $hostname);
3199         my $local_ip = &get_local_ip_for_remote_ip($host_ip);
3200         my $network_interface= &get_interface_for_ip($local_ip);
3201         my $host_mac = &get_mac_for_interface($network_interface);
3202         &add_content2xml_hash($myhash, 'macaddress', $host_mac);
3203         
3204         # build registration message and send it
3205         my $foreign_server_msg = &create_xml_string($myhash);
3206         my $error = &send_msg_to_target($foreign_server_msg, $hostname, $ServerPackages_key, "new_server", 0); 
3207     }
3208     
3209     $kernel->delay_set("register_at_foreign_servers", $foreign_servers_register_delay); 
3210     return;
3214 #==== MAIN = main ==============================================================
3215 #  parse commandline options
3216 Getopt::Long::Configure( "bundling" );
3217 GetOptions("h|help" => \&usage,
3218         "c|config=s" => \$cfg_file,
3219         "f|foreground" => \$foreground,
3220         "v|verbose+" => \$verbose,
3221         "no-arp+" => \$no_arp,
3222            );
3224 #  read and set config parameters
3225 &check_cmdline_param ;
3226 &read_configfile($cfg_file, %cfg_defaults);
3227 &check_pid;
3229 $SIG{CHLD} = 'IGNORE';
3231 # forward error messages to logfile
3232 if( ! $foreground ) {
3233   open( STDIN,  '+>/dev/null' );
3234   open( STDOUT, '+>&STDIN'    );
3235   open( STDERR, '+>&STDIN'    );
3238 # Just fork, if we are not in foreground mode
3239 if( ! $foreground ) { 
3240     chdir '/'                 or die "Can't chdir to /: $!";
3241     $pid = fork;
3242     setsid                    or die "Can't start a new session: $!";
3243     umask 0;
3244 } else { 
3245     $pid = $$; 
3248 # Do something useful - put our PID into the pid_file
3249 if( 0 != $pid ) {
3250     open( LOCK_FILE, ">$pid_file" );
3251     print LOCK_FILE "$pid\n";
3252     close( LOCK_FILE );
3253     if( !$foreground ) { 
3254         exit( 0 ) 
3255     };
3258 # parse head url and revision from svn
3259 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
3260 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
3261 $server_headURL = defined $1 ? $1 : 'unknown' ;
3262 $server_revision = defined $2 ? $2 : 'unknown' ;
3263 if ($server_headURL =~ /\/tag\// || 
3264         $server_headURL =~ /\/branches\// ) {
3265     $server_status = "stable"; 
3266 } else {
3267     $server_status = "developmental" ;
3269 # Prepare log file and set permissions
3270 $root_uid = getpwnam('root');
3271 $adm_gid = getgrnam('adm');
3272 open(FH, ">>$log_file");
3273 close FH;
3274 chmod(0440, $log_file);
3275 chown($root_uid, $adm_gid, $log_file);
3276 chown($root_uid, $adm_gid, "/var/lib/gosa-si");
3278 daemon_log(" ", 1);
3279 daemon_log("$0 started!", 1);
3280 daemon_log("status: $server_status", 1);
3281 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1); 
3283 # Create a pool of LDAP handles
3284 $ldap_factory =  ResourcePool::Factory::Net::LDAP->new($ldap_uri, version => $ldap_version);
3285 $ldap_factory->bind($ldap_admin_dn, password=>$ldap_admin_password);
3286 $ldap_pool = ResourcePool->new($ldap_factory,
3287                 Max         => 10,
3288                 #MaxTry      => 1,
3289                 #SleepOnFail    => [0, 0, 1, 1],
3290                 PreCreate       => 5,
3291 );
3294 # Buildup data bases
3296     no strict "refs";
3298     if ($db_module eq "DBmysql") {
3299         # connect to incoming_db
3300         $incoming_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3302         # connect to gosa-si job queue
3303         $job_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3305         # connect to known_clients_db
3306         $known_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3308         # connect to foreign_clients_db
3309         $foreign_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3311         # connect to known_server_db
3312         $known_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3314         # connect to login_usr_db
3315         $login_users_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3317         # connect to fai_server_db 
3318         $fai_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3320         # connect to fai_release_db
3321         $fai_release_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3323         # connect to packages_list_db
3324         $packages_list_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3326         # connect to messaging_db
3327         $messaging_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3329     } elsif ($db_module eq "DBsqlite") {
3330         # connect to incoming_db
3331         unlink($incoming_file_name);
3332         $incoming_db = GOSA::DBsqlite->new($incoming_file_name);
3333         
3334         # connect to gosa-si job queue
3335         unlink($job_queue_file_name);  ## just for debugging
3336         $job_db = GOSA::DBsqlite->new($job_queue_file_name);
3337         chmod(0640, $job_queue_file_name);
3338         chown($root_uid, $adm_gid, $job_queue_file_name);
3339         
3340         # connect to known_clients_db
3341         unlink($known_clients_file_name);   ## just for debugging
3342         $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
3343         chmod(0640, $known_clients_file_name);
3344         chown($root_uid, $adm_gid, $known_clients_file_name);
3345         
3346         # connect to foreign_clients_db
3347         unlink($foreign_clients_file_name);
3348         $foreign_clients_db = GOSA::DBsqlite->new($foreign_clients_file_name);
3349         chmod(0640, $foreign_clients_file_name);
3350         chown($root_uid, $adm_gid, $foreign_clients_file_name);
3351         
3352         # connect to known_server_db
3353         unlink($known_server_file_name);
3354         $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
3355         chmod(0640, $known_server_file_name);
3356         chown($root_uid, $adm_gid, $known_server_file_name);
3357         
3358         # connect to login_usr_db
3359         unlink($login_users_file_name);
3360         $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
3361         chmod(0640, $login_users_file_name);
3362         chown($root_uid, $adm_gid, $login_users_file_name);
3363         
3364         # connect to fai_server_db
3365         unlink($fai_server_file_name);
3366         $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
3367         chmod(0640, $fai_server_file_name);
3368         chown($root_uid, $adm_gid, $fai_server_file_name);
3369         
3370         # connect to fai_release_db
3371         unlink($fai_release_file_name);
3372         $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
3373         chmod(0640, $fai_release_file_name);
3374         chown($root_uid, $adm_gid, $fai_release_file_name);
3375         
3376         # connect to packages_list_db
3377         #unlink($packages_list_file_name);
3378         unlink($packages_list_under_construction);
3379         $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
3380         chmod(0640, $packages_list_file_name);
3381         chown($root_uid, $adm_gid, $packages_list_file_name);
3382         
3383         # connect to messaging_db
3384         unlink($messaging_file_name);
3385         $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
3386         chmod(0640, $messaging_file_name);
3387         chown($root_uid, $adm_gid, $messaging_file_name);
3388     }
3392 # Creating tables
3393 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
3394 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
3395 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
3396 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
3397 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
3398 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
3399 $foreign_clients_db->create_table($foreign_clients_tn, \@foreign_clients_col_names);
3400 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
3401 $incoming_db->create_table($incoming_tn, \@incoming_col_names);
3402 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
3404 # create xml object used for en/decrypting
3405 $xml = new XML::Simple();
3408 # foreign servers 
3409 my @foreign_server_list;
3411 # add foreign server from cfg file
3412 if ($foreign_server_string ne "") {
3413     my @cfg_foreign_server_list = split(",", $foreign_server_string);
3414     foreach my $foreign_server (@cfg_foreign_server_list) {
3415         push(@foreign_server_list, $foreign_server);
3416     }
3418     daemon_log("0 INFO: found foreign server in config file: ".join(", ", @foreign_server_list), 5);
3421 # Perform a DNS lookup for server registration if flag is true
3422 if ($dns_lookup eq "true") {
3423     # Add foreign server from dns
3424     my @tmp_servers;
3425     if (not $server_domain) {
3426         # Try our DNS Searchlist
3427         for my $domain(get_dns_domains()) {
3428             chomp($domain);
3429             my ($tmp_domains, $error_string) = &get_server_addresses($domain);
3430             if(@$tmp_domains) {
3431                 for my $tmp_server(@$tmp_domains) {
3432                     push @tmp_servers, $tmp_server;
3433                 }
3434             }
3435         }
3436         if(@tmp_servers && length(@tmp_servers)==0) {
3437             daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3438         }
3439     } else {
3440         @tmp_servers = &get_server_addresses($server_domain);
3441         if( 0 == @tmp_servers ) {
3442             daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3443         }
3444     }
3446     daemon_log("0 INFO: found foreign server via DNS ".join(", ", @tmp_servers), 5);    
3448     foreach my $server (@tmp_servers) { 
3449         unshift(@foreign_server_list, $server); 
3450     }
3451 } else {
3452     daemon_log("0 INFO: DNS lookup for server registration is disabled", 5);
3456 # eliminate duplicate entries
3457 @foreign_server_list = &del_doubles(@foreign_server_list);
3458 my $all_foreign_server = join(", ", @foreign_server_list);
3459 daemon_log("0 INFO: found foreign server in config file and DNS: '$all_foreign_server'", 5);
3461 # add all found foreign servers to known_server
3462 my $cur_timestamp = &get_time();
3463 foreach my $foreign_server (@foreign_server_list) {
3465         # do not add myself to known_server_db
3466         if (&is_local($foreign_server)) { next; }
3467         ######################################
3469     my $res = $known_server_db->add_dbentry( {table=>$known_server_tn, 
3470             primkey=>['hostname'],
3471             hostname=>$foreign_server,
3472             macaddress=>"",
3473             status=>'not_yet_registered',
3474             hostkey=>"none",
3475             loaded_modules => "none", 
3476             timestamp=>$cur_timestamp,
3477             } );
3481 # Import all modules
3482 &import_modules;
3484 # Check wether all modules are gosa-si valid passwd check
3485 &password_check;
3487 # Prepare for using Opsi 
3488 if ($opsi_enabled eq "true") {
3489     use JSON::RPC::Client;
3490     use XML::Quote qw(:all);
3491     $opsi_url= "https://".$opsi_admin.":".$opsi_password."@".$opsi_server.":4447/rpc";
3492     $opsi_client = new JSON::RPC::Client;
3496 POE::Component::Server::TCP->new(
3497         Alias => "TCP_SERVER",
3498         Port => $server_port,
3499         ClientInput => sub {
3500                 my ($kernel, $input, $heap, $session) = @_[KERNEL, ARG0, HEAP, SESSION];
3501         my $session_id = $session->ID;
3502         my $remote_ip = $heap->{'remote_ip'};
3503                 push(@msgs_to_decrypt, $input);
3504         &daemon_log("$session_id DEBUG: incoming message from '$remote_ip'", 7);
3505                 $kernel->yield("msg_to_decrypt");
3506         },
3507         InlineStates => {
3508                 msg_to_decrypt => \&msg_to_decrypt,
3509                 next_task => \&next_task,
3510                 task_result => \&handle_task_result,
3511                 task_done   => \&handle_task_done,
3512                 task_debug  => \&handle_task_debug,
3513                 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3514         }
3515 );
3517 daemon_log("0 INFO: start socket for incoming xml messages at port '$server_port' ", 1);
3519 # create session for repeatedly checking the job queue for jobs
3520 POE::Session->create(
3521         inline_states => {
3522                 _start => \&session_start,
3523         register_at_foreign_servers => \&register_at_foreign_servers,
3524         sig_handler => \&sig_handler,
3525         next_task => \&next_task,
3526         task_result => \&handle_task_result,
3527         task_done   => \&handle_task_done,
3528         task_debug  => \&handle_task_debug,
3529         watch_for_next_tasks => \&watch_for_next_tasks,
3530         watch_for_new_messages => \&watch_for_new_messages,
3531         watch_for_delivery_messages => \&watch_for_delivery_messages,
3532         watch_for_done_messages => \&watch_for_done_messages,
3533                 watch_for_new_jobs => \&watch_for_new_jobs,
3534         watch_for_modified_jobs => \&watch_for_modified_jobs,
3535         watch_for_done_jobs => \&watch_for_done_jobs,
3536         watch_for_opsi_jobs => \&watch_for_opsi_jobs,
3537         watch_for_old_known_clients => \&watch_for_old_known_clients,
3538         create_packages_list_db => \&run_create_packages_list_db,
3539         create_fai_server_db => \&run_create_fai_server_db,
3540         create_fai_release_db => \&run_create_fai_release_db,
3541                 recreate_packages_db => \&run_recreate_packages_db,
3542         session_run_result => \&session_run_result,
3543         session_run_debug => \&session_run_debug,
3544         session_run_done => \&session_run_done,
3545         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3546         }
3547 );
3550 POE::Kernel->run();
3551 exit;