Code

No longer pass on the list of users at local clients to all known
[gosa.git] / trunk / 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 Cwd;
43 use File::Spec;
44 use File::Basename;
45 use File::Find;
46 use File::Copy;
47 use File::Path;
48 use GOSA::GosaSupportDaemon;
49 use POE qw(Component::Server::TCP Wheel::Run Filter::Reference);
50 use Net::LDAP;
51 use Net::LDAP::Util qw(:escape);
52 use Time::HiRes qw( usleep);
54 # revision number of server and program name
55 my $server_headURL;
56 my $server_revision;
57 my $server_status;
58 our $prg= basename($0);
60 my $db_module = "DBsqlite";
61 {
62 no strict "refs";
63 require ("GOSA/".$db_module.".pm");
64 ("GOSA/".$db_module)->import;
65 daemon_log("0 INFO: importing database module '$db_module'", 1);
66 }
68 my $modules_path = "/usr/lib/gosa-si/modules";
69 use lib "/usr/lib/gosa-si/modules";
71 our $global_kernel;
72 my ($foreground, $ping_timeout);
73 my ($server);
74 my ($gosa_server, $job_queue_timeout, $job_queue_loop_delay);
75 my ($messaging_db_loop_delay);
76 my ($procid, $pid);
77 my ($arp_fifo);
78 my ($xml);
79 my $sources_list;
80 my $max_clients;
81 my %repo_files=();
82 my %repo_dirs=();
84 # Variables declared in config file are always set to 'our'
85 our (%cfg_defaults, $log_file, $pid_file, $repo_basepath,
86     $server_ip, $server_port, $ClientPackages_key, $dns_lookup,
87     $arp_activ, $gosa_unit_tag,
88     $GosaPackages_key, $gosa_timeout,
89     $foreign_server_string, $server_domain, $ServerPackages_key, $foreign_servers_register_delay,
90     $wake_on_lan_passwd, $job_synchronization, $modified_jobs_loop_delay,
91     $arp_enabled, $arp_interface,
92     $opsi_enabled, $opsi_server, $opsi_admin, $opsi_password,
93     $new_systems_ou, $logged_in_users_delay,
94 );
96 # additional variable which should be globaly accessable
97 our $server_address;
98 our $server_mac_address;
99 our $gosa_address;
100 our $no_arp;
101 our $verbose;
102 our $forground;
103 our $cfg_file;
104 our ($ldap_uri, $ldap_base, $ldap_admin_dn, $ldap_admin_password, $ldap_server_dn);
105 our ($mysql_username, $mysql_password, $mysql_database, $mysql_host);
106 our $known_modules;
107 our $root_uid;
108 our $adm_gid;
111 # specifies the verbosity of the daemon_log
112 $verbose = 0 ;
114 # if foreground is not null, script will be not forked to background
115 $foreground = 0 ;
117 # specifies the timeout seconds while checking the online status of a registrating client
118 $ping_timeout = 5;
120 $no_arp = 0;
121 my $packages_list_under_construction = "/tmp/packages_list_creation_in_progress";
122 my @packages_list_statements;
123 my $watch_for_new_jobs_in_progress = 0;
125 # holds all incoming decrypted messages
126 our $incoming_db;
127 our $incoming_tn = 'incoming';
128 my $incoming_file_name;
129 my @incoming_col_names = ("id INTEGER PRIMARY KEY",
130         "timestamp VARCHAR(14) DEFAULT 'none'", 
131         "headertag VARCHAR(255) DEFAULT 'none'",
132         "targettag VARCHAR(255) DEFAULT 'none'",
133         "xmlmessage TEXT",
134         "module VARCHAR(255) DEFAULT 'none'",
135         "sessionid VARCHAR(255) DEFAULT '0'",
136 );
138 # holds all gosa jobs
139 our $job_db;
140 our $job_queue_tn = 'jobs';
141 my $job_queue_file_name;
142 my @job_queue_col_names = ("id INTEGER PRIMARY KEY",
143         "timestamp VARCHAR(14) DEFAULT 'none'", 
144         "status VARCHAR(255) DEFAULT 'none'", 
145         "result TEXT",
146         "progress VARCHAR(255) DEFAULT 'none'",
147         "headertag VARCHAR(255) DEFAULT 'none'",
148         "targettag VARCHAR(255) DEFAULT 'none'", 
149         "xmlmessage TEXT", 
150         "macaddress VARCHAR(17) DEFAULT 'none'",
151         "plainname VARCHAR(255) DEFAULT 'none'",
152         "siserver VARCHAR(255) DEFAULT 'none'",
153         "modified INTEGER DEFAULT '0'",
154 );
156 # holds all other gosa-si-server
157 our $known_server_db;
158 our $known_server_tn = "known_server";
159 my $known_server_file_name;
160 my @known_server_col_names = ("hostname VARCHAR(255)", "macaddress VARCHAR(17)", "status VARCHAR(255)", "hostkey VARCHAR(255)", "loaded_modules TEXT", "timestamp VARCHAR(14)");
162 # holds all registrated clients
163 our $known_clients_db;
164 our $known_clients_tn = "known_clients";
165 my $known_clients_file_name;
166 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)");
168 # holds all registered clients at a foreign server
169 our $foreign_clients_db;
170 our $foreign_clients_tn = "foreign_clients"; 
171 my $foreign_clients_file_name;
172 my @foreign_clients_col_names = ("hostname VARCHAR(255)", "macaddress VARCHAR(17)", "regserver VARCHAR(255)", "timestamp VARCHAR(14)");
174 # holds all logged in user at each client 
175 our $login_users_db;
176 our $login_users_tn = "login_users";
177 my $login_users_file_name;
178 my @login_users_col_names = ("client VARCHAR(255)", "user VARCHAR(255)", "timestamp VARCHAR(14)", "regserver VARCHAR(255) DEFAULT 'localhost'");
180 # holds all fai server, the debian release and tag
181 our $fai_server_db;
182 our $fai_server_tn = "fai_server"; 
183 my $fai_server_file_name;
184 our @fai_server_col_names = ("timestamp VARCHAR(14)", "server VARCHAR(255)", "fai_release VARCHAR(255)", "sections VARCHAR(255)", "tag VARCHAR(255)"); 
186 our $fai_release_db;
187 our $fai_release_tn = "fai_release"; 
188 my $fai_release_file_name;
189 our @fai_release_col_names = ("timestamp VARCHAR(14)", "fai_release VARCHAR(255)", "class VARCHAR(255)", "type VARCHAR(255)", "state VARCHAR(255)"); 
191 # holds all packages available from different repositories
192 our $packages_list_db;
193 our $packages_list_tn = "packages_list";
194 my $packages_list_file_name;
195 our @packages_list_col_names = ("distribution VARCHAR(255)", "package VARCHAR(255)", "version VARCHAR(255)", "section VARCHAR(255)", "description TEXT", "template LONGBLOB", "timestamp VARCHAR(14)");
196 my $outdir = "/tmp/packages_list_db";
197 my $arch = "i386"; 
199 # holds all messages which should be delivered to a user
200 our $messaging_db;
201 our $messaging_tn = "messaging"; 
202 our @messaging_col_names = ("id INTEGER", "subject TEXT", "message_from VARCHAR(255)", "message_to VARCHAR(255)", 
203         "flag VARCHAR(255)", "direction VARCHAR(255)", "delivery_time VARCHAR(255)", "message TEXT", "timestamp VARCHAR(14)" );
204 my $messaging_file_name;
206 # path to directory to store client install log files
207 our $client_fai_log_dir = "/var/log/fai"; 
209 # queue which stores taskes until one of the $max_children children are ready to process the task
210 #my @tasks = qw();
211 my @msgs_to_decrypt = qw();
212 my $max_children = 2;
214 # Allow 50 POE Childs
215 sub MAX_CONCURRENT_TASKS () { 50 }
217 # loop delay for job queue to look for opsi jobs
218 my $job_queue_opsi_delay = 10;
219 our $opsi_client;
220 our $opsi_url;
221  
222 # Lifetime of logged in user information. If no update information comes after n seconds, 
223 # the user is expeceted to be no longer logged in or the host is no longer running. Because
224 # of this, the user is deleted from login_users_db
225 our $logged_in_user_date_of_expiry = 600;
228 %cfg_defaults = (
229 "general" => {
230     "log-file"      => [\$log_file, "/var/run/".$prg.".log"],
231     "pid-file"      => [\$pid_file, "/var/run/".$prg.".pid"],
232     "repo-basepath" => [\$repo_basepath, "/srv/www"],
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     "ldap-uri"              => [\$ldap_uri, ""],
248     "ldap-base"             => [\$ldap_base, ""],
249     "ldap-admin-dn"         => [\$ldap_admin_dn, ""],
250     "ldap-admin-password"   => [\$ldap_admin_password, ""],
251     "gosa-unit-tag"         => [\$gosa_unit_tag, ""],
252     "max-clients"           => [\$max_clients, 10],
253     "wol-password"          => [\$wake_on_lan_passwd, ""],
254                 "mysql-username"        => [\$mysql_username, "gosa_si"],
255                 "mysql-password"        => [\$mysql_password, ""],
256                 "mysql-database"        => [\$mysql_database, "gosa_si"],
257                 "mysql-host"            => [\$mysql_host, "127.0.0.1"],
258     },
259 "GOsaPackages" => {
260     "job-queue" => [\$job_queue_file_name, '/var/lib/gosa-si/jobs.db'],
261     "job-queue-loop-delay" => [\$job_queue_loop_delay, 3],
262     "messaging-db-loop-delay" => [\$messaging_db_loop_delay, 3],
263     "key" => [\$GosaPackages_key, "none"],
264                 "new-systems-ou" => [\$new_systems_ou, 'ou=workstations,ou=systems'],
265     },
266 "ClientPackages" => {
267     "key" => [\$ClientPackages_key, "none"],
268     "user-date-of-expiry" => [\$logged_in_user_date_of_expiry, 600],
269     },
270 "ServerPackages"=> {
271     "address"      => [\$foreign_server_string, ""],
272     "dns-lookup"            => [\$dns_lookup, "true"],
273     "domain"  => [\$server_domain, ""],
274     "key"     => [\$ServerPackages_key, "none"],
275     "key-lifetime" => [\$foreign_servers_register_delay, 120],
276     "job-synchronization-enabled" => [\$job_synchronization, "true"],
277     "synchronization-loop" => [\$modified_jobs_loop_delay, 5],
278     "report-logged-in-users" => [\$logged_in_users_delay, 600],
279     },
280 "ArpHandler" => {
281     "enabled"   => [\$arp_enabled, "true"],
282     "interface" => [\$arp_interface, "all"],
283         },
284 "Opsi" => {
285     "enabled"  => [\$opsi_enabled, "false"], 
286     "server"   => [\$opsi_server, "localhost"],
287     "admin"    => [\$opsi_admin, "opsi-admin"],
288     "password" => [\$opsi_password, "secret"],
289    },
291 );
294 #===  FUNCTION  ================================================================
295 #         NAME:  usage
296 #   PARAMETERS:  nothing
297 #      RETURNS:  nothing
298 #  DESCRIPTION:  print out usage text to STDERR
299 #===============================================================================
300 sub usage {
301     print STDERR << "EOF" ;
302 usage: $prg [-hvf] [-c config]
304            -h        : this (help) message
305            -c <file> : config file
306            -f        : foreground, process will not be forked to background
307            -v        : be verbose (multiple to increase verbosity)
308            -no-arp   : starts $prg without connection to arp module
309  
310 EOF
311     print "\n" ;
315 #===  FUNCTION  ================================================================
316 #         NAME:  logging
317 #   PARAMETERS:  level - string - default 'info'
318 #                msg - string -
319 #                facility - string - default 'LOG_DAEMON'
320 #      RETURNS:  nothing
321 #  DESCRIPTION:  function for logging
322 #===============================================================================
323 sub daemon_log {
324     # log into log_file
325     my( $msg, $level ) = @_;
326     if(not defined $msg) { return }
327     if(not defined $level) { $level = 1 }
328     if(defined $log_file){
329         my $open_log_fh = sysopen(LOG_HANDLE, $log_file, O_WRONLY | O_CREAT | O_APPEND , 0440);
330         if(not $open_log_fh) {
331             print STDERR "cannot open $log_file: $!";
332             return;
333         }
334         # check owner and group of log_file and update settings if necessary
335         my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = stat($log_file);
336         if((not $uid eq $root_uid) || (not $gid eq $adm_gid)) {
337             chown($root_uid, $adm_gid, $log_file);
338         }
340         chomp($msg);
341         #$msg =~s/\n//g;   # no newlines are allowed in log messages, this is important for later log parsing
342         if($level <= $verbose){
343             my ($seconds, $minutes, $hours, $monthday, $month,
344                     $year, $weekday, $yearday, $sommertime) = localtime(time);
345             $hours = $hours < 10 ? $hours = "0".$hours : $hours;
346             $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
347             $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
348             my @monthnames = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
349             $month = $monthnames[$month];
350             $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
351             $year+=1900;
352             my $name = $prg;
354             my $log_msg = "$month $monthday $hours:$minutes:$seconds $name $msg\n";
355                         flock(LOG_HANDLE, LOCK_EX);
356                         seek(LOG_HANDLE, 0, 2);
357             print LOG_HANDLE $log_msg;
358                         flock(LOG_HANDLE, LOCK_UN);
359             if( $foreground ) { 
360                 print STDERR $log_msg;
361             }
362         }
363         close( LOG_HANDLE );
364     }
368 #===  FUNCTION  ================================================================
369 #         NAME:  check_cmdline_param
370 #   PARAMETERS:  nothing
371 #      RETURNS:  nothing
372 #  DESCRIPTION:  validates commandline parameter
373 #===============================================================================
374 sub check_cmdline_param () {
375     my $err_config;
376     my $err_counter = 0;
377         if(not defined($cfg_file)) {
378                 $cfg_file = "/etc/gosa-si/server.conf";
379                 if(! -r $cfg_file) {
380                         $err_config = "please specify a config file";
381                         $err_counter += 1;
382                 }
383     }
384     if( $err_counter > 0 ) {
385         &usage( "", 1 );
386         if( defined( $err_config)) { print STDERR "$err_config\n"}
387         print STDERR "\n";
388         exit( -1 );
389     }
393 #===  FUNCTION  ================================================================
394 #         NAME:  check_pid
395 #   PARAMETERS:  nothing
396 #      RETURNS:  nothing
397 #  DESCRIPTION:  handels pid processing
398 #===============================================================================
399 sub check_pid {
400     $pid = -1;
401     # Check, if we are already running
402     if( open(LOCK_FILE, "<$pid_file") ) {
403         $pid = <LOCK_FILE>;
404         if( defined $pid ) {
405             chomp( $pid );
406             if( -f "/proc/$pid/stat" ) {
407                 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
408                 if( $stat ) {
409                                         daemon_log("ERROR: Already running",1);
410                     close( LOCK_FILE );
411                     exit -1;
412                 }
413             }
414         }
415         close( LOCK_FILE );
416         unlink( $pid_file );
417     }
419     # create a syslog msg if it is not to possible to open PID file
420     if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
421         my($msg) = "Couldn't obtain lockfile '$pid_file' ";
422         if (open(LOCK_FILE, '<', $pid_file)
423                 && ($pid = <LOCK_FILE>))
424         {
425             chomp($pid);
426             $msg .= "(PID $pid)\n";
427         } else {
428             $msg .= "(unable to read PID)\n";
429         }
430         if( ! ($foreground) ) {
431             openlog( $0, "cons,pid", "daemon" );
432             syslog( "warning", $msg );
433             closelog();
434         }
435         else {
436             print( STDERR " $msg " );
437         }
438         exit( -1 );
439     }
442 #===  FUNCTION  ================================================================
443 #         NAME:  import_modules
444 #   PARAMETERS:  module_path - string - abs. path to the directory the modules 
445 #                are stored
446 #      RETURNS:  nothing
447 #  DESCRIPTION:  each file in module_path which ends with '.pm' and activation 
448 #                state is on is imported by "require 'file';"
449 #===============================================================================
450 sub import_modules {
451     daemon_log(" ", 1);
453     if (not -e $modules_path) {
454         daemon_log("0 ERROR: cannot find directory or directory is not readable: $modules_path", 1);   
455     }
457     opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
458     while (defined (my $file = readdir (DIR))) {
459         if (not $file =~ /(\S*?).pm$/) {
460             next;
461         }
462                 my $mod_name = $1;
464         # ArpHandler switch
465         if( $file =~ /ArpHandler.pm/ ) {
466             if( $arp_enabled eq "false" ) { next; }
467         }
468         
469         eval { require $file; };
470         if ($@) {
471             daemon_log("0 ERROR: gosa-si-server could not load module $file", 1);
472             daemon_log("$@", 1);
473             exit;
474                 } else {
475                         my $info = eval($mod_name.'::get_module_info()');
476                         # Only load module if get_module_info() returns a non-null object
477                         if( $info ) {
478                                 my ($input_address, $input_key, $event_hash) = @{$info};
479                                 $known_modules->{$mod_name} = $info;
480                                 daemon_log("0 INFO: module $mod_name loaded", 5);
481                         }
482                 }
483     }   
485     close (DIR);
488 #===  FUNCTION  ================================================================
489 #         NAME:  password_check
490 #   PARAMETERS:  nothing
491 #      RETURNS:  nothing
492 #  DESCRIPTION:  escalates an critical error if two modules exist which are avaialable by 
493 #                the same password
494 #===============================================================================
495 sub password_check {
496     my $passwd_hash = {};
497     while (my ($mod_name, $mod_info) = each %$known_modules) {
498         my $mod_passwd = @$mod_info[1];
499         if (not defined $mod_passwd) { next; }
500         if (not exists $passwd_hash->{$mod_passwd}) {
501             $passwd_hash->{$mod_passwd} = $mod_name;
503         # escalates critical error
504         } else {
505             &daemon_log("0 ERROR: two loaded modules do have the same password. Please modify the 'key'-parameter in config file");
506             &daemon_log("0 ERROR: module='$mod_name' and module='".$passwd_hash->{$mod_passwd}."'");
507             exit( -1 );
508         }
509     }
513 sub clean_shutdown
515     unlink($pid_file) if (-w $pid_file);
516     unlink($packages_list_under_construction) if (-w $packages_list_under_construction);
519 sub sig_int_or_term_handler
521     my ($signal) = @_;
522     daemon_log("Got SIG${signal} - shutting down gosa-si-server", 1);
523     clean_shutdown();
524     POE::Kernel->signal('gosa-si_server_session', 'KILL');
525     POE::Kernel->signal('TCP_SERVER', 'KILL');
526     return 1;
529 sub sig_warn_handler
531     my @loc = caller(0);
532     daemon_log( "SIGWARN line " . $loc[2] . ": " . $_[0], 1 );
533     return 1;
536 $SIG{'INT'} = \&sig_int_or_term_handler;
537 $SIG{'TERM'} = \&sig_int_or_term_handler;
538 $SIG{'__WARN__'} = \&sig_warn_handler;
539 $SIG{'USR1'} = 'IGNORE';
541 sub check_key_and_xml_validity {
542     my ($crypted_msg, $module_key, $session_id) = @_;
543     my $msg;
544     my $msg_hash;
545     my $error_string;
546     eval{
547         $msg = &decrypt_msg($crypted_msg, $module_key);
549         if ($msg =~ /<xml>/i){
550             $msg =~ s/\s+/ /g;  # just for better daemon_log
551             daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 9);
552             $msg_hash = $xml->XMLin($msg, ForceArray=>1);
554             ##############
555             # check header
556             if( not exists $msg_hash->{'header'} ) { die "no header specified"; }
557             my $header_l = $msg_hash->{'header'};
558             if( (1 > @{$header_l}) || ( ( 'HASH' eq ref @{$header_l}[0]) && (1 > keys %{@{$header_l}[0]}) ) ) { die 'empty header tag'; }
559             if( 1 < @{$header_l} ) { die 'more than one header specified'; }
560             my $header = @{$header_l}[0];
561             if( 0 == length $header) { die 'empty string in header tag'; }
563             ##############
564             # check source
565             if( not exists $msg_hash->{'source'} ) { die "no source specified"; }
566             my $source_l = $msg_hash->{'source'};
567             if( (1 > @{$source_l}) || ( ( 'HASH' eq ref @{$source_l}[0]) && (1 > keys %{@{$source_l}[0]}) ) ) { die 'empty source tag'; }
568             if( 1 < @{$source_l} ) { die 'more than one source specified'; }
569             my $source = @{$source_l}[0];
570             if( 0 == length $source) { die 'source error'; }
572             ##############
573             # check target
574             if( not exists $msg_hash->{'target'} ) { die "no target specified"; }
575             my $target_l = $msg_hash->{'target'};
576             if( (1 > @{$target_l}) || ( ('HASH' eq ref @{$target_l}[0]) && (1 > keys %{@{$target_l}[0]}) ) ) { die 'empty target tag'; }
577         }
578     };
579     if($@) {
580         daemon_log("$session_id ERROR: do not understand the message: $@", 1);
581         $msg = undef;
582         $msg_hash = undef;
583     }
585     return ($msg, $msg_hash);
589 sub check_outgoing_xml_validity {
590     my ($msg, $session_id) = @_;
592     my $msg_hash;
593     eval{
594         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
596         ##############
597         # check header
598         my $header_l = $msg_hash->{'header'};
599         if( 1 != @{$header_l} ) {
600             die 'no or more than one headers specified';
601         }
602         my $header = @{$header_l}[0];
603         if( 0 == length $header) {
604             die 'header has length 0';
605         }
607         ##############
608         # check source
609         my $source_l = $msg_hash->{'source'};
610         if( 1 != @{$source_l} ) {
611             die 'no or more than 1 sources specified';
612         }
613         my $source = @{$source_l}[0];
614         if( 0 == length $source) {
615             die 'source has length 0';
616         }
618                                 # Check if source contains hostname instead of ip address
619                                 if($source =~ /^[a-z][a-z0-9\.]+:\d+$/i) {
620                                                 my ($hostname,$port) = split(/:/, $source);
621                                                 my $ip_address = inet_ntoa(scalar gethostbyname($hostname));
622                                                 if(defined($ip_address) && $ip_address =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/ && $port =~ /^\d+$/) {
623                                                         # Write ip address to $source variable
624                                                         $source = "$ip_address:$port";
625                                                 }
626                                 }
627         unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
628                 $source =~ /^GOSA$/i) {
629             die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
630         }
631         
632         ##############
633         # check target  
634         my $target_l = $msg_hash->{'target'};
635         if( 0 == @{$target_l} ) {
636             die "no targets specified";
637         }
638         foreach my $target (@$target_l) {
639             if( 0 == length $target) {
640                 die "target has length 0";
641             }
642             unless( $target =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
643                     $target =~ /^GOSA$/i ||
644                     $target =~ /^\*$/ ||
645                     $target =~ /KNOWN_SERVER/i ||
646                     $target =~ /JOBDB/i ||
647                     $target =~ /^([0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2})$/i ){
648                 die "target '$target' is not a complete ip-address with port or a valid target name or a mac-address";
649             }
650         }
651     };
652     if($@) {
653         daemon_log("$session_id ERROR: outgoing msg is not gosa-si envelope conform: $@", 1);
654         daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 1);
655         $msg_hash = undef;
656     }
658     return ($msg_hash);
662 sub input_from_known_server {
663     my ($input, $remote_ip, $session_id) = @_ ;  
664     my ($msg, $msg_hash, $module);
666     my $sql_statement= "SELECT * FROM known_server";
667     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
669     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
670         my $host_name = $hit->{hostname};
671         if( not $host_name =~ "^$remote_ip") {
672             next;
673         }
674         my $host_key = $hit->{hostkey};
675         daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 7);
676         daemon_log("$session_id DEBUG: input_from_known_server: host_key: $host_key", 7);
678         # check if module can open msg envelope with module key
679         my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
680         if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
681             daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 7);
682             daemon_log("$@", 8);
683             next;
684         }
685         else {
686             $msg = $tmp_msg;
687             $msg_hash = $tmp_msg_hash;
688             $module = "ServerPackages";
689             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 7);
690             last;
691         }
692     }
694     if( (!$msg) || (!$msg_hash) || (!$module) ) {
695         daemon_log("$session_id DEBUG: Incoming message is not from a known server", 7);
696     }
697   
698     return ($msg, $msg_hash, $module);
702 sub input_from_known_client {
703     my ($input, $remote_ip, $session_id) = @_ ;  
704     my ($msg, $msg_hash, $module);
706     my $sql_statement= "SELECT * FROM known_clients";
707     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
708     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
709         my $host_name = $hit->{hostname};
710         if( not $host_name =~ /^$remote_ip:\d*$/) {
711                 next;
712                 }
713         my $host_key = $hit->{hostkey};
714         &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 7);
715         &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 7);
717         # check if module can open msg envelope with module key
718         ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
720         if( (!$msg) || (!$msg_hash) ) {
721             &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 7);
722             &daemon_log("$@", 8);
723             next;
724         }
725         else {
726             $module = "ClientPackages";
727             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 7);
728             last;
729         }
730     }
732     if( (!$msg) || (!$msg_hash) || (!$module) ) {
733         &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 7);
734     }
736     return ($msg, $msg_hash, $module);
740 sub input_from_unknown_host {
741         no strict "refs";
742         my ($input, $session_id) = @_ ;
743         my ($msg, $msg_hash, $module);
744         my $error_string;
746         my %act_modules = %$known_modules;
748         while( my ($mod, $info) = each(%act_modules)) {
750                 # check a key exists for this module
751                 my $module_key = ${$mod."_key"};
752                 if( not defined $module_key ) {
753                         if( $mod eq 'ArpHandler' ) {
754                                 next;
755                         }
756                         daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
757                         next;
758                 }
759                 daemon_log("$session_id DEBUG: $mod: $module_key", 7);
761                 # check if module can open msg envelope with module key
762                 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
763                 if( (not defined $msg) || (not defined $msg_hash) ) {
764                         next;
765                 } else {
766                         $module = $mod;
767             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 7);
768                         last;
769                 }
770         }
772         if( (!$msg) || (!$msg_hash) || (!$module)) {
773                 daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 7);
774         }
776         return ($msg, $msg_hash, $module);
780 sub create_ciphering {
781     my ($passwd) = @_;
782         if((!defined($passwd)) || length($passwd)==0) {
783                 $passwd = "";
784         }
785     $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
786     my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
787     my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
788     $my_cipher->set_iv($iv);
789     return $my_cipher;
793 sub encrypt_msg {
794     my ($msg, $key) = @_;
795     my $my_cipher = &create_ciphering($key);
796     my $len;
797     {
798             use bytes;
799             $len= 16-length($msg)%16;
800     }
801     $msg = "\0"x($len).$msg;
802     $msg = $my_cipher->encrypt($msg);
803     chomp($msg = &encode_base64($msg));
804     # there are no newlines allowed inside msg
805     $msg=~ s/\n//g;
806     return $msg;
810 sub decrypt_msg {
812     my ($msg, $key) = @_ ;
813     $msg = &decode_base64($msg);
814     my $my_cipher = &create_ciphering($key);
815     $msg = $my_cipher->decrypt($msg); 
816     $msg =~ s/\0*//g;
817     return $msg;
821 sub get_encrypt_key {
822     my ($target) = @_ ;
823     my $encrypt_key;
824     my $error = 0;
826     # target can be in known_server
827     if( not defined $encrypt_key ) {
828         my $sql_statement= "SELECT * FROM known_server WHERE hostname='$target'";
829         my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
830         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
831             my $host_name = $hit->{hostname};
832             if( $host_name ne $target ) {
833                 next;
834             }
835             $encrypt_key = $hit->{hostkey};
836             last;
837         }
838     }
840     # target can be in known_client
841     if( not defined $encrypt_key ) {
842         my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$target'";
843         my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
844         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
845             my $host_name = $hit->{hostname};
846             if( $host_name ne $target ) {
847                 next;
848             }
849             $encrypt_key = $hit->{hostkey};
850             last;
851         }
852     }
854     return $encrypt_key;
858 #===  FUNCTION  ================================================================
859 #         NAME:  open_socket
860 #   PARAMETERS:  PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
861 #                [PeerPort] string necessary if port not appended by PeerAddr
862 #      RETURNS:  socket IO::Socket::INET
863 #  DESCRIPTION:  open a socket to PeerAddr
864 #===============================================================================
865 sub open_socket {
866     my ($PeerAddr, $PeerPort) = @_ ;
867     if(defined($PeerPort)){
868         $PeerAddr = $PeerAddr.":".$PeerPort;
869     }
870     my $socket;
871     $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
872             Porto => "tcp",
873             Type => SOCK_STREAM,
874             Timeout => 5,
875             );
876     if(not defined $socket) {
877         return;
878     }
879 #    &daemon_log("DEBUG: open_socket: $PeerAddr", 7);
880     return $socket;
884 sub send_msg_to_target {
885     my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
886     my $error = 0;
887     my $header;
888     my $timestamp = &get_time();
889     my $new_status;
890     my $act_status;
891     my ($sql_statement, $res);
892   
893     if( $msg_header ) {
894         $header = "'$msg_header'-";
895     } else {
896         $header = "";
897     }
899         # Patch the source ip
900         if($msg =~ /<source>0\.0\.0\.0:\d*?<\/source>/) {
901                 my $remote_ip = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
902                 $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$remote_ip:$2<\/source>/s;
903         }
905     # encrypt xml msg
906     my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
908     # opensocket
909     my $socket = &open_socket($address);
910     if( !$socket ) {
911         daemon_log("$session_id WARNING: cannot send ".$header."msg to $address , host not reachable", 3);
912         $error++;
913     }
914     
915     if( $error == 0 ) {
916         # send xml msg
917         print $socket $crypted_msg."\n";
919         daemon_log("$session_id INFO: send ".$header."msg to $address", 5);
920         daemon_log("$session_id DEBUG: message:\n$msg", 9);
921         
922     }
924     # close socket in any case
925     if( $socket ) {
926         close $socket;
927     }
929     if( $error > 0 ) { $new_status = "down"; }
930     else { $new_status = $msg_header; }
933     # known_clients
934     $sql_statement = "SELECT * FROM $known_clients_tn WHERE hostname='$address'";
935     $res = $known_clients_db->select_dbentry($sql_statement);
936     if( keys(%$res) == 1) {
937         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
938         if ($act_status eq "down" && $new_status eq "down") {
939             $sql_statement = "DELETE FROM known_clients WHERE hostname='$address'";
940             $res = $known_clients_db->del_dbentry($sql_statement);
941             daemon_log("$session_id WARNING: failed 2x to send msg to host '$address', delete host from known_clients", 3);
942         } else { 
943             $sql_statement = "UPDATE known_clients SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
944             $res = $known_clients_db->update_dbentry($sql_statement);
945             if($new_status eq "down"){
946                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
947             } else {
948                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
949             }
950         }
951     }
953     # known_server
954     $sql_statement = "SELECT * FROM $known_server_tn WHERE hostname='$address'";
955     $res = $known_server_db->select_dbentry($sql_statement);
956     if( keys(%$res) == 1) {
957         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
958         if ($act_status eq "down" && $new_status eq "down") {
959             $sql_statement = "DELETE FROM known_server WHERE hostname='$address'";
960             $res = $known_server_db->del_dbentry($sql_statement);
961             daemon_log("$session_id WARNING: failed 2x to send a message to host '$address', delete host from known_server", 3);
963             # Remove the registered clients of the server as well
964             $sql_statement = "DELETE FROM foreign_clients WHERE regserver='$address'";
965             $res = $foreign_clients_db->del_dbentry($sql_statement);
966         } 
967         else { 
968             $sql_statement = "UPDATE known_server SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
969             $res = $known_server_db->update_dbentry($sql_statement);
970             if($new_status eq "down"){
971                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
972             } else {
973                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
974             }
975         }
976     }
977     return $error; 
981 sub update_jobdb_status_for_send_msgs {
982     my ($session_id, $answer, $error) = @_;
983     &daemon_log("$session_id DEBUG: try to update job status", 7); 
984     if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
985         my $jobdb_id = $1;
986     
987         $answer =~ /<header>(.*)<\/header>/;
988         my $job_header = $1;
990         $answer =~ /<target>(.*)<\/target>/;
991         my $job_target = $1;
992             
993         # Sending msg failed
994         if( $error ) {
996             # Set jobs to done, jobs do not need to deliver their message in any case
997             if (($job_header eq "trigger_action_localboot")
998                     ||($job_header eq "trigger_action_lock")
999                     ||($job_header eq "trigger_action_halt") 
1000                     ) {
1001                 my $sql_statement = "UPDATE $job_queue_tn SET status='done' WHERE id=$jobdb_id";
1002                 &daemon_log("$session_id DEBUG: $sql_statement", 7); 
1003                 my $res = $job_db->update_dbentry($sql_statement);
1004                 
1005             # Reactivate jobs, jobs need to deliver their message
1006             } elsif (($job_header eq "trigger_action_activate")
1007                     ||($job_header eq "trigger_action_update")
1008                     ||($job_header eq "trigger_action_reinstall") 
1009                     ||($job_header eq "trigger_activate_new")
1010                     ) {
1011                 &reactivate_job_with_delay($session_id, $job_target, $job_header, 30 );
1013             # For all other messages
1014             } else {
1015                 my $sql_statement = "UPDATE $job_queue_tn ".
1016                     "SET status='error', result='can not deliver msg, please consult log file' ".
1017                     "WHERE id=$jobdb_id";
1018                 &daemon_log("$session_id DEBUG: $sql_statement", 7); 
1019                 my $res = $job_db->update_dbentry($sql_statement);
1020             }
1022         # Sending msg was successful
1023         } else {
1024             # Set jobs localboot, lock, activate, halt, reboot and wake to done
1025             # jobs reinstall, update, inst_update do themself setting to done
1026             if (($job_header eq "trigger_action_localboot")
1027                     ||($job_header eq "trigger_action_lock")
1028                     ||($job_header eq "trigger_action_activate")
1029                     ||($job_header eq "trigger_action_halt") 
1030                     ||($job_header eq "trigger_action_reboot")
1031                     ||($job_header eq "trigger_action_wake")
1032                     ||($job_header eq "trigger_wake")
1033                     ) {
1035                 my $sql_statement = "UPDATE $job_queue_tn ".
1036                     "SET status='done' ".
1037                     "WHERE id=$jobdb_id AND status='processed'";
1038                 &daemon_log("$session_id DEBUG: $sql_statement", 7); 
1039                 my $res = $job_db->update_dbentry($sql_statement);
1040             } else { 
1041                 &daemon_log("$session_id DEBUG: sending message succeed but cannot update job status.", 7); 
1042             } 
1043         } 
1044     } else { 
1045         &daemon_log("$session_id DEBUG: cannot update job status, msg has no jobdb_id-tag: $answer", 7); 
1046     }
1049 sub reactivate_job_with_delay {
1050     my ($session_id, $target, $header, $delay) = @_ ;
1051     # 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
1052     
1053     if (not defined $delay) { $delay = 30 } ;
1054     my $delay_timestamp = &calc_timestamp(&get_time(), "plus", $delay);
1056     my $sql = "UPDATE $job_queue_tn Set timestamp='$delay_timestamp', status='waiting' WHERE (macaddress='$target' AND headertag='$header')"; 
1057     my $res = $job_db->update_dbentry($sql);
1058     daemon_log("$session_id INFO: '$header'-job will be reactivated at '$delay_timestamp' ".
1059             "cause client '$target' is currently not available", 5);
1060     daemon_log("$session_id $sql", 7);                             
1061     return;
1065 sub msg_to_decrypt {
1066         my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
1067         my $session_id = $session->ID;
1068         my ($msg, $msg_hash, $module);
1069         my $error = 0;
1071         # fetch new msg out of @msgs_to_decrypt
1072         my $tmp_next_msg = shift @msgs_to_decrypt;
1073     my ($next_msg, $msg_source) = split(/;/, $tmp_next_msg);
1075         # msg is from a new client or gosa
1076         ($msg, $msg_hash, $module) = &input_from_unknown_host($next_msg, $session_id);
1078         # msg is from a gosa-si-server
1079         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1080                 ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $heap->{'remote_ip'}, $session_id);
1081         }
1082         # msg is from a gosa-si-client
1083         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1084                 ($msg, $msg_hash, $module) = &input_from_known_client($next_msg, $heap->{'remote_ip'}, $session_id);
1085         }
1086         # an error occurred
1087         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1088                 # if an incoming msg could not be decrypted (maybe a wrong key), send client a ping. If the client
1089                 # could not understand a msg from its server the client cause a re-registering process
1090         my $remote_ip = $heap->{'remote_ip'};
1091         my $remote_port = $heap->{'remote_port'};
1092         my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source><target>$msg_source</target></xml>";
1093         my ($test_error, $test_error_string) = &send_msg_to_target($ping_msg, "$msg_source", "dummy-key", "gosa_ping", $session_id);
1095                 daemon_log("$session_id WARNING cannot understand incoming msg, send 'ping'-msg to all host with ip '".$heap->{remote_ip}.
1096                         "' to cause a re-registering of the client if necessary", 3);
1097                 $error++;
1098         }
1101         my $header;
1102         my $target;
1103         my $source;
1104         my $done = 0;
1105         my $sql;
1106         my $res;
1108         # check whether this message should be processed here
1109         if ($error == 0) {
1110                 $header = @{$msg_hash->{'header'}}[0];
1111                 $target = @{$msg_hash->{'target'}}[0];
1112                 $source = @{$msg_hash->{'source'}}[0];
1113                 my $not_found_in_known_clients_db = 0;
1114                 my $not_found_in_known_server_db = 0;
1115                 my $not_found_in_foreign_clients_db = 0;
1116                 my $local_address;
1117                 my $local_mac;
1118                 my ($target_ip, $target_port) = split(':', $target);
1120                 # Determine the local ip address if target is an ip address
1121                 if ($target =~ /^\d+\.\d+\.\d+\.\d+:\d+$/) {
1122                         $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1123                 } else {
1124                         $local_address = $server_address;
1125                 }
1127                 # Determine the local mac address if target is a mac address
1128                 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) {
1129                         my $loc_ip = &get_local_ip_for_remote_ip($heap->{'remote_ip'});
1130                         my $network_interface= &get_interface_for_ip($loc_ip);
1131                         $local_mac = &get_mac_for_interface($network_interface);
1132                 } else {
1133                         $local_mac = $server_mac_address;
1134                 }
1136                 # target and source is equal to GOSA -> process here
1137                 if (not $done) {
1138                         if ($target eq "GOSA" && $source eq "GOSA") {
1139                                 $done = 1;                    
1140                                 &daemon_log("$session_id DEBUG: target and source is 'GOSA' -> process here", 7);
1141                         }
1142                 }
1144                 # target is own address without forward_to_gosa-tag -> process here
1145                 if (not $done) {
1146                         #if ((($target eq $local_address) || ($target eq $local_mac) ) && (not exists $msg_hash->{'forward_to_gosa'})) {
1147                         if (($target eq $local_address) && (not exists $msg_hash->{'forward_to_gosa'})) {
1148                                 $done = 1;
1149                                 if ($source eq "GOSA") {
1150                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1151                                 }
1152                                 &daemon_log("$session_id DEBUG: target is own address without forward_to_gosa-tag -> process here", 7);
1153                         }
1154                 }
1156                 # target is a client address in known_clients -> process here
1157                 if (not $done) {
1158                         $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')"; 
1159                         $res = $known_clients_db->select_dbentry($sql);
1160                         if (keys(%$res) > 0) {
1161                                 $done = 1; 
1162                                 my $hostname = $res->{1}->{'hostname'};
1163                                 $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1164                                 my $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1165                                 if ($source eq "GOSA") {
1166                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1167                                 }
1168                                 &daemon_log("$session_id DEBUG: target is a client address in known_clients -> process here", 7);
1170                         } else {
1171                                 $not_found_in_known_clients_db = 1;
1172                         }
1173                 }
1175                 # target ist own address with forward_to_gosa-tag not pointing to myself -> process here
1176                 if (not $done) {
1177                         my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1178                         my $gosa_at;
1179                         my $gosa_session_id;
1180                         if (($target eq $local_address) && (defined $forward_to_gosa)){
1181                                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1182                                 if ($gosa_at ne $local_address) {
1183                                         $done = 1;
1184                                         &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag not pointing to myself -> process here", 7); 
1185                                 }
1186                         }
1187                 }
1189                 # if message should be processed here -> add message to incoming_db
1190                 if ($done) {
1191                         # if a job or a gosa message comes from a foreign server, fake module to GosaPackages
1192                         # so gosa-si-server knows how to process this kind of messages
1193                         if ($header =~ /^gosa_/ || $header =~ /^job_/) {
1194                                 $module = "GosaPackages";
1195                         }
1197                         my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1198                                         primkey=>[],
1199                                         headertag=>$header,
1200                                         targettag=>$target,
1201                                         xmlmessage=>&encode_base64($msg),
1202                                         timestamp=>&get_time,
1203                                         module=>$module,
1204                                         sessionid=>$session_id,
1205                                 } );
1207                 }
1209                 # target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa
1210                 if (not $done) {
1211                         my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1212                         my $gosa_at;
1213                         my $gosa_session_id;
1214                         if (($target eq $local_address) && (defined $forward_to_gosa)){
1215                                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1216                                 if ($gosa_at eq $local_address) {
1217                                         my $session_reference = $kernel->ID_id_to_session($gosa_session_id);
1218                                         if( defined $session_reference ) {
1219                                                 $heap = $session_reference->get_heap();
1220                                         }
1221                                         if(exists $heap->{'client'}) {
1222                                                 $msg = &encrypt_msg($msg, $GosaPackages_key);
1223                                                 $heap->{'client'}->put($msg);
1224                                                 &daemon_log("$session_id INFO: incoming '$header' message forwarded to GOsa", 5); 
1225                                         }
1226                                         $done = 1;
1227                                         &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa", 7);
1228                                 }
1229                         }
1231                 }
1233                 # target is a client address in foreign_clients -> forward to registration server
1234                 if (not $done) {
1235                         $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1236                         $res = $foreign_clients_db->select_dbentry($sql);
1237                         if (keys(%$res) > 0) {
1238                                 my $hostname = $res->{1}->{'hostname'};
1239                                 my ($host_ip, $host_port) = split(/:/, $hostname);
1240                                 my $local_address =  &get_local_ip_for_remote_ip($host_ip).":$server_port";
1241                                 my $regserver = $res->{1}->{'regserver'};
1242                                 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$regserver'"; 
1243                                 my $res = $known_server_db->select_dbentry($sql);
1244                                 if (keys(%$res) > 0) {
1245                                         my $regserver_key = $res->{1}->{'hostkey'};
1246                                         $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1247                                         $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1248                                         if ($source eq "GOSA") {
1249                                                 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1250                                         }
1251                                         &send_msg_to_target($msg, $regserver, $regserver_key, $header, $session_id);
1252                                 }
1253                                 $done = 1;
1254                                 &daemon_log("$session_id DEBUG: target is a client address in foreign_clients -> forward to registration server", 7);
1255                         } else {
1256                                 $not_found_in_foreign_clients_db = 1;
1257                         }
1258                 }
1260                 # target is a server address -> forward to server
1261                 if (not $done) {
1262                         $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1263                         $res = $known_server_db->select_dbentry($sql);
1264                         if (keys(%$res) > 0) {
1265                                 my $hostkey = $res->{1}->{'hostkey'};
1267                                 if ($source eq "GOSA") {
1268                                         $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1269                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1271                                 }
1273                                 &send_msg_to_target($msg, $target, $hostkey, $header, $session_id);
1274                                 $done = 1;
1275                                 &daemon_log("$session_id DEBUG: target is a server address -> forward to server", 7);
1276                         } else {
1277                                 $not_found_in_known_server_db = 1;
1278                         }
1279                 }
1282                 # target is not in foreign_clients_db, known_server_db or known_clients_db, maybe it is a complete new one -> process here
1283                 if ( $not_found_in_foreign_clients_db 
1284                         && $not_found_in_known_server_db
1285                         && $not_found_in_known_clients_db) {
1286                         &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);
1287             if ($header =~ /^gosa_/ || $header =~ /^job_/) { 
1288                 $module = "GosaPackages"; 
1289             }
1290                         my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1291                                         primkey=>[],
1292                                         headertag=>$header,
1293                                         targettag=>$target,
1294                                         xmlmessage=>&encode_base64($msg),
1295                                         timestamp=>&get_time,
1296                                         module=>$module,
1297                                         sessionid=>$session_id,
1298                                 } );
1299                         $done = 1;
1300                 }
1303                 if (not $done) {
1304                         daemon_log("$session_id ERROR: do not know what to do with this message: $msg", 1);
1305                         if ($source eq "GOSA") {
1306                                 my %data = ('error_msg' => &encode_base64($msg), 'error_string' => "Do not know what to do with this message!");
1307                                 my $error_msg = &build_msg("error", $local_address, "GOSA", \%data ); 
1309                                 my $session_reference = $kernel->ID_id_to_session($session_id);
1310                                 if( defined $session_reference ) {
1311                                         $heap = $session_reference->get_heap();
1312                                 }
1313                                 if(exists $heap->{'client'}) {
1314                                         $error_msg = &encrypt_msg($error_msg, $GosaPackages_key);
1315                                         $heap->{'client'}->put($error_msg);
1316                                 }
1317                         }
1318                 }
1320         }
1322         return;
1326 sub next_task {
1327     my ($session, $heap, $task) = @_[SESSION, HEAP, ARG0];
1328     my $running_task = POE::Wheel::Run->new(
1329             Program => sub { process_task($session, $heap, $task) },
1330             StdioFilter => POE::Filter::Reference->new(),
1331             StdoutEvent  => "task_result",
1332             StderrEvent  => "task_debug",
1333             CloseEvent   => "task_done",
1334             );
1335     $heap->{task}->{ $running_task->ID } = $running_task;
1338 sub handle_task_result {
1339     my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
1340     my $client_answer = $result->{'answer'};
1341     if( $client_answer =~ s/session_id=(\d+)$// ) {
1342         my $session_id = $1;
1343         if( defined $session_id ) {
1344             my $session_reference = $kernel->ID_id_to_session($session_id);
1345             if( defined $session_reference ) {
1346                 $heap = $session_reference->get_heap();
1347             }
1348         }
1350         if(exists $heap->{'client'}) {
1351             $heap->{'client'}->put($client_answer);
1352         }
1353     }
1354     $kernel->sig(CHLD => "child_reap");
1357 sub handle_task_debug {
1358     my $result = $_[ARG0];
1359     print STDERR "$result\n";
1362 sub handle_task_done {
1363     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1364     delete $heap->{task}->{$task_id};
1367 sub process_task {
1368     no strict "refs";
1369     #CHECK: Not @_[...]?
1370     my ($session, $heap, $task) = @_;
1371     my $error = 0;
1372     my $answer_l;
1373     my ($answer_header, @answer_target_l, $answer_source);
1374     my $client_answer = "";
1376     # prepare all variables needed to process message
1377     #my $msg = $task->{'xmlmessage'};
1378     my $msg = &decode_base64($task->{'xmlmessage'});
1379     my $incoming_id = $task->{'id'};
1380     my $module = $task->{'module'};
1381     my $header =  $task->{'headertag'};
1382     my $session_id = $task->{'sessionid'};
1383                 my $msg_hash;
1384                 eval {
1385         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
1386                 }; 
1387                 daemon_log("ERROR: XML failure '$@'") if ($@);
1388     my $source = @{$msg_hash->{'source'}}[0];
1389     
1390     # set timestamp of incoming client uptodate, so client will not 
1391     # be deleted from known_clients because of expiration
1392     my $cur_time = &get_time();
1393     my $sql = "UPDATE $known_clients_tn SET timestamp='$cur_time' WHERE hostname='$source'"; 
1394     my $res = $known_clients_db->exec_statement($sql);
1396     ######################
1397     # process incoming msg
1398     if( $error == 0) {
1399         daemon_log("$session_id INFO: Incoming msg (session_id=$session_id) with header '".@{$msg_hash->{'header'}}[0]."'", 5); 
1400         daemon_log("$session_id DEBUG: Processing module ".$module, 7);
1401         $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1403         if ( 0 < @{$answer_l} ) {
1404             my $answer_str = join("\n", @{$answer_l});
1405             while ($answer_str =~ /<header>(\w+)<\/header>/g) {
1406                 daemon_log("$session_id INFO: got answer message with header '$1'", 5);
1407             }
1408             daemon_log("$session_id DEBUG: $module: got answer from module: \n".$answer_str,9);
1409         } else {
1410             daemon_log("$session_id DEBUG: $module: got no answer from module!" ,7);
1411         }
1413     }
1414     if( !$answer_l ) { $error++ };
1416     ########
1417     # answer
1418     if( $error == 0 ) {
1420         foreach my $answer ( @{$answer_l} ) {
1421             # check outgoing msg to xml validity
1422             my $answer_hash = &check_outgoing_xml_validity($answer, $session_id);
1423             if( not defined $answer_hash ) { next; }
1424             
1425             $answer_header = @{$answer_hash->{'header'}}[0];
1426             @answer_target_l = @{$answer_hash->{'target'}};
1427             $answer_source = @{$answer_hash->{'source'}}[0];
1429             # deliver msg to all targets 
1430             foreach my $answer_target ( @answer_target_l ) {
1432                 # targets of msg are all gosa-si-clients in known_clients_db
1433                 if( $answer_target eq "*" ) {
1434                     # answer is for all clients
1435                     my $sql_statement= "SELECT * FROM known_clients";
1436                     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
1437                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1438                         my $host_name = $hit->{hostname};
1439                         my $host_key = $hit->{hostkey};
1440                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1441                         &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1442                     }
1443                 }
1445                 # targets of msg are all gosa-si-server in known_server_db
1446                 elsif( $answer_target eq "KNOWN_SERVER" ) {
1447                     # answer is for all server in known_server
1448                     my $sql_statement= "SELECT * FROM $known_server_tn";
1449                     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
1450                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1451                         my $host_name = $hit->{hostname};
1452                         my $host_key = $hit->{hostkey};
1453                         $answer =~ s/<target>\S+<\/target>/<target>$host_name<\/target>/g;
1454                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1455                         &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1456                     }
1457                 }
1459                 # target of msg is GOsa
1460                                 elsif( $answer_target eq "GOSA" ) {
1461                                         my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1462                                         my $add_on = "";
1463                     if( defined $session_id ) {
1464                         $add_on = ".session_id=$session_id";
1465                     }
1466                     # answer is for GOSA and has to returned to connected client
1467                     my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1468                     $client_answer = $gosa_answer.$add_on;
1469                 }
1471                 # target of msg is job queue at this host
1472                 elsif( $answer_target eq "JOBDB") {
1473                     $answer =~ /<header>(\S+)<\/header>/;   
1474                     my $header;
1475                     if( defined $1 ) { $header = $1; }
1476                     my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1477                     &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1478                 }
1480                 # Target of msg is a mac address
1481                 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 ) {
1482                     daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients and foreign_clients", 5);
1484                     # Looking for macaddress in known_clients
1485                     my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1486                     my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1487                     my $found_ip_flag = 0;
1488                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1489                         my $host_name = $hit->{hostname};
1490                         my $host_key = $hit->{hostkey};
1491                         $answer =~ s/$answer_target/$host_name/g;
1492                         daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1493                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1494                         &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1495                         $found_ip_flag++ ;
1496                     }   
1498                     # Looking for macaddress in foreign_clients
1499                     if ($found_ip_flag == 0) {
1500                         my $sql = "SELECT * FROM $foreign_clients_tn WHERE macaddress LIKE '$answer_target'";
1501                         my $res = $foreign_clients_db->select_dbentry($sql);
1502                         while( my ($hit_num, $hit) = each %{ $res } ) {
1503                             my $host_name = $hit->{hostname};
1504                             my $reg_server = $hit->{regserver};
1505                             daemon_log("$session_id INFO: found host '$host_name' with mac '$answer_target', registered at '$reg_server'", 5);
1506                             
1507                             # Fetch key for reg_server
1508                             my $reg_server_key;
1509                             my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$reg_server'";
1510                             my $res = $known_server_db->select_dbentry($sql);
1511                             if (exists $res->{1}) {
1512                                 $reg_server_key = $res->{1}->{'hostkey'}; 
1513                             } else {
1514                                 daemon_log("$session_id ERROR: cannot find hostkey for '$host_name' in '$known_server_tn'", 1); 
1515                                 daemon_log("$session_id ERROR: unable to forward answer to correct registration server, processing is aborted!", 1); 
1516                                 $reg_server_key = undef;
1517                             }
1519                             # Send answer to server where client is registered
1520                             if (defined $reg_server_key) {
1521                                 $answer =~ s/$answer_target/$host_name/g;
1522                                 my $error = &send_msg_to_target($answer, $reg_server, $reg_server_key, $answer_header, $session_id);
1523                                 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1524                                 $found_ip_flag++ ;
1525                             }
1526                         }
1527                     }
1529                     # No mac to ip matching found
1530                     if( $found_ip_flag == 0) {
1531                         daemon_log("$session_id WARNING: no host found in known_clients or foreign_clients with mac address '$answer_target'", 3);
1532                         &reactivate_job_with_delay($session_id, $answer_target, $answer_header, 30);
1533                     }
1535                 # Answer is for one specific host   
1536                 } else {
1537                     # get encrypt_key
1538                     my $encrypt_key = &get_encrypt_key($answer_target);
1539                     if( not defined $encrypt_key ) {
1540                         # unknown target
1541                         daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1542                         next;
1543                     }
1544                     my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1545                     &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1546                 }
1547             }
1548         }
1549     }
1551     my $filter = POE::Filter::Reference->new();
1552     my %result = ( 
1553             status => "seems ok to me",
1554             answer => $client_answer,
1555             );
1557     my $output = $filter->put( [ \%result ] );
1558     print @$output;
1563 sub session_start {
1564     my ($kernel) = $_[KERNEL];
1565     $kernel->alias_set('gosa-si_server_session');
1566     $global_kernel = $kernel;
1567     $kernel->yield('register_at_foreign_servers');
1568         $kernel->yield('create_fai_server_db', $fai_server_tn );
1569         $kernel->yield('create_fai_release_db', $fai_release_tn );
1570     $kernel->yield('watch_for_next_tasks');
1571         $kernel->sig(USR2 => "recreate_packages_db");
1572         $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1573         $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay); 
1574     $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay); 
1575         $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1576     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1577         $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1578     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1579     $kernel->delay_set('watch_for_logged_in_users', $logged_in_users_delay);
1581     # Start opsi check
1582     if ($opsi_enabled eq "true") {
1583         $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay); 
1584     }
1587 sub session_stop {
1588     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
1589     $kernel->alias_remove($heap->{alias});
1590     $kernel->alarm_remove_all();
1591     $kernel->post($heap->{child_session}, '_stop');
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', timestamp='".&calc_timestamp(&get_time(), 'plus', 30)."' WHERE macaddress LIKE '$macaddress' AND headertag = 'trigger_action_reinstall'");
1803                                         }
1804                                 }
1805                                 next;
1806                         }
1808                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1809                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1810                                 if(defined($jobdb_id)) {
1811                                         my $job_msg = @{$hits->{$macaddress}->{$jobdb_headertag}}[7];
1813                                         daemon_log("J DEBUG: its time to execute $job_msg", 7);
1814                                         my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1815                                         my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1817                                         # expect macaddress is unique!!!!!!
1818                                         my $target = $res_hash->{1}->{hostname};
1820                                         # change header
1821                                         $job_msg =~ s/<header>job_/<header>gosa_/;
1823                                         # add sqlite_id
1824                                         $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1826                                         $job_msg =~ /<header>(\S+)<\/header>/;
1827                                         my $header = $1 ;
1828                                         my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");                    
1830                                         # update status in job queue to ...
1831                     # ... 'processing', for jobs: 'reinstall', 'update'
1832                     if (($header =~ /gosa_trigger_action_reinstall/) 
1833                             || ($header =~ /gosa_trigger_activate_new/)
1834                             || ($header =~ /gosa_trigger_action_update/)) {
1835                         my $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
1836                         my $dbres = $job_db->update_dbentry($sql_statement);
1837                     }
1839                     # ... 'done', for all other jobs, they are no longer needed in the jobqueue
1840                     else {
1841                         my $sql_statement = "UPDATE $job_queue_tn SET status='done' WHERE id=$jobdb_id";
1842                         my $dbres = $job_db->update_dbentry($sql_statement);
1843                     }
1844                 
1846                                         # We don't want parallel processing
1847                                         last;
1848                                 }
1849                         }
1850                 }
1852                 $watch_for_new_jobs_in_progress = 0;
1853                 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1854         }
1858 sub watch_for_new_messages {
1859     my ($kernel,$heap) = @_[KERNEL, HEAP];
1860     my @coll_user_msg;   # collection list of outgoing messages
1861     
1862     # check messaging_db for new incoming messages with executable timestamp
1863     my $timestamp = &get_time();
1864     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS UNSIGNED))<$timestamp AND flag='n' AND direction='in' )";
1865     my $res = $messaging_db->exec_statement( $sql_statement );
1866         foreach my $hit (@{$res}) {
1868         # create outgoing messages
1869         my $message_to = @{$hit}[3];
1870         # translate message_to to plain login name
1871         my @message_to_l = split(/,/, $message_to);  
1872                 my %receiver_h; 
1873                 foreach my $receiver (@message_to_l) {
1874                         if ($receiver =~ /^u_([\s\S]*)$/) {
1875                                 $receiver_h{$1} = 0;
1876                         } elsif ($receiver =~ /^g_([\s\S]*)$/) {
1877                                 my $group_name = $1;
1878                                 # fetch all group members from ldap and add them to receiver hash
1879                                 my $ldap_handle = &get_ldap_handle();
1880                                 if (defined $ldap_handle) {
1881                                                 my $mesg = $ldap_handle->search(
1882                                                                                 base => $ldap_base,
1883                                                                                 scope => 'sub',
1884                                                                                 attrs => ['memberUid'],
1885                                                                                 filter => "cn=$group_name",
1886                                                                                 );
1887                                                 if ($mesg->count) {
1888                                                                 my @entries = $mesg->entries;
1889                                                                 foreach my $entry (@entries) {
1890                                                                                 my @receivers= $entry->get_value("memberUid");
1891                                                                                 foreach my $receiver (@receivers) { 
1892                                                                                                 $receiver_h{$receiver} = 0;
1893                                                                                 }
1894                                                                 }
1895                                                 } 
1896                                                 # translating errors ?
1897                                                 if ($mesg->code) {
1898                                                                 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: $mesg->error", 1);
1899                                                 }
1900                                 # ldap handle error ?           
1901                                 } else {
1902                                         daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: no ldap handle available", 1);
1903                                 }
1904                         } else {
1905                                 my $sbjct = &encode_base64(@{$hit}[1]);
1906                                 my $msg = &encode_base64(@{$hit}[7]);
1907                                 &daemon_log("M WARNING: unknown receiver '$receiver' for a user-message '$sbjct - $msg'", 3); 
1908                         }
1909                 }
1910                 my @receiver_l = keys(%receiver_h);
1912         my $message_id = @{$hit}[0];
1914         #add each outgoing msg to messaging_db
1915         my $receiver;
1916         foreach $receiver (@receiver_l) {
1917             my $sql_statement = "INSERT INTO $messaging_tn (id, subject, message_from, message_to, flag, direction, delivery_time, message, timestamp) ".
1918                 "VALUES ('".
1919                 $message_id."', '".    # id
1920                 @{$hit}[1]."', '".     # subject
1921                 @{$hit}[2]."', '".     # message_from
1922                 $receiver."', '".      # message_to
1923                 "none"."', '".         # flag
1924                 "out"."', '".          # direction
1925                 @{$hit}[6]."', '".     # delivery_time
1926                 @{$hit}[7]."', '".     # message
1927                 $timestamp."'".     # timestamp
1928                 ")";
1929             &daemon_log("M DEBUG: $sql_statement", 1);
1930             my $res = $messaging_db->exec_statement($sql_statement);
1931             &daemon_log("M INFO: message '".@{$hit}[0]."' is prepared for delivery to receiver '$receiver'", 5);
1932         }
1934         # set incoming message to flag d=deliverd
1935         $sql_statement = "UPDATE $messaging_tn SET flag='p' WHERE id='$message_id'"; 
1936         &daemon_log("M DEBUG: $sql_statement", 7);
1937         $res = $messaging_db->update_dbentry($sql_statement);
1938         &daemon_log("M INFO: message '$message_id' is set to flag 'p' (processed)", 5);
1939     }
1941     $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay); 
1942     return;
1945 sub watch_for_delivery_messages {
1946     my ($kernel, $heap) = @_[KERNEL, HEAP];
1948     # select outgoing messages
1949     my $timestamp= &get_time();
1950     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( flag='p' AND direction='out' AND delivery_time<$timestamp)";
1951     #&daemon_log("0 DEBUG: $sql", 7);
1952     my $res = $messaging_db->exec_statement( $sql_statement );
1953     
1954     # build out msg for each    usr
1955     foreach my $hit (@{$res}) {
1956         my $receiver = @{$hit}[3];
1957         my $msg_id = @{$hit}[0];
1958         my $subject = @{$hit}[1];
1959         my $message = @{$hit}[7];
1961         # resolve usr -> host where usr is logged in
1962         my $sql = "SELECT * FROM $login_users_tn WHERE (user='$receiver')"; 
1963         #&daemon_log("0 DEBUG: $sql", 7);
1964         my $res = $login_users_db->exec_statement($sql);
1966         # receiver is logged in nowhere
1967         if (not ref(@$res[0]) eq "ARRAY") { next; }    
1969         # receiver ist logged in at a client registered at local server
1970                 my $send_succeed = 0;
1971                 foreach my $hit (@$res) {
1972                                 my $receiver_host = @$hit[0];
1973                 my $delivered2host = 0;
1974                                 &daemon_log("M DEBUG: user '$receiver' is logged in at host '$receiver_host'", 7);
1976                                 # Looking for host in know_clients_db 
1977                                 my $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$receiver_host')";
1978                                 my $res = $known_clients_db->exec_statement($sql);
1980                 # Host is known in known_clients_db
1981                 if (ref(@$res[0]) eq "ARRAY") {
1982                     my $receiver_key = @{@{$res}[0]}[2];
1983                     my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
1984                     my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
1985                     my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0); 
1986                     if ($error == 0 ) {
1987                         $send_succeed++ ;
1988                         $delivered2host++ ;
1989                         &daemon_log("M DEBUG: send message for user '$receiver' to host '$receiver_host'", 7); 
1990                     } else {
1991                         &daemon_log("M DEBUG: cannot send message for user '$receiver' to host '$receiver_host'", 7); 
1992                     }
1993                 }
1994                 
1995                 # Message already send, do not need to do anything more, otherwise ...
1996                 if ($delivered2host) { next;}
1997     
1998                 # ...looking for host in foreign_clients_db
1999                 $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$receiver_host')";
2000                 $res = $foreign_clients_db->exec_statement($sql);
2001   
2002                                 # Host is known in foreign_clients_db 
2003                                 if (ref(@$res[0]) eq "ARRAY") { 
2004                     my $registration_server = @{@{$res}[0]}[2];
2005                     
2006                     # Fetch encryption key for registration server
2007                     my $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$registration_server')";
2008                     my $res = $known_server_db->exec_statement($sql);
2009                     if (ref(@$res[0]) eq "ARRAY") { 
2010                         my $registration_server_key = @{@{$res}[0]}[3];
2011                         my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
2012                         my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
2013                         my $error = &send_msg_to_target($out_msg, $registration_server, $registration_server_key, "usr_msg", 0); 
2014                         if ($error == 0 ) {
2015                             $send_succeed++ ;
2016                             $delivered2host++ ;
2017                             &daemon_log("M DEBUG: send message for user '$receiver' to server '$registration_server'", 7); 
2018                         } else {
2019                             &daemon_log("M ERROR: cannot send message for user '$receiver' to server '$registration_server'", 1); 
2020                         }
2022                     } else {
2023                         &daemon_log("M ERROR: host '$receiver_host' is reported to be ".
2024                                 "registrated at server '$registration_server', ".
2025                                 "but no data available in known_server_db ", 1); 
2026                     }
2027                 }
2028                 
2029                 if (not $delivered2host) {
2030                     &daemon_log("M ERROR: unable to send user message to host '$receiver_host'", 1);
2031                 }
2032                 }
2034                 if ($send_succeed) {
2035                                 # set outgoing msg at db to deliverd
2036                                 my $sql = "UPDATE $messaging_tn SET flag='d' WHERE (id='$msg_id' AND direction='out' AND message_to='$receiver')"; 
2037                                 my $res = $messaging_db->exec_statement($sql); 
2038                 &daemon_log("M INFO: send message for user '$receiver' to logged in hosts", 5);
2039                 } else {
2040             &daemon_log("M WARNING: failed to deliver message for user '$receiver'", 3); 
2041         }
2042         }
2044     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay); 
2045     return;
2049 sub watch_for_done_messages {
2050     my ($kernel,$heap) = @_[KERNEL, HEAP];
2052     my $sql = "SELECT * FROM $messaging_tn WHERE (flag='p' AND direction='in')"; 
2053     #&daemon_log("0 DEBUG: $sql", 7);
2054     my $res = $messaging_db->exec_statement($sql); 
2056     foreach my $hit (@{$res}) {
2057         my $msg_id = @{$hit}[0];
2059         my $sql = "SELECT * FROM $messaging_tn WHERE (id='$msg_id' AND direction='out' AND (NOT flag='s'))"; 
2060         #&daemon_log("0 DEBUG: $sql", 7); 
2061         my $res = $messaging_db->exec_statement($sql);
2063         # not all usr msgs have been seen till now
2064         if ( ref(@$res[0]) eq "ARRAY") { next; }
2065         
2066         $sql = "DELETE FROM $messaging_tn WHERE (id='$msg_id')"; 
2067         #&daemon_log("0 DEBUG: $sql", 7);
2068         $res = $messaging_db->exec_statement($sql);
2069     
2070     }
2072     $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay); 
2073     return;
2077 sub watch_for_old_known_clients {
2078     my ($kernel,$heap) = @_[KERNEL, HEAP];
2080     my $sql_statement = "SELECT * FROM $known_clients_tn";
2081     my $res = $known_clients_db->select_dbentry( $sql_statement );
2083     my $cur_time = int(&get_time());
2085     while ( my ($hit_num, $hit) = each %$res) {
2086         my $expired_timestamp = int($hit->{'timestamp'});
2087         $expired_timestamp =~ /(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/;
2088         my $dt = DateTime->new( year   => $1,
2089                 month  => $2,
2090                 day    => $3,
2091                 hour   => $4,
2092                 minute => $5,
2093                 second => $6,
2094                 );
2096         $dt->add( seconds => 2 * int($hit->{'keylifetime'}) );
2097         $expired_timestamp = $dt->ymd('').$dt->hms('');
2098         if ($cur_time > $expired_timestamp) {
2099             my $hostname = $hit->{'hostname'};
2100             my $del_sql = "DELETE FROM $known_clients_tn WHERE hostname='$hostname'"; 
2101             my $del_res = $known_clients_db->exec_statement($del_sql);
2103             &main::daemon_log("0 INFO: timestamp '".$hit->{'timestamp'}."' of client '$hostname' is expired('$expired_timestamp'), client will be deleted from known_clients_db", 5);
2104         }
2106     }
2108     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
2112 sub watch_for_next_tasks {
2113     my ($kernel,$heap) = @_[KERNEL, HEAP];
2115     my $sql = "SELECT * FROM $incoming_tn";
2116     my $res = $incoming_db->select_dbentry($sql);
2117     
2118     while ( my ($hit_num, $hit) = each %$res) {
2119         my $headertag = $hit->{'headertag'};
2120         if ($headertag =~ /^answer_(\d+)/) {
2121             # do not start processing, this message is for a still running POE::Wheel
2122             next;
2123         }
2124         my $message_id = $hit->{'id'};
2125         my $session_id = $hit->{'sessionid'};
2126         &daemon_log("$session_id DEBUG: start processing for message with incoming id: '$message_id'", 7);
2127         $kernel->yield('next_task', $hit);
2129         my $sql = "DELETE FROM $incoming_tn WHERE id=$message_id";
2130         my $res = $incoming_db->exec_statement($sql);
2131     }
2133     $kernel->delay_set('watch_for_next_tasks', 1); 
2136 sub watch_for_logged_in_users {
2137     my ($kernel,$heap) = @_[KERNEL, HEAP];
2139     # Get list of currently logged in users
2140     my $sql = "SELECT * FROM $login_users_tn WHERE regserver='localhost'";
2141     my $res = $main::login_users_db->select_dbentry($sql);
2142     my $msg_hash = &create_xml_hash("information_sharing", $server_address, "KNOWN_SERVER");
2143     while (my ($hit_id, $hit) = each(%$res)) {
2144         &add_content2xml_hash($msg_hash, 'user_db', $hit->{'client'}.";".$hit->{'user'});
2145     }
2146     my $msg = &create_xml_string($msg_hash);
2148     # Inform all other server which users are logged in at clients registered at local server
2149     my $sql_statement= "SELECT * FROM $known_server_tn";
2150     my $query_res = $known_server_db->select_dbentry( $sql_statement );
2151     while( my ($hit_num, $hit) = each %{ $query_res } ) {
2152         my $host_name = $hit->{hostname};
2153         my $host_key = $hit->{hostkey};
2154         $msg =~ s/<target>\S+<\/target>/<target>$host_name<\/target>/g;
2155         # TODO: get session_id
2156         my $error = &send_msg_to_target($msg, $host_name, $host_key, "information_sharing", 0);
2157     }
2159     $kernel->delay_set('watch_for_logged_in_users', $logged_in_users_delay); 
2162 sub get_ldap_handle {
2163         my ($session_id) = @_;
2164         my $heap;
2165         my $ldap_handle;
2167         if (not defined $session_id ) { $session_id = 0 };
2168         if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
2170         if ($session_id == 0) {
2171                 daemon_log("$session_id DEBUG: get_ldap_handle invoked without a session_id, create a new ldap_handle", 7); 
2172                 $ldap_handle = Net::LDAP->new( $ldap_uri );
2173                 if (defined $ldap_handle) {
2174                         $ldap_handle->bind($ldap_admin_dn, password => $ldap_admin_password) or daemon_log("$session_id ERROR: Bind to LDAP $ldap_uri as $ldap_admin_dn failed!"); 
2175                 } else {
2176                         daemon_log("$session_id ERROR: creation of a new LDAP handle failed (ldap_uri '$ldap_uri')");
2177                 }
2179         } else {
2180                 my $session_reference = $global_kernel->ID_id_to_session($session_id);
2181                 if( defined $session_reference ) {
2182                         $heap = $session_reference->get_heap();
2183                 }
2185                 if (not defined $heap) {
2186                         daemon_log("$session_id DEBUG: cannot get heap for session_id '$session_id'", 7); 
2187                         return;
2188                 }
2190                 # TODO: This "if" is nonsense, because it doesn't prove that the
2191                 #       used handle is still valid - or if we've to reconnect...
2192                 #if (not exists $heap->{ldap_handle}) {
2193                         $ldap_handle = Net::LDAP->new( $ldap_uri );
2194                         $ldap_handle->bind($ldap_admin_dn, password => $ldap_admin_password) or daemon_log("$session_id ERROR: Bind to LDAP $ldap_uri as $ldap_admin_dn failed!"); 
2195                         $heap->{ldap_handle} = $ldap_handle;
2196                 #}
2197         }
2198         return $ldap_handle;
2202 sub change_fai_state {
2203     my ($st, $targets, $session_id) = @_;
2204     $session_id = 0 if not defined $session_id;
2205     # Set FAI state to localboot
2206     my %mapActions= (
2207         reboot    => '',
2208         update    => 'softupdate',
2209         localboot => 'localboot',
2210         reinstall => 'install',
2211         rescan    => '',
2212         wake      => '',
2213         memcheck  => 'memcheck',
2214         sysinfo   => 'sysinfo',
2215         install   => 'install',
2216     );
2218     # Return if this is unknown
2219     if (!exists $mapActions{ $st }){
2220         daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1); 
2221       return;
2222     }
2224     my $state= $mapActions{ $st };
2226     my $ldap_handle = &get_ldap_handle($session_id);
2227     if( defined($ldap_handle) ) {
2229       # Build search filter for hosts
2230         my $search= "(&(objectClass=GOhard)";
2231         foreach (@{$targets}){
2232             $search.= "(macAddress=$_)";
2233         }
2234         $search.= ")";
2236       # If there's any host inside of the search string, procress them
2237         if (!($search =~ /macAddress/)){
2238             daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);    
2239             return;
2240         }
2242       # Perform search for Unit Tag
2243       my $mesg = $ldap_handle->search(
2244           base   => $ldap_base,
2245           scope  => 'sub',
2246           attrs  => ['dn', 'FAIstate', 'objectClass'],
2247           filter => "$search"
2248           );
2250           if ($mesg->count) {
2251                   my @entries = $mesg->entries;
2252                   if (0 == @entries) {
2253                                   daemon_log("$session_id ERROR: ldap search failed: ldap_base=$ldap_base, filter=$search", 1); 
2254                   }
2256                   foreach my $entry (@entries) {
2257                           # Only modify entry if it is not set to '$state'
2258                           if ($entry->get_value("FAIstate") ne "$state"){
2259                                   daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
2260                                   my $result;
2261                                   my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
2262                                   if (exists $tmp{'FAIobject'}){
2263                                           if ($state eq ''){
2264                                                   $result= $ldap_handle->modify($entry->dn, changes => [ delete => [ FAIstate => [] ] ]);
2265                                           } else {
2266                                                   $result= $ldap_handle->modify($entry->dn, changes => [ replace => [ FAIstate => $state ] ]);
2267                                           }
2268                                   } elsif ($state ne ''){
2269                                           $result= $ldap_handle->modify($entry->dn, changes => [ add => [ objectClass => 'FAIobject' ], add => [ FAIstate => $state ] ]);
2270                                   }
2272                                   # Errors?
2273                                   if ($result->code){
2274                                           daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2275                                   }
2276                           } else {
2277                                   daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 7); 
2278                           }  
2279                   }
2280           } else {
2281                 daemon_log("$session_id ERROR: LDAP search failed: ldap_base=$ldap_base, filter=$search", 1);
2282           }
2284     # if no ldap handle defined
2285     } else {
2286         daemon_log("$session_id ERROR: no LDAP handle defined for update FAIstate", 1); 
2287     }
2289         return;
2293 sub change_goto_state {
2294     my ($st, $targets, $session_id) = @_;
2295     $session_id = 0  if not defined $session_id;
2297     # Switch on or off?
2298     my $state= $st eq 'active' ? 'active': 'locked';
2300     my $ldap_handle = &get_ldap_handle($session_id);
2301     if( defined($ldap_handle) ) {
2303       # Build search filter for hosts
2304       my $search= "(&(objectClass=GOhard)";
2305       foreach (@{$targets}){
2306         $search.= "(macAddress=$_)";
2307       }
2308       $search.= ")";
2310       # If there's any host inside of the search string, procress them
2311       if (!($search =~ /macAddress/)){
2312         return;
2313       }
2315       # Perform search for Unit Tag
2316       my $mesg = $ldap_handle->search(
2317           base   => $ldap_base,
2318           scope  => 'sub',
2319           attrs  => ['dn', 'gotoMode'],
2320           filter => "$search"
2321           );
2323       if ($mesg->count) {
2324         my @entries = $mesg->entries;
2325         foreach my $entry (@entries) {
2327           # Only modify entry if it is not set to '$state'
2328           if ($entry->get_value("gotoMode") ne $state){
2330             daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
2331             my $result;
2332             $result= $ldap_handle->modify($entry->dn, changes => [replace => [ gotoMode => $state ] ]);
2334             # Errors?
2335             if ($result->code){
2336               &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2337             }
2339           }
2340         }
2341       } else {
2342                 daemon_log("$session_id ERROR: LDAP search failed in function change_goto_state: ldap_base=$ldap_base, filter=$search", 1);
2343           }
2345     }
2349 sub run_recreate_packages_db {
2350     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2351     my $session_id = $session->ID;
2352         &main::daemon_log("$session_id INFO: Recreating FAI Packages DB ('$fai_release_tn', '$fai_server_tn', '$packages_list_tn')", 5);
2353         $kernel->yield('create_fai_release_db', $fai_release_tn);
2354         $kernel->yield('create_fai_server_db', $fai_server_tn);
2355         return;
2359 sub run_create_fai_server_db {
2360     my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2361     my $session_id = $session->ID;
2362     my $task = POE::Wheel::Run->new(
2363             Program => sub { &create_fai_server_db($table_name,$kernel, undef, $session_id) },
2364             StdoutEvent  => "session_run_result",
2365             StderrEvent  => "session_run_debug",
2366             CloseEvent   => "session_run_done",
2367             );
2369     $heap->{task}->{ $task->ID } = $task;
2370     return;
2374 sub create_fai_server_db {
2375         my ($table_name, $kernel, $dont_create_packages_list, $session_id) = @_;
2376         my $result;
2378         if (not defined $session_id) { $session_id = 0; }
2379         my $ldap_handle = &get_ldap_handle();
2380         if(defined($ldap_handle)) {
2381                 daemon_log("$session_id INFO: create_fai_server_db: start", 5);
2382                 my $mesg= $ldap_handle->search(
2383                         base   => $ldap_base,
2384                         scope  => 'sub',
2385                         attrs  => ['FAIrepository', 'gosaUnitTag'],
2386                         filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
2387                 );
2388                 if($mesg->{'resultCode'} == 0 &&
2389                         $mesg->count != 0) {
2390                         foreach my $entry (@{$mesg->{entries}}) {
2391                                 if($entry->exists('FAIrepository')) {
2392                                         # Add an entry for each Repository configured for server
2393                                         foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
2394                                                 my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
2395                                                 my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
2396                                                 $result= $fai_server_db->add_dbentry( { 
2397                                                                 table => $table_name,
2398                                                                 primkey => ['server', 'fai_release', 'tag'],
2399                                                                 server => $tmp_url,
2400                                                                 fai_release => $tmp_release,
2401                                                                 sections => $tmp_sections,
2402                                                                 tag => (length($tmp_tag)>0)?$tmp_tag:"",
2403                                                         } );
2404                                         }
2405                                 }
2406                         }
2407                 }
2408                 daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
2410                 # TODO: Find a way to post the 'create_packages_list_db' event
2411                 if(not defined($dont_create_packages_list)) {
2412                         &create_packages_list_db(undef, undef, $session_id);
2413                 }
2414         }       
2416         $ldap_handle->disconnect;
2417         return $result;
2421 sub run_create_fai_release_db {
2422         my ($session, $heap, $table_name) = @_[SESSION, HEAP, ARG0];
2423         my $session_id = $session->ID;
2424         my $task = POE::Wheel::Run->new(
2425                 Program => sub { &create_fai_release_db($table_name, $session_id) },
2426                 StdoutEvent  => "session_run_result",
2427                 StderrEvent  => "session_run_debug",
2428                 CloseEvent   => "session_run_done",
2429         );
2431         $heap->{task}->{ $task->ID } = $task;
2432         return;
2436 sub create_fai_release_db {
2437         my ($table_name, $session_id) = @_;
2438         my $result;
2440         # used for logging
2441         if (not defined $session_id) { $session_id = 0; }
2443         my $ldap_handle = &get_ldap_handle();
2444         if(defined($ldap_handle)) {
2445                 daemon_log("$session_id INFO: create_fai_release_db: start",5);
2446                 my $mesg= $ldap_handle->search(
2447                         base   => $ldap_base,
2448                         scope  => 'sub',
2449                         attrs  => [],
2450                         filter => "(&(objectClass=organizationalUnit)(ou=fai))",
2451                 );
2452                 if(($mesg->code == 0) && ($mesg->count != 0))
2453                 {
2454                         daemon_log("$session_id DEBUG: create_fai_release_db: count " . $mesg->count,8);
2456                         # Walk through all possible FAI container ou's
2457                         my @sql_list;
2458                         my $timestamp= &get_time();
2459                         foreach my $ou (@{$mesg->{entries}}) {
2460                                 my $tmp_classes= resolve_fai_classes($ou->dn, $ldap_handle, $session_id);
2461                                 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
2462                                         my @tmp_array=get_fai_release_entries($tmp_classes);
2463                                         if(@tmp_array) {
2464                                                 foreach my $entry (@tmp_array) {
2465                                                         if(defined($entry) && ref($entry) eq 'HASH') {
2466                                                                 my $sql= 
2467                                                                 "INSERT INTO $table_name "
2468                                                                 ."(timestamp, fai_release, class, type, state) VALUES ("
2469                                                                 .$timestamp.","
2470                                                                 ."'".$entry->{'release'}."',"
2471                                                                 ."'".$entry->{'class'}."',"
2472                                                                 ."'".$entry->{'type'}."',"
2473                                                                 ."'".$entry->{'state'}."')";
2474                                                                 push @sql_list, $sql;
2475                                                         }
2476                                                 }
2477                                         }
2478                                 }
2479                         }
2481                         daemon_log("$session_id DEBUG: create_fai_release_db: Inserting ".scalar @sql_list." entries to DB",8);
2482                         if(@sql_list) {
2483                                 unshift @sql_list, "VACUUM";
2484                                 unshift @sql_list, "DELETE FROM $table_name";
2485                                 $fai_release_db->exec_statementlist(\@sql_list);
2486                         }
2487                         daemon_log("$session_id DEBUG: create_fai_release_db: Done with inserting",7);
2488                 } else {
2489                         daemon_log("$session_id INFO: create_fai_release_db: error: " . $mesg->code, 5);
2490                 }
2491                 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
2492         }
2493         $ldap_handle->disconnect;
2494         return $result;
2497 sub get_fai_types {
2498         my $tmp_classes = shift || return undef;
2499         my @result;
2501         foreach my $type(keys %{$tmp_classes}) {
2502                 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
2503                         my $entry = {
2504                                 type => $type,
2505                                 state => $tmp_classes->{$type}[0],
2506                         };
2507                         push @result, $entry;
2508                 }
2509         }
2511         return @result;
2514 sub get_fai_state {
2515         my $result = "";
2516         my $tmp_classes = shift || return $result;
2518         foreach my $type(keys %{$tmp_classes}) {
2519                 if(defined($tmp_classes->{$type}[0])) {
2520                         $result = $tmp_classes->{$type}[0];
2521                         
2522                 # State is equal for all types in class
2523                         last;
2524                 }
2525         }
2527         return $result;
2530 sub resolve_fai_classes {
2531         my ($fai_base, $ldap_handle, $session_id) = @_;
2532         if (not defined $session_id) { $session_id = 0; }
2533         my $result;
2534         my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
2535         my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
2536         my $fai_classes;
2538         daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base",7);
2539         my $mesg= $ldap_handle->search(
2540                 base   => $fai_base,
2541                 scope  => 'sub',
2542                 attrs  => ['cn','objectClass','FAIstate'],
2543                 filter => $fai_filter,
2544         );
2545         daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries",7);
2547         if($mesg->{'resultCode'} == 0 &&
2548                 $mesg->count != 0) {
2549                 foreach my $entry (@{$mesg->{entries}}) {
2550                         if($entry->exists('cn')) {
2551                                 my $tmp_dn= $entry->dn();
2552                                 $tmp_dn= substr( $tmp_dn, 0, length($tmp_dn)
2553                                         - length($fai_base) - 1 );
2555                                 # Skip classname and ou dn parts for class
2556                                 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?)$/;
2558                                 # Skip classes without releases
2559                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2560                                         next;
2561                                 }
2563                                 my $tmp_cn= $entry->get_value('cn');
2564                                 my $tmp_state= $entry->get_value('FAIstate');
2566                                 my $tmp_type;
2567                                 # Get FAI type
2568                                 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
2569                                         if(grep $_ eq $oclass, @possible_fai_classes) {
2570                                                 $tmp_type= $oclass;
2571                                                 last;
2572                                         }
2573                                 }
2575                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2576                                         # A Subrelease
2577                                         my @sub_releases = split(/,/, $tmp_release);
2579                                         # Walk through subreleases and build hash tree
2580                                         my $hash;
2581                                         while(my $tmp_sub_release = pop @sub_releases) {
2582                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2583                                         }
2584                                         eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
2585                                 } else {
2586                                         # A branch, no subrelease
2587                                         push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
2588                                 }
2589                         } elsif (!$entry->exists('cn')) {
2590                                 my $tmp_dn= $entry->dn();
2591                                 $tmp_dn= substr( $tmp_dn, 0, length($tmp_dn)
2592                                         - length($fai_base) - 1 );
2593                                 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?)$/;
2595                                 # Skip classes without releases
2596                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2597                                         next;
2598                                 }
2600                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2601                                         # A Subrelease
2602                                         my @sub_releases= split(/,/, $tmp_release);
2604                                         # Walk through subreleases and build hash tree
2605                                         my $hash;
2606                                         while(my $tmp_sub_release = pop @sub_releases) {
2607                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2608                                         }
2609                                         # Remove the last two characters
2610                                         chop($hash);
2611                                         chop($hash);
2613                                         eval('$fai_classes->'.$hash.'= {}');
2614                                 } else {
2615                                         # A branch, no subrelease
2616                                         if(!exists($fai_classes->{$tmp_release})) {
2617                                                 $fai_classes->{$tmp_release} = {};
2618                                         }
2619                                 }
2620                         }
2621                 }
2623                 # The hash is complete, now we can honor the copy-on-write based missing entries
2624                 foreach my $release (keys %$fai_classes) {
2625                         $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
2626                 }
2627         }
2628         return $result;
2631 sub apply_fai_inheritance {
2632        my $fai_classes = shift || return {};
2633        my $tmp_classes;
2635        # Get the classes from the branch
2636        foreach my $class (keys %{$fai_classes}) {
2637                # Skip subreleases
2638                if($class =~ /^ou=.*$/) {
2639                        next;
2640                } else {
2641                        $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
2642                }
2643        }
2645        # Apply to each subrelease
2646        foreach my $subrelease (keys %{$fai_classes}) {
2647                if($subrelease =~ /ou=/) {
2648                        foreach my $tmp_class (keys %{$tmp_classes}) {
2649                                if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
2650                                        $fai_classes->{$subrelease}->{$tmp_class} =
2651                                        deep_copy($tmp_classes->{$tmp_class});
2652                                } else {
2653                                        foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
2654                                                if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
2655                                                        $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
2656                                                        deep_copy($tmp_classes->{$tmp_class}->{$type});
2657                                                }
2658                                        }
2659                                }
2660                        }
2661                }
2662        }
2664        # Find subreleases in deeper levels
2665        foreach my $subrelease (keys %{$fai_classes}) {
2666                if($subrelease =~ /ou=/) {
2667                        foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
2668                                if($subsubrelease =~ /ou=/) {
2669                                        apply_fai_inheritance($fai_classes->{$subrelease});
2670                                }
2671                        }
2672                }
2673        }
2675        return $fai_classes;
2678 sub get_fai_release_entries {
2679         my $tmp_classes = shift || return;
2680         my $parent = shift || "";
2681         my @result = shift || ();
2683         foreach my $entry (keys %{$tmp_classes}) {
2684                 if(defined($entry)) {
2685                         if($entry =~ /^ou=.*$/) {
2686                                 my $release_name = $entry;
2687                                 $release_name =~ s/ou=//g;
2688                                 if(length($parent)>0) {
2689                                         $release_name = $parent."/".$release_name;
2690                                 }
2691                                 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2692                                 foreach my $bufentry(@bufentries) {
2693                                         push @result, $bufentry;
2694                                 }
2695                         } else {
2696                                 my @types = get_fai_types($tmp_classes->{$entry});
2697                                 foreach my $type (@types) {
2698                                         push @result, 
2699                                         {
2700                                                 'class' => $entry,
2701                                                 'type' => $type->{'type'},
2702                                                 'release' => $parent,
2703                                                 'state' => $type->{'state'},
2704                                         };
2705                                 }
2706                         }
2707                 }
2708         }
2710         return @result;
2713 sub deep_copy {
2714         my $this = shift;
2715         if (not ref $this) {
2716                 $this;
2717         } elsif (ref $this eq "ARRAY") {
2718                 [map deep_copy($_), @$this];
2719         } elsif (ref $this eq "HASH") {
2720                 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2721         } else { die "what type is $_?" }
2725 sub session_run_result {
2726     my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];    
2727     $kernel->sig(CHLD => "child_reap");
2730 sub session_run_debug {
2731     my $result = $_[ARG0];
2732     print STDERR "$result\n";
2735 sub session_run_done {
2736     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2737     delete $heap->{task}->{$task_id};
2741 sub create_sources_list {
2742         my $session_id = shift;
2743         my $ldap_handle = &main::get_ldap_handle;
2744         my $result="/tmp/gosa_si_tmp_sources_list";
2746         # Remove old file
2747         if(stat($result)) {
2748                 unlink($result);
2749                 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7); 
2750         }
2752         my $fh;
2753         open($fh, ">$result");
2754         if (not defined $fh) {
2755                 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7); 
2756                 return undef;
2757         }
2758         if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2759                 my $mesg=$ldap_handle->search(
2760                         base    => $main::ldap_server_dn,
2761                         scope   => 'base',
2762                         attrs   => 'FAIrepository',
2763                         filter  => 'objectClass=FAIrepositoryServer'
2764                 );
2765                 if($mesg->count) {
2766                         foreach my $entry(@{$mesg->{'entries'}}) {
2767                                 foreach my $value(@{$entry->get_value('FAIrepository', asref => 1)}) {
2768                                         my ($server, $tag, $release, $sections)= split /\|/, $value;
2769                                         my $line = "deb $server $release";
2770                                         $sections =~ s/,/ /g;
2771                                         $line.= " $sections";
2772                                         print $fh $line."\n";
2773                                 }
2774                         }
2775                 }
2776         } else {
2777                 if (defined $main::ldap_server_dn){
2778                         &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1); 
2779                 } else {
2780                         &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2781                 }
2782         }
2783         close($fh);
2785         return $result;
2789 sub run_create_packages_list_db {
2790     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2791         my $session_id = $session->ID;
2793         my $task = POE::Wheel::Run->new(
2794                                         Priority => +20,
2795                                         Program => sub {&create_packages_list_db(undef, undef, $session_id)},
2796                                         StdoutEvent  => "session_run_result",
2797                                         StderrEvent  => "session_run_debug",
2798                                         CloseEvent   => "session_run_done",
2799                                         );
2800         $heap->{task}->{ $task->ID } = $task;
2804 sub create_packages_list_db {
2805         my ($ldap_handle, $sources_file, $session_id) = @_;
2807         # Cleanup repo basepath
2808         $repo_basepath = File::Spec->canonpath($repo_basepath);
2810         # it should not be possible to trigger a recreation of packages_list_db
2811         # while packages_list_db is under construction, so set flag packages_list_under_construction
2812         # which is tested befor recreation can be started
2813         if (-r $packages_list_under_construction) {
2814                 daemon_log("$session_id WARNING: packages_list_db is right now under construction, please wait until this process is finished", 3);
2815                 return;
2816         } else {
2817                 daemon_log("$session_id INFO: create_packages_list_db: start", 5); 
2818                 # set packages_list_under_construction to true
2819                 system("touch $packages_list_under_construction");
2820                 @packages_list_statements=();
2821         }
2823         if (not defined $session_id) { $session_id = 0; }
2824         if (not defined $ldap_handle) { 
2825                 $ldap_handle= &get_ldap_handle();
2827                 if (not defined $ldap_handle) {
2828                         daemon_log("$session_id ERROR: no ldap_handle available to create_packages_list_db", 1);
2829                         unlink($packages_list_under_construction);
2830                         return;
2831                 }
2832         }
2833         if (not defined $sources_file) { 
2834                 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5); 
2835                 $sources_file = &create_sources_list($session_id);
2836         }
2838         if (not defined $sources_file) {
2839                 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1); 
2840                 unlink($packages_list_under_construction);
2841                 return;
2842         }
2844         my $line;
2846         open(CONFIG, "<$sources_file") or do {
2847                 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2848                 unlink($packages_list_under_construction);
2849                 return;
2850         };
2852         # Read lines
2853         while ($line = <CONFIG>){
2854                 # Unify
2855                 chop($line);
2856                 $line =~ s/^\s+//;
2857                 $line =~ s/^\s+/ /;
2859                 # Strip comments
2860                 $line =~ s/#.*$//g;
2862                 # Skip empty lines
2863                 if ($line =~ /^\s*$/){
2864                         next;
2865                 }
2867                 # Interpret deb line
2868                 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2869                         my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2870                         my $section;
2871                         foreach $section (split(' ', $sections)){
2872                                 &parse_package_info( $baseurl, $dist, $section, $session_id );
2873                         }
2874                 }
2875         }
2877         close (CONFIG);
2879         if(keys(%repo_dirs)) {
2880                 find(\&cleanup_and_extract, keys( %repo_dirs ));
2881                 &main::strip_packages_list_statements();
2882                 $packages_list_db->exec_statementlist(\@packages_list_statements);
2883         }
2884         unlink($packages_list_under_construction);
2885         daemon_log("$session_id INFO: create_packages_list_db: finished", 5); 
2886         return;
2889 # This function should do some intensive task to minimize the db-traffic
2890 sub strip_packages_list_statements {
2891         my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2892         my @new_statement_list=();
2893         my $hash;
2894         my $insert_hash;
2895         my $update_hash;
2896         my $delete_hash;
2897         my $known_packages_hash;
2898         my $local_timestamp=get_time();
2900         foreach my $existing_entry (@existing_entries) {
2901                 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
2902         }
2904         foreach my $statement (@packages_list_statements) {
2905                 if($statement =~ /^INSERT/i) {
2906                         # Assign the values from the insert statement
2907                         my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~ 
2908                         /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
2909                         if(exists($hash->{$distribution}->{$package}->{$version})) {
2910                                 # If section or description has changed, update the DB
2911                                 if( 
2912                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or 
2913                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
2914                                 ) {
2915                                         @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
2916                                 } else {
2917                                         # package is already present in database. cache this knowledge for later use
2918                                         @{$known_packages_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2919                                 }
2920                         } else {
2921                                 # Insert a non-existing entry to db
2922                                 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2923                         }
2924                 } elsif ($statement =~ /^UPDATE/i) {
2925                         my ($template,$package,$version) = ($1,$2,$3) if $statement =~
2926                         /^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;
2927                         foreach my $distribution (keys %{$hash}) {
2928                                 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
2929                                         # update the insertion hash to execute only one query per package (insert instead insert+update)
2930                                         @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
2931                                 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
2932                                         if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
2933                                                 my $section;
2934                                                 my $description;
2935                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
2936                                                         length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
2937                                                         $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
2938                                                 }
2939                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2940                                                         $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
2941                                                 }
2942                                                 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2943                                         }
2944                                 }
2945                         }
2946                 }
2947         }
2949         # Check for orphaned entries
2950         foreach my $existing_entry (@existing_entries) {
2951                 my $distribution= @{$existing_entry}[0];
2952                 my $package= @{$existing_entry}[1];
2953                 my $version= @{$existing_entry}[2];
2954                 my $section= @{$existing_entry}[3];
2956                 if(
2957                         exists($insert_hash->{$distribution}->{$package}->{$version}) ||
2958                         exists($update_hash->{$distribution}->{$package}->{$version}) ||
2959                         exists($known_packages_hash->{$distribution}->{$package}->{$version})
2960                 ) {
2961                         next;
2962                 } else {
2963                         # Insert entry to delete hash
2964                         @{$delete_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section);
2965                 }
2966         }
2968         # unroll the insert hash
2969         foreach my $distribution (keys %{$insert_hash}) {
2970                 foreach my $package (keys %{$insert_hash->{$distribution}}) {
2971                         foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
2972                                 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
2973                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
2974                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
2975                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
2976                                 ."'$local_timestamp')";
2977                         }
2978                 }
2979         }
2981         # unroll the update hash
2982         foreach my $distribution (keys %{$update_hash}) {
2983                 foreach my $package (keys %{$update_hash->{$distribution}}) {
2984                         foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
2985                                 my $set = "";
2986                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
2987                                         $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
2988                                 }
2989                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2990                                         $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
2991                                 }
2992                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
2993                                         $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
2994                                 }
2995                                 if(defined($set) and length($set) > 0) {
2996                                         $set .= "timestamp = '$local_timestamp'";
2997                                 } else {
2998                                         next;
2999                                 }
3000                                 push @new_statement_list, 
3001                                 "UPDATE $main::packages_list_tn SET $set WHERE"
3002                                 ." distribution = '$distribution'"
3003                                 ." AND package = '$package'"
3004                                 ." AND version = '$version'";
3005                         }
3006                 }
3007         }
3008         
3009         # unroll the delete hash
3010         foreach my $distribution (keys %{$delete_hash}) {
3011                 foreach my $package (keys %{$delete_hash->{$distribution}}) {
3012                         foreach my $version (keys %{$delete_hash->{$distribution}->{$package}}) {
3013                                 my $section = @{$delete_hash->{$distribution}->{$package}->{$version}}[3];
3014                                 push @new_statement_list, "DELETE FROM $main::packages_list_tn WHERE distribution='$distribution' AND package='$package' AND version='$version' AND section='$section'";
3015                         }
3016                 }
3017         }
3019         unshift(@new_statement_list, "VACUUM");
3021         @packages_list_statements = @new_statement_list;
3025 sub parse_package_info {
3026     my ($baseurl, $dist, $section, $session_id)= @_;
3027     my ($package);
3028     if (not defined $session_id) { $session_id = 0; }
3029     my ($path) = ($baseurl =~ m%://[^/]*/(.*)$%);
3031     my $repo_path = File::Spec->join($repo_basepath, $path);
3033     $repo_dirs{ "${repo_path}/pool" } = 1;
3035     foreach $package ("Packages.gz"){
3036         daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
3037         get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
3038         parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
3039     }
3040     
3044 sub get_package {
3045     my ($url, $dest, $session_id)= @_;
3046     if (not defined $session_id) { $session_id = 0; }
3048     my $tpath = dirname($dest);
3049     -d "$tpath" || mkpath "$tpath";
3051     # This is ugly, but I've no time to take a look at "how it works in perl"
3052     if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
3053         system("gunzip -cd '$dest' > '$dest.in'");
3054         daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 5);
3055         unlink($dest);
3056         daemon_log("$session_id DEBUG: delete file '$dest'", 5); 
3057     } else {
3058         daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' into '$dest' failed!", 1);
3059     }
3060     return 0;
3064 sub parse_package {
3065     my ($path, $dist, $srv_path, $session_id)= @_;
3066     if (not defined $session_id) { $session_id = 0;}
3067     my ($package, $version, $section, $description);
3068     my $PACKAGES;
3069     my $timestamp = &get_time();
3071     if(not stat("$path.in")) {
3072         daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
3073         return;
3074     }
3076     open($PACKAGES, "<$path.in");
3077     if(not defined($PACKAGES)) {
3078         daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1); 
3079         return;
3080     }
3082     # Read lines
3083     while (<$PACKAGES>){
3084         my $line = $_;
3085         # Unify
3086         chop($line);
3088         # Use empty lines as a trigger
3089         if ($line =~ /^\s*$/){
3090             my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
3091             push(@packages_list_statements, $sql);
3092             $package = "none";
3093             $version = "none";
3094             $section = "none";
3095             $description = "none"; 
3096             next;
3097         }
3099         # Trigger for package name
3100         if ($line =~ /^Package:\s/){
3101             ($package)= ($line =~ /^Package: (.*)$/);
3102             next;
3103         }
3105         # Trigger for version
3106         if ($line =~ /^Version:\s/){
3107             ($version)= ($line =~ /^Version: (.*)$/);
3108             next;
3109         }
3111         # Trigger for description
3112         if ($line =~ /^Description:\s/){
3113             ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
3114             next;
3115         }
3117         # Trigger for section
3118         if ($line =~ /^Section:\s/){
3119             ($section)= ($line =~ /^Section: (.*)$/);
3120             next;
3121         }
3123         # Trigger for filename
3124         if ($line =~ /^Filename:\s/){
3125             my ($filename) = ($line =~ /^Filename: (.*)$/);
3126             # Construct real path
3127             my $repo_path = File::Spec->join($repo_basepath, $srv_path);
3128             store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
3129             next;
3130         }
3131     }
3133     close( $PACKAGES );
3134     unlink( "$path.in" );
3138 sub store_fileinfo {
3139     my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
3141     my %fileinfo = (
3142         'package' => $package,
3143         'dist' => $dist,
3144         'version' => $vers,
3145     );
3147     $repo_files{ "${srvdir}/$file" } = \%fileinfo;
3151 sub cleanup_and_extract {
3152         my $fileinfo = $repo_files{ $File::Find::name };
3154         if( defined $fileinfo ) {
3155                 my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
3156                 my $sql;
3157                 my $package = $fileinfo->{ 'package' };
3158                 my $newver = $fileinfo->{ 'version' };
3160                 mkpath($dir);
3161                 system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
3163                 if( -f "$dir/DEBIAN/templates" ) {
3165                         daemon_log("0 DEBUG: Found debconf templates in '$package' - $newver", 7);
3167                         my $tmpl= ""; {
3168                                 local $/=undef;
3169                                 open FILE, "$dir/DEBIAN/templates";
3170                                 $tmpl = &encode_base64(<FILE>);
3171                                 close FILE;
3172                         }
3173                         rmtree("$dir/DEBIAN/templates");
3175                         $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
3176                         push @packages_list_statements, $sql;
3177                 }
3178         }
3180         return;
3184 sub register_at_foreign_servers {   
3185     my ($kernel) = $_[KERNEL];
3187     # hole alle bekannten server aus known_server_db
3188     my $server_sql = "SELECT * FROM $known_server_tn";
3189     my $server_res = $known_server_db->exec_statement($server_sql);
3191     # no entries in known_server_db
3192     if (not ref(@$server_res[0]) eq "ARRAY") { 
3193         # TODO
3194     }
3196     # detect already connected clients
3197     my $client_sql = "SELECT * FROM $known_clients_tn"; 
3198     my $client_res = $known_clients_db->exec_statement($client_sql);
3200     # send my server details to all other gosa-si-server within the network
3201     foreach my $hit (@$server_res) {
3202         my $hostname = @$hit[0];
3203         my $hostkey = &create_passwd;
3205         # add already connected clients to registration message 
3206         my $myhash = &create_xml_hash('new_server', $server_address, $hostname);
3207         &add_content2xml_hash($myhash, 'key', $hostkey);
3208         map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res);
3210         # add locally loaded gosa-si modules to registration message
3211         my $loaded_modules = {};
3212         while (my ($package, $pck_info) = each %$known_modules) {
3213                                                 next if ((!defined(@$pck_info[2])) || (!(ref (@$pck_info[2]) eq 'HASH')));
3214                                                 foreach my $act_module (keys(%{@$pck_info[2]})) {
3215                                                         $loaded_modules->{$act_module} = ""; 
3216                                                 }
3217         }
3219         map(&add_content2xml_hash($myhash, "loaded_modules", $_), keys(%$loaded_modules));
3221         # add macaddress to registration message
3222         my ($host_ip, $host_port) = split(/:/, $hostname);
3223         my $local_ip = &get_local_ip_for_remote_ip($host_ip);
3224         my $network_interface= &get_interface_for_ip($local_ip);
3225         my $host_mac = &get_mac_for_interface($network_interface);
3226         &add_content2xml_hash($myhash, 'macaddress', $host_mac);
3227         
3228         # build registration message and send it
3229         my $foreign_server_msg = &create_xml_string($myhash);
3230         my $error = &send_msg_to_target($foreign_server_msg, $hostname, $ServerPackages_key, "new_server", 0); 
3231     }
3232     
3233     $kernel->delay_set("register_at_foreign_servers", $foreign_servers_register_delay); 
3234     return;
3238 #==== MAIN = main ==============================================================
3239 #  parse commandline options
3240 Getopt::Long::Configure( "bundling" );
3241 GetOptions("h|help" => \&usage,
3242         "c|config=s" => \$cfg_file,
3243         "f|foreground" => \$foreground,
3244         "v|verbose+" => \$verbose,
3245         "no-arp+" => \$no_arp,
3246            );
3248 # Prepare UID / GID as daemon_log may need it quite early
3249 $root_uid = getpwnam('root');
3250 $adm_gid = getgrnam('adm');
3252 #  read and set config parameters
3253 &check_cmdline_param ;
3254 &read_configfile($cfg_file, %cfg_defaults);
3255 &check_pid;
3257 $SIG{CHLD} = 'IGNORE';
3259 # Just fork, if we are not in foreground mode
3260 if( ! $foreground ) { 
3261     if (! chdir('/')) {
3262         daemon_log("Can't chdir to /: $!", 1);
3263         exit( 1 );
3264     }
3265     umask( 0 );
3266     $pid = fork;
3267 } else { 
3268     $pid = $$; 
3271 if( 0 != $pid ) {
3272     # Parent: put PID into the $pid_file
3273     open( LOCK_FILE, ">$pid_file" );
3274     print LOCK_FILE "$pid\n";
3275     close( LOCK_FILE );
3276     if( !$foreground ) { 
3277         exit( 0 );
3280 else {
3281     # Child
3282     open( STDIN,  '+>/dev/null' );
3283     open( STDOUT, '+>&STDIN'    );
3284     open( STDERR, '+>&STDIN'    );
3285     if (! POSIX::setsid()) {
3286         daemon_log("Can't start a new session: $!");
3287         exit( 1 );
3288     }
3289     $poe_kernel->has_forked() if ($poe_kernel->can('has_forked'));
3293 # parse head url and revision from svn
3294 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
3295 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
3296 $server_headURL = defined $1 ? $1 : 'unknown' ;
3297 $server_revision = defined $2 ? $2 : 'unknown' ;
3298 if ($server_headURL =~ /\/tag\// || 
3299         $server_headURL =~ /\/branches\// ) {
3300     $server_status = "stable"; 
3301 } else {
3302     $server_status = "developmental" ;
3305 # Prepare log file and set permissons
3306 open(FH, ">>$log_file");
3307 close FH;
3308 chmod(0440, $log_file);
3309 chown($root_uid, $adm_gid, $log_file);
3310 chown($root_uid, $adm_gid, "/var/lib/gosa-si");
3312 daemon_log(" ", 1);
3313 daemon_log("$0 started!", 1);
3314 daemon_log("status: $server_status", 1);
3315 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1); 
3318     no strict "refs";
3320     if ($db_module eq "DBmysql") {
3321         # connect to incoming_db
3322         $incoming_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3324         # connect to gosa-si job queue
3325         $job_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3327         # connect to known_clients_db
3328         $known_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3330         # connect to foreign_clients_db
3331         $foreign_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3333         # connect to known_server_db
3334         $known_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3336         # connect to login_usr_db
3337         $login_users_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3339         # connect to fai_server_db 
3340         $fai_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3342         # connect to fai_release_db
3343         $fai_release_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3345         # connect to packages_list_db
3346         $packages_list_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3348         # connect to messaging_db
3349         $messaging_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3351     } elsif ($db_module eq "DBsqlite") {
3352         # connect to incoming_db
3353         unlink($incoming_file_name);
3354         $incoming_db = GOSA::DBsqlite->new($incoming_file_name);
3355         
3356         # connect to gosa-si job queue
3357         unlink($job_queue_file_name);  ## just for debugging
3358         $job_db = GOSA::DBsqlite->new($job_queue_file_name);
3359         chmod(0640, $job_queue_file_name);
3360         chown($root_uid, $adm_gid, $job_queue_file_name);
3361         
3362         # connect to known_clients_db
3363         unlink($known_clients_file_name);   ## just for debugging
3364         $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
3365         chmod(0640, $known_clients_file_name);
3366         chown($root_uid, $adm_gid, $known_clients_file_name);
3367         
3368         # connect to foreign_clients_db
3369         unlink($foreign_clients_file_name);
3370         $foreign_clients_db = GOSA::DBsqlite->new($foreign_clients_file_name);
3371         chmod(0640, $foreign_clients_file_name);
3372         chown($root_uid, $adm_gid, $foreign_clients_file_name);
3373         
3374         # connect to known_server_db
3375         unlink($known_server_file_name);
3376         $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
3377         chmod(0640, $known_server_file_name);
3378         chown($root_uid, $adm_gid, $known_server_file_name);
3379         
3380         # connect to login_usr_db
3381         unlink($login_users_file_name);
3382         $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
3383         chmod(0640, $login_users_file_name);
3384         chown($root_uid, $adm_gid, $login_users_file_name);
3385         
3386         # connect to fai_server_db
3387         unlink($fai_server_file_name);
3388         $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
3389         chmod(0640, $fai_server_file_name);
3390         chown($root_uid, $adm_gid, $fai_server_file_name);
3391         
3392         # connect to fai_release_db
3393         unlink($fai_release_file_name);
3394         $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
3395         chmod(0640, $fai_release_file_name);
3396         chown($root_uid, $adm_gid, $fai_release_file_name);
3397         
3398         # connect to packages_list_db
3399         #unlink($packages_list_file_name);
3400         unlink($packages_list_under_construction);
3401         $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
3402         chmod(0640, $packages_list_file_name);
3403         chown($root_uid, $adm_gid, $packages_list_file_name);
3404         
3405         # connect to messaging_db
3406         unlink($messaging_file_name);
3407         $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
3408         chmod(0640, $messaging_file_name);
3409         chown($root_uid, $adm_gid, $messaging_file_name);
3410     }
3414 # Creating tables
3415 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
3416 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
3417 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
3418 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
3419 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
3420 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
3421 $foreign_clients_db->create_table($foreign_clients_tn, \@foreign_clients_col_names);
3422 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
3423 $incoming_db->create_table($incoming_tn, \@incoming_col_names);
3424 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
3426 # create xml object used for en/decrypting
3427 $xml = new XML::Simple();
3430 # foreign servers 
3431 my @foreign_server_list;
3433 # add foreign server from cfg file
3434 if ($foreign_server_string ne "") {
3435     my @cfg_foreign_server_list = split(",", $foreign_server_string);
3436     foreach my $foreign_server (@cfg_foreign_server_list) {
3437         push(@foreign_server_list, $foreign_server);
3438     }
3440     daemon_log("0 INFO: found foreign server in config file: ".join(", ", @foreign_server_list), 5);
3443 # Perform a DNS lookup for server registration if flag is true
3444 if ($dns_lookup eq "true") {
3445     # Add foreign server from dns
3446     my @tmp_servers;
3447     if (not $server_domain) {
3448         # Try our DNS Searchlist
3449         for my $domain(get_dns_domains()) {
3450             chomp($domain);
3451             my ($tmp_domains, $error_string) = &get_server_addresses($domain);
3452             if(@$tmp_domains) {
3453                 for my $tmp_server(@$tmp_domains) {
3454                     push @tmp_servers, $tmp_server;
3455                 }
3456             }
3457         }
3458         if(@tmp_servers && length(@tmp_servers)==0) {
3459             daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3460         }
3461     } else {
3462         @tmp_servers = &get_server_addresses($server_domain);
3463         if( 0 == @tmp_servers ) {
3464             daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3465         }
3466     }
3468     daemon_log("0 INFO: found foreign server via DNS ".join(", ", @tmp_servers), 5);    
3470     foreach my $server (@tmp_servers) { 
3471         unshift(@foreign_server_list, $server); 
3472     }
3473 } else {
3474     daemon_log("0 INFO: DNS lookup for server registration is disabled", 5);
3478 # eliminate duplicate entries
3479 @foreign_server_list = &del_doubles(@foreign_server_list);
3480 my $all_foreign_server = join(", ", @foreign_server_list);
3481 daemon_log("0 INFO: found foreign server in config file and DNS: '$all_foreign_server'", 5);
3483 # add all found foreign servers to known_server
3484 my $cur_timestamp = &get_time();
3485 foreach my $foreign_server (@foreign_server_list) {
3487         # do not add myself to known_server_db
3488         if (&is_local($foreign_server)) { next; }
3489         ######################################
3491     my $res = $known_server_db->add_dbentry( {table=>$known_server_tn, 
3492             primkey=>['hostname'],
3493             hostname=>$foreign_server,
3494             macaddress=>"",
3495             status=>'not_yet_registered',
3496             hostkey=>"none",
3497             loaded_modules => "none", 
3498             timestamp=>$cur_timestamp,
3499             } );
3503 # Import all modules
3504 &import_modules;
3506 # Check wether all modules are gosa-si valid passwd check
3507 &password_check;
3509 # Prepare for using Opsi 
3510 if ($opsi_enabled eq "true") {
3511     use JSON::RPC::Client;
3512     use XML::Quote qw(:all);
3513     $opsi_url= "https://".$opsi_admin.":".$opsi_password."@".$opsi_server.":4447/rpc";
3514     $opsi_client = new JSON::RPC::Client;
3518 POE::Component::Server::TCP->new(
3519         Alias => "TCP_SERVER",
3520         Port => $server_port,
3521         ClientInput => sub {
3522                 my ($kernel, $input, $heap, $session) = @_[KERNEL, ARG0, HEAP, SESSION];
3523         my $session_id = $session->ID;
3524         my $remote_ip = $heap->{'remote_ip'};
3525                 push(@msgs_to_decrypt, $input);
3526         &daemon_log("$session_id DEBUG: incoming message from '$remote_ip'", 7);
3527                 $kernel->yield("msg_to_decrypt");
3528         },
3529         InlineStates => {
3530                 msg_to_decrypt => \&msg_to_decrypt,
3531                 next_task => \&next_task,
3532                 task_result => \&handle_task_result,
3533                 task_done   => \&handle_task_done,
3534                 task_debug  => \&handle_task_debug,
3535                 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3536         }
3537 );
3539 daemon_log("0 INFO: start socket for incoming xml messages at port '$server_port' ", 1);
3541 # create session for repeatedly checking the job queue for jobs
3542 POE::Session->create(
3543         inline_states => {
3544                 _start => \&session_start,
3545         _stop => \&session_stop,
3546         register_at_foreign_servers => \&register_at_foreign_servers,
3547         next_task => \&next_task,
3548         task_result => \&handle_task_result,
3549         task_done   => \&handle_task_done,
3550         task_debug  => \&handle_task_debug,
3551         watch_for_next_tasks => \&watch_for_next_tasks,
3552         watch_for_new_messages => \&watch_for_new_messages,
3553         watch_for_delivery_messages => \&watch_for_delivery_messages,
3554         watch_for_done_messages => \&watch_for_done_messages,
3555                 watch_for_new_jobs => \&watch_for_new_jobs,
3556         watch_for_modified_jobs => \&watch_for_modified_jobs,
3557         watch_for_done_jobs => \&watch_for_done_jobs,
3558         watch_for_opsi_jobs => \&watch_for_opsi_jobs,
3559         watch_for_old_known_clients => \&watch_for_old_known_clients,
3560         watch_for_logged_in_users => \&watch_for_logged_in_users,
3561         create_packages_list_db => \&run_create_packages_list_db,
3562         create_fai_server_db => \&run_create_fai_server_db,
3563         create_fai_release_db => \&run_create_fai_release_db,
3564                 recreate_packages_db => \&run_recreate_packages_db,
3565         session_run_result => \&session_run_result,
3566         session_run_debug => \&session_run_debug,
3567         session_run_done => \&session_run_done,
3568         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3569         }
3570 );
3573 POE::Kernel->run();
3574 exit;