Code

bugfix: multiple declaration of $next_msg
[gosa.git] / gosa-si / gosa-si-server
1 #!/usr/bin/perl
2 #===============================================================================
3 #
4 #         FILE:  gosa-sd
5 #
6 #        USAGE:  ./gosa-sd
7 #
8 #  DESCRIPTION:
9 #
10 #      OPTIONS:  ---
11 # REQUIREMENTS:  libconfig-inifiles-perl libcrypt-rijndael-perl libxml-simple-perl 
12 #                libdata-dumper-simple-perl libdbd-sqlite3-perl libnet-ldap-perl
13 #                libpoe-perl
14 #         BUGS:  ---
15 #        NOTES:
16 #       AUTHOR:   (Andreas Rettenberger), <rettenberger@gonicus.de>
17 #      COMPANY:
18 #      VERSION:  1.0
19 #      CREATED:  12.09.2007 08:54:41 CEST
20 #     REVISION:  ---
21 #===============================================================================
24 # TODO
25 #
26 # max_children wird momentan nicht mehr verwendet, jede eingehende nachricht bekommt ein eigenes POE child
28 use strict;
29 use warnings;
30 use Getopt::Long;
31 use Config::IniFiles;
32 use POSIX;
34 use Fcntl;
35 use IO::Socket::INET;
36 use IO::Handle;
37 use IO::Select;
38 use Symbol qw(qualify_to_ref);
39 use Crypt::Rijndael;
40 use MIME::Base64;
41 use Digest::MD5  qw(md5 md5_hex md5_base64);
42 use XML::Simple;
43 use Data::Dumper;
44 use Sys::Syslog qw( :DEFAULT setlogsock);
45 use Cwd;
46 use File::Spec;
47 use File::Basename;
48 use File::Find;
49 use File::Copy;
50 use File::Path;
51 use GOSA::GosaSupportDaemon;
52 use POE qw(Component::Server::TCP Wheel::Run Filter::Reference);
53 use Net::LDAP;
54 use Net::LDAP::Util qw(:escape);
55 use Time::HiRes qw( usleep);
57 my $db_module = "DBsqlite";
58 {
59 no strict "refs";
60 require ("GOSA/".$db_module.".pm");
61 ("GOSA/".$db_module)->import;
62 daemon_log("0 INFO: importing database module '$db_module'", 1);
63 }
65 my $modules_path = "/usr/lib/gosa-si/modules";
66 use lib "/usr/lib/gosa-si/modules";
68 # revision number of server and program name
69 my $server_version = '$HeadURL: https://oss.gonicus.de/repositories/gosa/trunk/gosa-si/gosa-si-server $:$Rev: 10826 $';
70 my $server_headURL;
71 my $server_revision;
72 my $server_status;
73 our $prg= basename($0);
75 our $global_kernel;
76 my ($foreground, $ping_timeout);
77 my ($server);
78 my ($gosa_server, $job_queue_timeout, $job_queue_loop_delay);
79 my ($messaging_db_loop_delay);
80 my ($procid, $pid);
81 my ($arp_fifo);
82 my ($xml);
83 my $sources_list;
84 my $max_clients;
85 my %repo_files=();
86 my $repo_path;
87 my %repo_dirs=();
89 # Variables declared in config file are always set to 'our'
90 our (%cfg_defaults, $log_file, $pid_file, 
91     $server_ip, $server_port, $ClientPackages_key, $dns_lookup,
92     $arp_activ, $gosa_unit_tag,
93     $GosaPackages_key, $gosa_timeout,
94     $foreign_server_string, $server_domain, $ServerPackages_key, $foreign_servers_register_delay,
95     $wake_on_lan_passwd, $job_synchronization, $modified_jobs_loop_delay,
96     $arp_enabled, $arp_interface,
97     $opsi_enabled, $opsi_server, $opsi_admin, $opsi_password,
98                 $new_systems_ou,
99 );
101 # additional variable which should be globaly accessable
102 our $server_address;
103 our $server_mac_address;
104 our $gosa_address;
105 our $no_arp;
106 our $verbose;
107 our $forground;
108 our $cfg_file;
109 our ($ldap_uri, $ldap_base, $ldap_admin_dn, $ldap_admin_password, $ldap_server_dn);
110 our ($mysql_username, $mysql_password, $mysql_database, $mysql_host);
111 our $known_modules;
112 our $root_uid;
113 our $adm_gid;
116 # specifies the verbosity of the daemon_log
117 $verbose = 0 ;
119 # if foreground is not null, script will be not forked to background
120 $foreground = 0 ;
122 # specifies the timeout seconds while checking the online status of a registrating client
123 $ping_timeout = 5;
125 $no_arp = 0;
126 my $packages_list_under_construction = "/tmp/packages_list_creation_in_progress";
127 my @packages_list_statements;
128 my $watch_for_new_jobs_in_progress = 0;
130 # holds all incoming decrypted messages
131 our $incoming_db;
132 our $incoming_tn = 'incoming';
133 my $incoming_file_name;
134 my @incoming_col_names = ("id INTEGER PRIMARY KEY auto_increment",
135         "timestamp VARCHAR(14) DEFAULT 'none'", 
136         "headertag VARCHAR(255) DEFAULT 'none'",
137         "targettag VARCHAR(255) DEFAULT 'none'",
138         "xmlmessage TEXT",
139         "module VARCHAR(255) DEFAULT 'none'",
140         "sessionid VARCHAR(255) DEFAULT '0'",
141 );
143 # holds all gosa jobs
144 our $job_db;
145 our $job_queue_tn = 'jobs';
146 my $job_queue_file_name;
147 my @job_queue_col_names = ("id INTEGER PRIMARY KEY auto_increment",
148         "timestamp VARCHAR(14) DEFAULT 'none'", 
149         "status VARCHAR(255) DEFAULT 'none'", 
150         "result TEXT",
151         "progress VARCHAR(255) DEFAULT 'none'",
152         "headertag VARCHAR(255) DEFAULT 'none'",
153         "targettag VARCHAR(255) DEFAULT 'none'", 
154         "xmlmessage TEXT", 
155         "macaddress VARCHAR(17) DEFAULT 'none'",
156         "plainname VARCHAR(255) DEFAULT 'none'",
157         "siserver VARCHAR(255) DEFAULT 'none'",
158         "modified INTEGER DEFAULT '0'",
159 );
161 # holds all other gosa-si-server
162 our $known_server_db;
163 our $known_server_tn = "known_server";
164 my $known_server_file_name;
165 my @known_server_col_names = ("hostname VARCHAR(255)", "macaddress VARCHAR(17)", "status VARCHAR(255)", "hostkey VARCHAR(255)", "loaded_modules TEXT", "timestamp VARCHAR(14)");
167 # holds all registrated clients
168 our $known_clients_db;
169 our $known_clients_tn = "known_clients";
170 my $known_clients_file_name;
171 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)");
173 # holds all registered clients at a foreign server
174 our $foreign_clients_db;
175 our $foreign_clients_tn = "foreign_clients"; 
176 my $foreign_clients_file_name;
177 my @foreign_clients_col_names = ("hostname VARCHAR(255)", "macaddress VARCHAR(17)", "regserver VARCHAR(255)", "timestamp VARCHAR(14)");
179 # holds all logged in user at each client 
180 our $login_users_db;
181 our $login_users_tn = "login_users";
182 my $login_users_file_name;
183 my @login_users_col_names = ("client VARCHAR(255)", "user VARCHAR(255)", "timestamp VARCHAR(14)", "regserver VARCHAR(255) DEFAULT 'localhost'");
185 # holds all fai server, the debian release and tag
186 our $fai_server_db;
187 our $fai_server_tn = "fai_server"; 
188 my $fai_server_file_name;
189 our @fai_server_col_names = ("timestamp VARCHAR(14)", "server VARCHAR(255)", "fai_release VARCHAR(255)", "sections VARCHAR(255)", "tag VARCHAR(255)"); 
191 our $fai_release_db;
192 our $fai_release_tn = "fai_release"; 
193 my $fai_release_file_name;
194 our @fai_release_col_names = ("timestamp VARCHAR(14)", "fai_release VARCHAR(255)", "class VARCHAR(255)", "type VARCHAR(255)", "state VARCHAR(255)"); 
196 # holds all packages available from different repositories
197 our $packages_list_db;
198 our $packages_list_tn = "packages_list";
199 my $packages_list_file_name;
200 our @packages_list_col_names = ("distribution VARCHAR(255)", "package VARCHAR(255)", "version VARCHAR(255)", "section VARCHAR(255)", "description TEXT", "template LONGBLOB", "timestamp VARCHAR(14)");
201 my $outdir = "/tmp/packages_list_db";
202 my $arch = "i386"; 
204 # holds all messages which should be delivered to a user
205 our $messaging_db;
206 our $messaging_tn = "messaging"; 
207 our @messaging_col_names = ("id INTEGER", "subject TEXT", "message_from VARCHAR(255)", "message_to VARCHAR(255)", 
208         "flag VARCHAR(255)", "direction VARCHAR(255)", "delivery_time VARCHAR(255)", "message TEXT", "timestamp VARCHAR(14)" );
209 my $messaging_file_name;
211 # path to directory to store client install log files
212 our $client_fai_log_dir = "/var/log/fai"; 
214 # queue which stores taskes until one of the $max_children children are ready to process the task
215 #my @tasks = qw();
216 my @msgs_to_decrypt = qw();
217 my $max_children = 2;
220 # loop delay for job queue to look for opsi jobs
221 my $job_queue_opsi_delay = 10;
222 our $opsi_client;
223 our $opsi_url;
224  
225 # Lifetime of logged in user information. If no update information comes after n seconds, 
226 # the user is expeceted to be no longer logged in or the host is no longer running. Because
227 # of this, the user is deleted from login_users_db
228 our $logged_in_user_date_of_expiry = 600;
231 %cfg_defaults = (
232 "general" => {
233     "log-file" => [\$log_file, "/var/run/".$prg.".log"],
234     "pid-file" => [\$pid_file, "/var/run/".$prg.".pid"],
235     },
236 "server" => {
237     "ip"                    => [\$server_ip, "0.0.0.0"],
238     "port"                  => [\$server_port, "20081"],
239     "known-clients"         => [\$known_clients_file_name, '/var/lib/gosa-si/clients.db' ],
240     "known-servers"         => [\$known_server_file_name, '/var/lib/gosa-si/servers.db'],
241     "incoming"              => [\$incoming_file_name, '/var/lib/gosa-si/incoming.db'],
242     "login-users"           => [\$login_users_file_name, '/var/lib/gosa-si/users.db'],
243     "fai-server"            => [\$fai_server_file_name, '/var/lib/gosa-si/fai_server.db'],
244     "fai-release"           => [\$fai_release_file_name, '/var/lib/gosa-si/fai_release.db'],
245     "packages-list"         => [\$packages_list_file_name, '/var/lib/gosa-si/packages.db'],
246     "messaging"             => [\$messaging_file_name, '/var/lib/gosa-si/messaging.db'],
247     "foreign-clients"       => [\$foreign_clients_file_name, '/var/lib/gosa-si/foreign_clients.db'],
248     "source-list"           => [\$sources_list, '/etc/apt/sources.list'],
249     "repo-path"             => [\$repo_path, '/srv/www/repository'],
250     "ldap-uri"              => [\$ldap_uri, ""],
251     "ldap-base"             => [\$ldap_base, ""],
252     "ldap-admin-dn"         => [\$ldap_admin_dn, ""],
253     "ldap-admin-password"   => [\$ldap_admin_password, ""],
254     "gosa-unit-tag"         => [\$gosa_unit_tag, ""],
255     "max-clients"           => [\$max_clients, 10],
256     "wol-password"          => [\$wake_on_lan_passwd, ""],
257                 "mysql-username"        => [\$mysql_username, "gosa_si"],
258                 "mysql-password"        => [\$mysql_password, ""],
259                 "mysql-database"        => [\$mysql_database, "gosa_si"],
260                 "mysql-host"            => [\$mysql_host, "127.0.0.1"],
261     },
262 "GOsaPackages" => {
263     "job-queue" => [\$job_queue_file_name, '/var/lib/gosa-si/jobs.db'],
264     "job-queue-loop-delay" => [\$job_queue_loop_delay, 3],
265     "messaging-db-loop-delay" => [\$messaging_db_loop_delay, 3],
266     "key" => [\$GosaPackages_key, "none"],
267                 "new-systems-ou" => [\$new_systems_ou, 'ou=workstations,ou=systems'],
268     },
269 "ClientPackages" => {
270     "key" => [\$ClientPackages_key, "none"],
271     "user-date-of-expiry" => [\$logged_in_user_date_of_expiry, 600],
272     },
273 "ServerPackages"=> {
274     "address"      => [\$foreign_server_string, ""],
275     "dns-lookup"            => [\$dns_lookup, "true"],
276     "domain"  => [\$server_domain, ""],
277     "key"     => [\$ServerPackages_key, "none"],
278     "key-lifetime" => [\$foreign_servers_register_delay, 120],
279     "job-synchronization-enabled" => [\$job_synchronization, "true"],
280     "synchronization-loop" => [\$modified_jobs_loop_delay, 5],
281     },
282 "ArpHandler" => {
283     "enabled"   => [\$arp_enabled, "true"],
284     "interface" => [\$arp_interface, "all"],
285         },
286 "Opsi" => {
287     "enabled"  => [\$opsi_enabled, "false"], 
288     "server"   => [\$opsi_server, "localhost"],
289     "admin"    => [\$opsi_admin, "opsi-admin"],
290     "password" => [\$opsi_password, "secret"],
291    },
293 );
296 #===  FUNCTION  ================================================================
297 #         NAME:  usage
298 #   PARAMETERS:  nothing
299 #      RETURNS:  nothing
300 #  DESCRIPTION:  print out usage text to STDERR
301 #===============================================================================
302 sub usage {
303     print STDERR << "EOF" ;
304 usage: $prg [-hvf] [-c config]
306            -h        : this (help) message
307            -c <file> : config file
308            -f        : foreground, process will not be forked to background
309            -v        : be verbose (multiple to increase verbosity)
310            -no-arp   : starts $prg without connection to arp module
311  
312 EOF
313     print "\n" ;
317 #===  FUNCTION  ================================================================
318 #         NAME:  logging
319 #   PARAMETERS:  level - string - default 'info'
320 #                msg - string -
321 #                facility - string - default 'LOG_DAEMON'
322 #      RETURNS:  nothing
323 #  DESCRIPTION:  function for logging
324 #===============================================================================
325 sub daemon_log {
326     # log into log_file
327     my( $msg, $level ) = @_;
328     if(not defined $msg) { return }
329     if(not defined $level) { $level = 1 }
330     if(defined $log_file){
331         open(LOG_HANDLE, ">>$log_file");
332         if(not defined open( LOG_HANDLE, ">>$log_file" )) {
333             print STDERR "cannot open $log_file: $!";
334             return 
335         }
336         chomp($msg);
337         #$msg =~s/\n//g;   # no newlines are allowed in log messages, this is important for later log parsing
338         if($level <= $verbose){
339             my ($seconds, $minutes, $hours, $monthday, $month,
340                     $year, $weekday, $yearday, $sommertime) = localtime(time);
341             $hours = $hours < 10 ? $hours = "0".$hours : $hours;
342             $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
343             $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
344             my @monthnames = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
345             $month = $monthnames[$month];
346             $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
347             $year+=1900;
348             my $name = $prg;
350             my $log_msg = "$month $monthday $hours:$minutes:$seconds $name $msg\n";
351             print LOG_HANDLE $log_msg;
352             if( $foreground ) { 
353                 print STDERR $log_msg;
354             }
355         }
356         close( LOG_HANDLE );
357     }
361 #===  FUNCTION  ================================================================
362 #         NAME:  check_cmdline_param
363 #   PARAMETERS:  nothing
364 #      RETURNS:  nothing
365 #  DESCRIPTION:  validates commandline parameter
366 #===============================================================================
367 sub check_cmdline_param () {
368     my $err_config;
369     my $err_counter = 0;
370         if(not defined($cfg_file)) {
371                 $cfg_file = "/etc/gosa-si/server.conf";
372                 if(! -r $cfg_file) {
373                         $err_config = "please specify a config file";
374                         $err_counter += 1;
375                 }
376     }
377     if( $err_counter > 0 ) {
378         &usage( "", 1 );
379         if( defined( $err_config)) { print STDERR "$err_config\n"}
380         print STDERR "\n";
381         exit( -1 );
382     }
386 #===  FUNCTION  ================================================================
387 #         NAME:  check_pid
388 #   PARAMETERS:  nothing
389 #      RETURNS:  nothing
390 #  DESCRIPTION:  handels pid processing
391 #===============================================================================
392 sub check_pid {
393     $pid = -1;
394     # Check, if we are already running
395     if( open(LOCK_FILE, "<$pid_file") ) {
396         $pid = <LOCK_FILE>;
397         if( defined $pid ) {
398             chomp( $pid );
399             if( -f "/proc/$pid/stat" ) {
400                 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
401                 if( $stat ) {
402                                         daemon_log("ERROR: Already running",1);
403                     close( LOCK_FILE );
404                     exit -1;
405                 }
406             }
407         }
408         close( LOCK_FILE );
409         unlink( $pid_file );
410     }
412     # create a syslog msg if it is not to possible to open PID file
413     if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
414         my($msg) = "Couldn't obtain lockfile '$pid_file' ";
415         if (open(LOCK_FILE, '<', $pid_file)
416                 && ($pid = <LOCK_FILE>))
417         {
418             chomp($pid);
419             $msg .= "(PID $pid)\n";
420         } else {
421             $msg .= "(unable to read PID)\n";
422         }
423         if( ! ($foreground) ) {
424             openlog( $0, "cons,pid", "daemon" );
425             syslog( "warning", $msg );
426             closelog();
427         }
428         else {
429             print( STDERR " $msg " );
430         }
431         exit( -1 );
432     }
435 #===  FUNCTION  ================================================================
436 #         NAME:  import_modules
437 #   PARAMETERS:  module_path - string - abs. path to the directory the modules 
438 #                are stored
439 #      RETURNS:  nothing
440 #  DESCRIPTION:  each file in module_path which ends with '.pm' and activation 
441 #                state is on is imported by "require 'file';"
442 #===============================================================================
443 sub import_modules {
444     daemon_log(" ", 1);
446     if (not -e $modules_path) {
447         daemon_log("0 ERROR: cannot find directory or directory is not readable: $modules_path", 1);   
448     }
450     opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
451     while (defined (my $file = readdir (DIR))) {
452         if (not $file =~ /(\S*?).pm$/) {
453             next;
454         }
455                 my $mod_name = $1;
457         # ArpHandler switch
458         if( $file =~ /ArpHandler.pm/ ) {
459             if( $arp_enabled eq "false" ) { next; }
460         }
461         
462         eval { require $file; };
463         if ($@) {
464             daemon_log("0 ERROR: gosa-si-server could not load module $file", 1);
465             daemon_log("$@", 1);
466             exit;
467                 } else {
468                         my $info = eval($mod_name.'::get_module_info()');
469                         # Only load module if get_module_info() returns a non-null object
470                         if( $info ) {
471                                 my ($input_address, $input_key, $event_hash) = @{$info};
472                                 $known_modules->{$mod_name} = $info;
473                                 daemon_log("0 INFO: module $mod_name loaded", 5);
474                         }
475                 }
476     }   
478     close (DIR);
481 #===  FUNCTION  ================================================================
482 #         NAME:  password_check
483 #   PARAMETERS:  nothing
484 #      RETURNS:  nothing
485 #  DESCRIPTION:  escalates an critical error if two modules exist which are avaialable by 
486 #                the same password
487 #===============================================================================
488 sub password_check {
489     my $passwd_hash = {};
490     while (my ($mod_name, $mod_info) = each %$known_modules) {
491         my $mod_passwd = @$mod_info[1];
492         if (not defined $mod_passwd) { next; }
493         if (not exists $passwd_hash->{$mod_passwd}) {
494             $passwd_hash->{$mod_passwd} = $mod_name;
496         # escalates critical error
497         } else {
498             &daemon_log("0 ERROR: two loaded modules do have the same password. Please modify the 'key'-parameter in config file");
499             &daemon_log("0 ERROR: module='$mod_name' and module='".$passwd_hash->{$mod_passwd}."'");
500             exit( -1 );
501         }
502     }
507 #===  FUNCTION  ================================================================
508 #         NAME:  sig_int_handler
509 #   PARAMETERS:  signal - string - signal arose from system
510 #      RETURNS:  nothing
511 #  DESCRIPTION:  handels tasks to be done befor signal becomes active
512 #===============================================================================
513 sub sig_int_handler {
514     my ($signal) = @_;
516 #       if (defined($ldap_handle)) {
517 #               $ldap_handle->disconnect;
518 #       }
519     # TODO alle verbliebenden ldap verbindungen aus allen heaps beenden
520     
522     daemon_log("shutting down gosa-si-server", 1);
523     system("kill `ps -C gosa-si-server -o pid=`");
525 $SIG{INT} = \&sig_int_handler;
528 sub check_key_and_xml_validity {
529     my ($crypted_msg, $module_key, $session_id) = @_;
530     my $msg;
531     my $msg_hash;
532     my $error_string;
533     eval{
534         $msg = &decrypt_msg($crypted_msg, $module_key);
536         if ($msg =~ /<xml>/i){
537             $msg =~ s/\s+/ /g;  # just for better daemon_log
538             daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 9);
539             $msg_hash = $xml->XMLin($msg, ForceArray=>1);
541             ##############
542             # check header
543             if( not exists $msg_hash->{'header'} ) { die "no header specified"; }
544             my $header_l = $msg_hash->{'header'};
545             if( (1 > @{$header_l}) || ( ( 'HASH' eq ref @{$header_l}[0]) && (1 > keys %{@{$header_l}[0]}) ) ) { die 'empty header tag'; }
546             if( 1 < @{$header_l} ) { die 'more than one header specified'; }
547             my $header = @{$header_l}[0];
548             if( 0 == length $header) { die 'empty string in header tag'; }
550             ##############
551             # check source
552             if( not exists $msg_hash->{'source'} ) { die "no source specified"; }
553             my $source_l = $msg_hash->{'source'};
554             if( (1 > @{$source_l}) || ( ( 'HASH' eq ref @{$source_l}[0]) && (1 > keys %{@{$source_l}[0]}) ) ) { die 'empty source tag'; }
555             if( 1 < @{$source_l} ) { die 'more than one source specified'; }
556             my $source = @{$source_l}[0];
557             if( 0 == length $source) { die 'source error'; }
559             ##############
560             # check target
561             if( not exists $msg_hash->{'target'} ) { die "no target specified"; }
562             my $target_l = $msg_hash->{'target'};
563             if( (1 > @{$target_l}) || ( ('HASH' eq ref @{$target_l}[0]) && (1 > keys %{@{$target_l}[0]}) ) ) { die 'empty target tag'; }
564         }
565     };
566     if($@) {
567         daemon_log("$session_id ERROR: do not understand the message: $@", 1);
568         $msg = undef;
569         $msg_hash = undef;
570     }
572     return ($msg, $msg_hash);
576 sub check_outgoing_xml_validity {
577     my ($msg, $session_id) = @_;
579     my $msg_hash;
580     eval{
581         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
583         ##############
584         # check header
585         my $header_l = $msg_hash->{'header'};
586         if( 1 != @{$header_l} ) {
587             die 'no or more than one headers specified';
588         }
589         my $header = @{$header_l}[0];
590         if( 0 == length $header) {
591             die 'header has length 0';
592         }
594         ##############
595         # check source
596         my $source_l = $msg_hash->{'source'};
597         if( 1 != @{$source_l} ) {
598             die 'no or more than 1 sources specified';
599         }
600         my $source = @{$source_l}[0];
601         if( 0 == length $source) {
602             die 'source has length 0';
603         }
605                                 # Check if source contains hostname instead of ip address
606                                 if(not $source =~ /^[a-z0-9\.]+:\d+$/i) {
607                                                 my ($hostname,$port) = split(/:/, $source);
608                                                 my $ip_address = inet_ntoa(scalar gethostbyname($hostname));
609                                                 if(defined($ip_address) && $ip_address =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/ && $port =~ /^\d+$/) {
610                                                         # Write ip address to $source variable
611                                                         $source = "$ip_address:$port";
612                                                 }
613                                 }
614         unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
615                 $source =~ /^GOSA$/i) {
616             die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
617         }
618         
619         ##############
620         # check target  
621         my $target_l = $msg_hash->{'target'};
622         if( 0 == @{$target_l} ) {
623             die "no targets specified";
624         }
625         foreach my $target (@$target_l) {
626             if( 0 == length $target) {
627                 die "target has length 0";
628             }
629             unless( $target =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
630                     $target =~ /^GOSA$/i ||
631                     $target =~ /^\*$/ ||
632                     $target =~ /KNOWN_SERVER/i ||
633                     $target =~ /JOBDB/i ||
634                     $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 ){
635                 die "target '$target' is not a complete ip-address with port or a valid target name or a mac-address";
636             }
637         }
638     };
639     if($@) {
640         daemon_log("$session_id ERROR: outgoing msg is not gosa-si envelope conform: $@", 1);
641         daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 1);
642         $msg_hash = undef;
643     }
645     return ($msg_hash);
649 sub input_from_known_server {
650     my ($input, $remote_ip, $session_id) = @_ ;  
651     my ($msg, $msg_hash, $module);
653     my $sql_statement= "SELECT * FROM known_server";
654     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
656     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
657         my $host_name = $hit->{hostname};
658         if( not $host_name =~ "^$remote_ip") {
659             next;
660         }
661         my $host_key = $hit->{hostkey};
662         daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 7);
663         daemon_log("$session_id DEBUG: input_from_known_server: host_key: $host_key", 7);
665         # check if module can open msg envelope with module key
666         my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
667         if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
668             daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 7);
669             daemon_log("$@", 8);
670             next;
671         }
672         else {
673             $msg = $tmp_msg;
674             $msg_hash = $tmp_msg_hash;
675             $module = "ServerPackages";
676             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 7);
677             last;
678         }
679     }
681     if( (!$msg) || (!$msg_hash) || (!$module) ) {
682         daemon_log("$session_id DEBUG: Incoming message is not from a known server", 7);
683     }
684   
685     return ($msg, $msg_hash, $module);
689 sub input_from_known_client {
690     my ($input, $remote_ip, $session_id) = @_ ;  
691     my ($msg, $msg_hash, $module);
693     my $sql_statement= "SELECT * FROM known_clients";
694     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
695     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
696         my $host_name = $hit->{hostname};
697         if( not $host_name =~ /^$remote_ip:\d*$/) {
698                 next;
699                 }
700         my $host_key = $hit->{hostkey};
701         &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 7);
702         &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 7);
704         # check if module can open msg envelope with module key
705         ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
707         if( (!$msg) || (!$msg_hash) ) {
708             &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 7);
709             &daemon_log("$@", 8);
710             next;
711         }
712         else {
713             $module = "ClientPackages";
714             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 7);
715             last;
716         }
717     }
719     if( (!$msg) || (!$msg_hash) || (!$module) ) {
720         &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 7);
721     }
723     return ($msg, $msg_hash, $module);
727 sub input_from_unknown_host {
728         no strict "refs";
729         my ($input, $session_id) = @_ ;
730         my ($msg, $msg_hash, $module);
731         my $error_string;
733         my %act_modules = %$known_modules;
735         while( my ($mod, $info) = each(%act_modules)) {
737                 # check a key exists for this module
738                 my $module_key = ${$mod."_key"};
739                 if( not defined $module_key ) {
740                         if( $mod eq 'ArpHandler' ) {
741                                 next;
742                         }
743                         daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
744                         next;
745                 }
746                 daemon_log("$session_id DEBUG: $mod: $module_key", 7);
748                 # check if module can open msg envelope with module key
749                 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
750                 if( (not defined $msg) || (not defined $msg_hash) ) {
751                         next;
752                 } else {
753                         $module = $mod;
754             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 7);
755                         last;
756                 }
757         }
759         if( (!$msg) || (!$msg_hash) || (!$module)) {
760                 daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 7);
761         }
763         return ($msg, $msg_hash, $module);
767 sub create_ciphering {
768     my ($passwd) = @_;
769         if((!defined($passwd)) || length($passwd)==0) {
770                 $passwd = "";
771         }
772     $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
773     my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
774     my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
775     $my_cipher->set_iv($iv);
776     return $my_cipher;
780 sub encrypt_msg {
781     my ($msg, $key) = @_;
782     my $my_cipher = &create_ciphering($key);
783     my $len;
784     {
785             use bytes;
786             $len= 16-length($msg)%16;
787     }
788     $msg = "\0"x($len).$msg;
789     $msg = $my_cipher->encrypt($msg);
790     chomp($msg = &encode_base64($msg));
791     # there are no newlines allowed inside msg
792     $msg=~ s/\n//g;
793     return $msg;
797 sub decrypt_msg {
799     my ($msg, $key) = @_ ;
800     $msg = &decode_base64($msg);
801     my $my_cipher = &create_ciphering($key);
802     $msg = $my_cipher->decrypt($msg); 
803     $msg =~ s/\0*//g;
804     return $msg;
808 sub get_encrypt_key {
809     my ($target) = @_ ;
810     my $encrypt_key;
811     my $error = 0;
813     # target can be in known_server
814     if( not defined $encrypt_key ) {
815         my $sql_statement= "SELECT * FROM known_server WHERE hostname='$target'";
816         my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
817         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
818             my $host_name = $hit->{hostname};
819             if( $host_name ne $target ) {
820                 next;
821             }
822             $encrypt_key = $hit->{hostkey};
823             last;
824         }
825     }
827     # target can be in known_client
828     if( not defined $encrypt_key ) {
829         my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$target'";
830         my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
831         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
832             my $host_name = $hit->{hostname};
833             if( $host_name ne $target ) {
834                 next;
835             }
836             $encrypt_key = $hit->{hostkey};
837             last;
838         }
839     }
841     return $encrypt_key;
845 #===  FUNCTION  ================================================================
846 #         NAME:  open_socket
847 #   PARAMETERS:  PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
848 #                [PeerPort] string necessary if port not appended by PeerAddr
849 #      RETURNS:  socket IO::Socket::INET
850 #  DESCRIPTION:  open a socket to PeerAddr
851 #===============================================================================
852 sub open_socket {
853     my ($PeerAddr, $PeerPort) = @_ ;
854     if(defined($PeerPort)){
855         $PeerAddr = $PeerAddr.":".$PeerPort;
856     }
857     my $socket;
858     $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
859             Porto => "tcp",
860             Type => SOCK_STREAM,
861             Timeout => 5,
862             );
863     if(not defined $socket) {
864         return;
865     }
866 #    &daemon_log("DEBUG: open_socket: $PeerAddr", 7);
867     return $socket;
871 #sub get_local_ip_for_remote_ip {
872 #       my $remote_ip= shift;
873 #       my $result="0.0.0.0";
875 #       if($remote_ip =~ /^(\d\d?\d?\.){3}\d\d?\d?$/) {
876 #               if($remote_ip eq "127.0.0.1") {
877 #                       $result = "127.0.0.1";
878 #               } else {
879 #                       my $PROC_NET_ROUTE= ('/proc/net/route');
881 #                       open(PROC_NET_ROUTE, "<$PROC_NET_ROUTE")
882 #                               or die "Could not open $PROC_NET_ROUTE";
884 #                       my @ifs = <PROC_NET_ROUTE>;
886 #                       close(PROC_NET_ROUTE);
888 #                       # Eat header line
889 #                       shift @ifs;
890 #                       chomp @ifs;
891 #                       foreach my $line(@ifs) {
892 #                               my ($Iface,$Destination,$Gateway,$Flags,$RefCnt,$Use,$Metric,$Mask,$MTU,$Window,$IRTT)=split(/\s/, $line);
893 #                               my $destination;
894 #                               my $mask;
895 #                               my ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Destination);
896 #                               $destination= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
897 #                               ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Mask);
898 #                               $mask= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
899 #                               if(new NetAddr::IP($remote_ip)->within(new NetAddr::IP($destination, $mask))) {
900 #                                       # destination matches route, save mac and exit
901 #                                       $result= &get_ip($Iface);
902 #                                       last;
903 #                               }
904 #                       }
905 #               }
906 #       } else {
907 #               daemon_log("0 WARNING: get_local_ip_for_remote_ip() was called with a non-ip parameter: '$remote_ip'", 1);
908 #       }
909 #       return $result;
910 #}
913 sub send_msg_to_target {
914     my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
915     my $error = 0;
916     my $header;
917     my $timestamp = &get_time();
918     my $new_status;
919     my $act_status;
920     my ($sql_statement, $res);
921   
922     if( $msg_header ) {
923         $header = "'$msg_header'-";
924     } else {
925         $header = "";
926     }
928         # Patch the source ip
929         if($msg =~ /<source>0\.0\.0\.0:\d*?<\/source>/) {
930                 my $remote_ip = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
931                 $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$remote_ip:$2<\/source>/s;
932         }
934     # encrypt xml msg
935     my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
937     # opensocket
938     my $socket = &open_socket($address);
939     if( !$socket ) {
940         daemon_log("$session_id WARNING: cannot send ".$header."msg to $address , host not reachable", 3);
941         $error++;
942     }
943     
944     if( $error == 0 ) {
945         # send xml msg
946         print $socket $crypted_msg."\n";
948         daemon_log("$session_id INFO: send ".$header."msg to $address", 5);
949         daemon_log("$session_id DEBUG: message:\n$msg", 9);
950         
951     }
953     # close socket in any case
954     if( $socket ) {
955         close $socket;
956     }
958     if( $error > 0 ) { $new_status = "down"; }
959     else { $new_status = $msg_header; }
962     # known_clients
963     $sql_statement = "SELECT * FROM $known_clients_tn WHERE hostname='$address'";
964     $res = $known_clients_db->select_dbentry($sql_statement);
965     if( keys(%$res) == 1) {
966         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
967         if ($act_status eq "down" && $new_status eq "down") {
968             $sql_statement = "DELETE FROM known_clients WHERE hostname='$address'";
969             $res = $known_clients_db->del_dbentry($sql_statement);
970             daemon_log("$session_id WARNING: failed 2x to send msg to host '$address', delete host from known_clients", 3);
971         } else { 
972             $sql_statement = "UPDATE known_clients SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
973             $res = $known_clients_db->update_dbentry($sql_statement);
974             if($new_status eq "down"){
975                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
976             } else {
977                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
978             }
979         }
980     }
982     # known_server
983     $sql_statement = "SELECT * FROM $known_server_tn WHERE hostname='$address'";
984     $res = $known_server_db->select_dbentry($sql_statement);
985     if( keys(%$res) == 1) {
986         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
987         if ($act_status eq "down" && $new_status eq "down") {
988             $sql_statement = "DELETE FROM known_server WHERE hostname='$address'";
989             $res = $known_server_db->del_dbentry($sql_statement);
990             daemon_log("$session_id WARNING: failed 2x to send a message to host '$address', delete host from known_server", 3);
991         } 
992         else { 
993             $sql_statement = "UPDATE known_server SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
994             $res = $known_server_db->update_dbentry($sql_statement);
995             if($new_status eq "down"){
996                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
997             } else {
998                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
999             }
1000         }
1001     }
1002     return $error; 
1006 sub update_jobdb_status_for_send_msgs {
1007     my ($session_id, $answer, $error) = @_;
1008     &daemon_log("$session_id DEBUG: try to update job status", 7); 
1009     if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
1010         my $jobdb_id = $1;
1011     
1012         $answer =~ /<header>(.*)<\/header>/;
1013         my $job_header = $1;
1015         $answer =~ /<target>(.*)<\/target>/;
1016         my $job_target = $1;
1017             
1018         # Sending msg failed
1019         if( $error ) {
1021             # Set jobs to done, jobs do not need to deliver their message in any case
1022             if (($job_header eq "trigger_action_localboot")
1023                     ||($job_header eq "trigger_action_lock")
1024                     ||($job_header eq "trigger_action_halt") 
1025                     ) {
1026                 my $sql_statement = "UPDATE $job_queue_tn SET status='done' WHERE id=$jobdb_id";
1027                 &daemon_log("$session_id DEBUG: $sql_statement", 7); 
1028                 my $res = $job_db->update_dbentry($sql_statement);
1029                 
1030             # Reactivate jobs, jobs need to deliver their message
1031             } elsif (($job_header eq "trigger_action_activate")
1032                     ||($job_header eq "trigger_action_update")
1033                     ||($job_header eq "trigger_action_reinstall") 
1034                     ||($job_header eq "trigger_activate_new")
1035                     ) {
1036                 &reactivate_job_with_delay($session_id, $job_target, $job_header, 30 );
1038             # For all other messages
1039             } else {
1040                 my $sql_statement = "UPDATE $job_queue_tn ".
1041                     "SET status='error', result='can not deliver msg, please consult log file' ".
1042                     "WHERE id=$jobdb_id";
1043                 &daemon_log("$session_id DEBUG: $sql_statement", 7); 
1044                 my $res = $job_db->update_dbentry($sql_statement);
1045             }
1047         # Sending msg was successful
1048         } else {
1049             # Set jobs localboot, lock, activate, halt, reboot and wake to done
1050             # jobs reinstall, update, inst_update do themself setting to done
1051             if (($job_header eq "trigger_action_localboot")
1052                     ||($job_header eq "trigger_action_lock")
1053                     ||($job_header eq "trigger_action_activate")
1054                     ||($job_header eq "trigger_action_halt") 
1055                     ||($job_header eq "trigger_action_reboot")
1056                     ||($job_header eq "trigger_action_wake")
1057                     ||($job_header eq "trigger_wake")
1058                     ) {
1060                 my $sql_statement = "UPDATE $job_queue_tn ".
1061                     "SET status='done' ".
1062                     "WHERE id=$jobdb_id AND status='processed'";
1063                 &daemon_log("$session_id DEBUG: $sql_statement", 7); 
1064                 my $res = $job_db->update_dbentry($sql_statement);
1065             } else { 
1066                 &daemon_log("$session_id DEBUG: sending message succeed but cannot update job status.", 7); 
1067             } 
1068         } 
1069     } else { 
1070         &daemon_log("$session_id DEBUG: cannot update job status, msg has no jobdb_id-tag: $answer", 7); 
1071     }
1074 sub reactivate_job_with_delay {
1075     my ($session_id, $target, $header, $delay) = @_ ;
1076     # 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
1077     
1078     if (not defined $delay) { $delay = 30 } ;
1079     my $delay_timestamp = &calc_timestamp(&get_time(), "plus", $delay);
1081     my $sql = "UPDATE $job_queue_tn Set timestamp='$delay_timestamp', status='waiting' WHERE (macaddress='$target' AND headertag='$header')"; 
1082     my $res = $job_db->update_dbentry($sql);
1083     daemon_log("$session_id INFO: '$header'-job will be reactivated at '$delay_timestamp' ".
1084             "cause client '$target' is currently not available", 5);
1085     daemon_log("$session_id $sql", 7);                             
1086     return;
1090 sub sig_handler {
1091         my ($kernel, $signal) = @_[KERNEL, ARG0] ;
1092         daemon_log("0 INFO got signal '$signal'", 1); 
1093         $kernel->sig_handled();
1094         return;
1098 sub msg_to_decrypt {
1099         my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
1100         my $session_id = $session->ID;
1101         my ($msg, $msg_hash, $module);
1102         my $error = 0;
1104         # fetch new msg out of @msgs_to_decrypt
1105         my $tmp_next_msg = shift @msgs_to_decrypt;
1106     my ($next_msg, $msg_source) = split(/;/, $tmp_next_msg);
1108         # msg is from a new client or gosa
1109         ($msg, $msg_hash, $module) = &input_from_unknown_host($next_msg, $session_id);
1111         # msg is from a gosa-si-server
1112         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1113                 ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $heap->{'remote_ip'}, $session_id);
1114         }
1115         # msg is from a gosa-si-client
1116         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1117                 ($msg, $msg_hash, $module) = &input_from_known_client($next_msg, $heap->{'remote_ip'}, $session_id);
1118         }
1119         # an error occurred
1120         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1121                 # if an incoming msg could not be decrypted (maybe a wrong key), send client a ping. If the client
1122                 # could not understand a msg from its server the client cause a re-registering process
1123         my $remote_ip = $heap->{'remote_ip'};
1124         my $remote_port = $heap->{'remote_port'};
1125         my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source><target>$msg_source</target></xml>";
1126         my ($test_error, $test_error_string) = &send_msg_to_target($ping_msg, "$msg_source", "dummy-key", "gosa_ping", $session_id);
1128                 daemon_log("$session_id WARNING cannot understand incoming msg, send 'ping'-msg to all host with ip '".$heap->{remote_ip}.
1129                         "' to cause a re-registering of the client if necessary", 3);
1130                 $error++;
1131         }
1134         my $header;
1135         my $target;
1136         my $source;
1137         my $done = 0;
1138         my $sql;
1139         my $res;
1141         # check whether this message should be processed here
1142         if ($error == 0) {
1143                 $header = @{$msg_hash->{'header'}}[0];
1144                 $target = @{$msg_hash->{'target'}}[0];
1145                 $source = @{$msg_hash->{'source'}}[0];
1146                 my $not_found_in_known_clients_db = 0;
1147                 my $not_found_in_known_server_db = 0;
1148                 my $not_found_in_foreign_clients_db = 0;
1149                 my $local_address;
1150                 my $local_mac;
1151                 my ($target_ip, $target_port) = split(':', $target);
1153                 # Determine the local ip address if target is an ip address
1154                 if ($target =~ /^\d+\.\d+\.\d+\.\d+:\d+$/) {
1155                         $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1156                 } else {
1157                         $local_address = $server_address;
1158                 }
1160                 # Determine the local mac address if target is a mac address
1161                 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) {
1162                         my $loc_ip = &get_local_ip_for_remote_ip($heap->{'remote_ip'});
1163                         my $network_interface= &get_interface_for_ip($loc_ip);
1164                         $local_mac = &get_mac_for_interface($network_interface);
1165                 } else {
1166                         $local_mac = $server_mac_address;
1167                 }
1169                 # target and source is equal to GOSA -> process here
1170                 if (not $done) {
1171                         if ($target eq "GOSA" && $source eq "GOSA") {
1172                                 $done = 1;                    
1173                                 &daemon_log("$session_id DEBUG: target and source is 'GOSA' -> process here", 7);
1174                         }
1175                 }
1177                 # target is own address without forward_to_gosa-tag -> process here
1178                 if (not $done) {
1179                         #if ((($target eq $local_address) || ($target eq $local_mac) ) && (not exists $msg_hash->{'forward_to_gosa'})) {
1180                         if (($target eq $local_address) && (not exists $msg_hash->{'forward_to_gosa'})) {
1181                                 $done = 1;
1182                                 if ($source eq "GOSA") {
1183                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1184                                 }
1185                                 &daemon_log("$session_id DEBUG: target is own address without forward_to_gosa-tag -> process here", 7);
1186                         }
1187                 }
1189                 # target is a client address in known_clients -> process here
1190                 if (not $done) {
1191                         $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')"; 
1192                         $res = $known_clients_db->select_dbentry($sql);
1193                         if (keys(%$res) > 0) {
1194                                 $done = 1; 
1195                                 my $hostname = $res->{1}->{'hostname'};
1196                                 $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1197                                 my $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1198                                 if ($source eq "GOSA") {
1199                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1200                                 }
1201                                 &daemon_log("$session_id DEBUG: target is a client address in known_clients -> process here", 7);
1203                         } else {
1204                                 $not_found_in_known_clients_db = 1;
1205                         }
1206                 }
1208                 # target ist own address with forward_to_gosa-tag not pointing to myself -> process here
1209                 if (not $done) {
1210                         my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1211                         my $gosa_at;
1212                         my $gosa_session_id;
1213                         if (($target eq $local_address) && (defined $forward_to_gosa)){
1214                                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1215                                 if ($gosa_at ne $local_address) {
1216                                         $done = 1;
1217                                         &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag not pointing to myself -> process here", 7); 
1218                                 }
1219                         }
1220                 }
1222                 # if message should be processed here -> add message to incoming_db
1223                 if ($done) {
1224                         # if a job or a gosa message comes from a foreign server, fake module to GosaPackages
1225                         # so gosa-si-server knows how to process this kind of messages
1226                         if ($header =~ /^gosa_/ || $header =~ /^job_/) {
1227                                 $module = "GosaPackages";
1228                         }
1230                         my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1231                                         primkey=>[],
1232                                         headertag=>$header,
1233                                         targettag=>$target,
1234                                         xmlmessage=>&encode_base64($msg),
1235                                         timestamp=>&get_time,
1236                                         module=>$module,
1237                                         sessionid=>$session_id,
1238                                 } );
1240                 }
1242                 # target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa
1243                 if (not $done) {
1244                         my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1245                         my $gosa_at;
1246                         my $gosa_session_id;
1247                         if (($target eq $local_address) && (defined $forward_to_gosa)){
1248                                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1249                                 if ($gosa_at eq $local_address) {
1250                                         my $session_reference = $kernel->ID_id_to_session($gosa_session_id);
1251                                         if( defined $session_reference ) {
1252                                                 $heap = $session_reference->get_heap();
1253                                         }
1254                                         if(exists $heap->{'client'}) {
1255                                                 $msg = &encrypt_msg($msg, $GosaPackages_key);
1256                                                 $heap->{'client'}->put($msg);
1257                                                 &daemon_log("$session_id INFO: incoming '$header' message forwarded to GOsa", 5); 
1258                                         }
1259                                         $done = 1;
1260                                         &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa", 7);
1261                                 }
1262                         }
1264                 }
1266                 # target is a client address in foreign_clients -> forward to registration server
1267                 if (not $done) {
1268                         $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1269                         $res = $foreign_clients_db->select_dbentry($sql);
1270                         if (keys(%$res) > 0) {
1271                                 my $hostname = $res->{1}->{'hostname'};
1272                                 my ($host_ip, $host_port) = split(/:/, $hostname);
1273                                 my $local_address =  &get_local_ip_for_remote_ip($host_ip).":$server_port";
1274                                 my $regserver = $res->{1}->{'regserver'};
1275                                 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$regserver'"; 
1276                                 my $res = $known_server_db->select_dbentry($sql);
1277                                 if (keys(%$res) > 0) {
1278                                         my $regserver_key = $res->{1}->{'hostkey'};
1279                                         $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1280                                         $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1281                                         if ($source eq "GOSA") {
1282                                                 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1283                                         }
1284                                         &send_msg_to_target($msg, $regserver, $regserver_key, $header, $session_id);
1285                                 }
1286                                 $done = 1;
1287                                 &daemon_log("$session_id DEBUG: target is a client address in foreign_clients -> forward to registration server", 7);
1288                         } else {
1289                                 $not_found_in_foreign_clients_db = 1;
1290                         }
1291                 }
1293                 # target is a server address -> forward to server
1294                 if (not $done) {
1295                         $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1296                         $res = $known_server_db->select_dbentry($sql);
1297                         if (keys(%$res) > 0) {
1298                                 my $hostkey = $res->{1}->{'hostkey'};
1300                                 if ($source eq "GOSA") {
1301                                         $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1302                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1304                                 }
1306                                 &send_msg_to_target($msg, $target, $hostkey, $header, $session_id);
1307                                 $done = 1;
1308                                 &daemon_log("$session_id DEBUG: target is a server address -> forward to server", 7);
1309                         } else {
1310                                 $not_found_in_known_server_db = 1;
1311                         }
1312                 }
1315                 # target is not in foreign_clients_db, known_server_db or known_clients_db, maybe it is a complete new one -> process here
1316                 if ( $not_found_in_foreign_clients_db 
1317                         && $not_found_in_known_server_db
1318                         && $not_found_in_known_clients_db) {
1319                         &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);
1320             if ($header =~ /^gosa_/ || $header =~ /^job_/) { 
1321                 $module = "GosaPackages"; 
1322             }
1323                         my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1324                                         primkey=>[],
1325                                         headertag=>$header,
1326                                         targettag=>$target,
1327                                         xmlmessage=>&encode_base64($msg),
1328                                         timestamp=>&get_time,
1329                                         module=>$module,
1330                                         sessionid=>$session_id,
1331                                 } );
1332                         $done = 1;
1333                 }
1336                 if (not $done) {
1337                         daemon_log("$session_id ERROR: do not know what to do with this message: $msg", 1);
1338                         if ($source eq "GOSA") {
1339                                 my %data = ('error_msg' => &encode_base64($msg), 'error_string' => "Do not know what to do with this message!");
1340                                 my $error_msg = &build_msg("error", $local_address, "GOSA", \%data ); 
1342                                 my $session_reference = $kernel->ID_id_to_session($session_id);
1343                                 if( defined $session_reference ) {
1344                                         $heap = $session_reference->get_heap();
1345                                 }
1346                                 if(exists $heap->{'client'}) {
1347                                         $error_msg = &encrypt_msg($error_msg, $GosaPackages_key);
1348                                         $heap->{'client'}->put($error_msg);
1349                                 }
1350                         }
1351                 }
1353         }
1355         return;
1359 sub next_task {
1360     my ($session, $heap, $task) = @_[SESSION, HEAP, ARG0];
1361     my $running_task = POE::Wheel::Run->new(
1362             Program => sub { process_task($session, $heap, $task) },
1363             StdioFilter => POE::Filter::Reference->new(),
1364             StdoutEvent  => "task_result",
1365             StderrEvent  => "task_debug",
1366             CloseEvent   => "task_done",
1367             );
1368     $heap->{task}->{ $running_task->ID } = $running_task;
1371 sub handle_task_result {
1372     my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
1373     my $client_answer = $result->{'answer'};
1374     if( $client_answer =~ s/session_id=(\d+)$// ) {
1375         my $session_id = $1;
1376         if( defined $session_id ) {
1377             my $session_reference = $kernel->ID_id_to_session($session_id);
1378             if( defined $session_reference ) {
1379                 $heap = $session_reference->get_heap();
1380             }
1381         }
1383         if(exists $heap->{'client'}) {
1384             $heap->{'client'}->put($client_answer);
1385         }
1386     }
1387     $kernel->sig(CHLD => "child_reap");
1390 sub handle_task_debug {
1391     my $result = $_[ARG0];
1392     print STDERR "$result\n";
1395 sub handle_task_done {
1396     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1397     delete $heap->{task}->{$task_id};
1400 sub process_task {
1401     no strict "refs";
1402     #CHECK: Not @_[...]?
1403     my ($session, $heap, $task) = @_;
1404     my $error = 0;
1405     my $answer_l;
1406     my ($answer_header, @answer_target_l, $answer_source);
1407     my $client_answer = "";
1409     # prepare all variables needed to process message
1410     #my $msg = $task->{'xmlmessage'};
1411     my $msg = &decode_base64($task->{'xmlmessage'});
1412     my $incoming_id = $task->{'id'};
1413     my $module = $task->{'module'};
1414     my $header =  $task->{'headertag'};
1415     my $session_id = $task->{'sessionid'};
1416                 my $msg_hash;
1417                 eval {
1418         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
1419                 }; 
1420                 daemon_log("ERROR: XML failure '$@'") if ($@);
1421     my $source = @{$msg_hash->{'source'}}[0];
1422     
1423     # set timestamp of incoming client uptodate, so client will not 
1424     # be deleted from known_clients because of expiration
1425     my $act_time = &get_time();
1426     my $sql = "UPDATE $known_clients_tn SET timestamp='$act_time' WHERE hostname='$source'"; 
1427     my $res = $known_clients_db->exec_statement($sql);
1429     ######################
1430     # process incoming msg
1431     if( $error == 0) {
1432         daemon_log("$session_id INFO: Incoming msg (session_id=$session_id) with header '".@{$msg_hash->{'header'}}[0]."'", 5); 
1433         daemon_log("$session_id DEBUG: Processing module ".$module, 7);
1434         $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1436         if ( 0 < @{$answer_l} ) {
1437             my $answer_str = join("\n", @{$answer_l});
1438             while ($answer_str =~ /<header>(\w+)<\/header>/g) {
1439                 daemon_log("$session_id INFO: got answer message with header '$1'", 5);
1440             }
1441             daemon_log("$session_id DEBUG: $module: got answer from module: \n".$answer_str,9);
1442         } else {
1443             daemon_log("$session_id DEBUG: $module: got no answer from module!" ,7);
1444         }
1446     }
1447     if( !$answer_l ) { $error++ };
1449     ########
1450     # answer
1451     if( $error == 0 ) {
1453         foreach my $answer ( @{$answer_l} ) {
1454             # check outgoing msg to xml validity
1455             my $answer_hash = &check_outgoing_xml_validity($answer, $session_id);
1456             if( not defined $answer_hash ) { next; }
1457             
1458             $answer_header = @{$answer_hash->{'header'}}[0];
1459             @answer_target_l = @{$answer_hash->{'target'}};
1460             $answer_source = @{$answer_hash->{'source'}}[0];
1462             # deliver msg to all targets 
1463             foreach my $answer_target ( @answer_target_l ) {
1465                 # targets of msg are all gosa-si-clients in known_clients_db
1466                 if( $answer_target eq "*" ) {
1467                     # answer is for all clients
1468                     my $sql_statement= "SELECT * FROM known_clients";
1469                     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
1470                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1471                         my $host_name = $hit->{hostname};
1472                         my $host_key = $hit->{hostkey};
1473                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1474                         &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1475                     }
1476                 }
1478                 # targets of msg are all gosa-si-server in known_server_db
1479                 elsif( $answer_target eq "KNOWN_SERVER" ) {
1480                     # answer is for all server in known_server
1481                     my $sql_statement= "SELECT * FROM $known_server_tn";
1482                     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
1483                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1484                         my $host_name = $hit->{hostname};
1485                         my $host_key = $hit->{hostkey};
1486                         $answer =~ s/<target>\S+<\/target>/<target>$host_name<\/target>/g;
1487                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1488                         &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1489                     }
1490                 }
1492                 # target of msg is GOsa
1493                                 elsif( $answer_target eq "GOSA" ) {
1494                                         my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1495                                         my $add_on = "";
1496                     if( defined $session_id ) {
1497                         $add_on = ".session_id=$session_id";
1498                     }
1499                     # answer is for GOSA and has to returned to connected client
1500                     my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1501                     $client_answer = $gosa_answer.$add_on;
1502                 }
1504                 # target of msg is job queue at this host
1505                 elsif( $answer_target eq "JOBDB") {
1506                     $answer =~ /<header>(\S+)<\/header>/;   
1507                     my $header;
1508                     if( defined $1 ) { $header = $1; }
1509                     my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1510                     &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1511                 }
1513                 # Target of msg is a mac address
1514                 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 ) {
1515                     daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients and foreign_clients", 5);
1517                     # Looking for macaddress in known_clients
1518                     my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1519                     my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1520                     my $found_ip_flag = 0;
1521                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1522                         my $host_name = $hit->{hostname};
1523                         my $host_key = $hit->{hostkey};
1524                         $answer =~ s/$answer_target/$host_name/g;
1525                         daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1526                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1527                         &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1528                         $found_ip_flag++ ;
1529                     }   
1531                     # Looking for macaddress in foreign_clients
1532                     if ($found_ip_flag == 0) {
1533                         my $sql = "SELECT * FROM $foreign_clients_tn WHERE macaddress LIKE '$answer_target'";
1534                         my $res = $foreign_clients_db->select_dbentry($sql);
1535                         while( my ($hit_num, $hit) = each %{ $res } ) {
1536                             my $host_name = $hit->{hostname};
1537                             my $reg_server = $hit->{regserver};
1538                             daemon_log("$session_id INFO: found host '$host_name' with mac '$answer_target', registered at '$reg_server'", 5);
1539                             
1540                             # Fetch key for reg_server
1541                             my $reg_server_key;
1542                             my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$reg_server'";
1543                             my $res = $known_server_db->select_dbentry($sql);
1544                             if (exists $res->{1}) {
1545                                 $reg_server_key = $res->{1}->{'hostkey'}; 
1546                             } else {
1547                                 daemon_log("$session_id ERROR: cannot find hostkey for '$host_name' in '$known_server_tn'", 1); 
1548                                 daemon_log("$session_id ERROR: unable to forward answer to correct registration server, processing is aborted!", 1); 
1549                                 $reg_server_key = undef;
1550                             }
1552                             # Send answer to server where client is registered
1553                             if (defined $reg_server_key) {
1554                                 $answer =~ s/$answer_target/$host_name/g;
1555                                 my $error = &send_msg_to_target($answer, $reg_server, $reg_server_key, $answer_header, $session_id);
1556                                 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1557                                 $found_ip_flag++ ;
1558                             }
1559                         }
1560                     }
1562                     # No mac to ip matching found
1563                     if( $found_ip_flag == 0) {
1564                         daemon_log("$session_id WARNING: no host found in known_clients or foreign_clients with mac address '$answer_target'", 3);
1565                         &reactivate_job_with_delay($session_id, $answer_target, $answer_header, 30);
1566                     }
1568                 # Answer is for one specific host   
1569                 } else {
1570                     # get encrypt_key
1571                     my $encrypt_key = &get_encrypt_key($answer_target);
1572                     if( not defined $encrypt_key ) {
1573                         # unknown target
1574                         daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1575                         next;
1576                     }
1577                     my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1578                     &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1579                 }
1580             }
1581         }
1582     }
1584     my $filter = POE::Filter::Reference->new();
1585     my %result = ( 
1586             status => "seems ok to me",
1587             answer => $client_answer,
1588             );
1590     my $output = $filter->put( [ \%result ] );
1591     print @$output;
1596 sub session_start {
1597     my ($kernel) = $_[KERNEL];
1598     $global_kernel = $kernel;
1599     $kernel->yield('register_at_foreign_servers');
1600         $kernel->yield('create_fai_server_db', $fai_server_tn );
1601         $kernel->yield('create_fai_release_db', $fai_release_tn );
1602     $kernel->yield('watch_for_next_tasks');
1603         $kernel->sig(USR1 => "sig_handler");
1604         $kernel->sig(USR2 => "recreate_packages_db");
1605         $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1606         $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay); 
1607     $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay); 
1608         $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1609     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1610         $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1611     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1613     # Start opsi check
1614     if ($opsi_enabled eq "true") {
1615         $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay); 
1616     }
1621 sub watch_for_done_jobs {
1622     #CHECK: $heap for what?
1623     my ($kernel,$heap) = @_[KERNEL, HEAP];
1625     my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((status='done') AND (modified='0'))";
1626         my $res = $job_db->select_dbentry( $sql_statement );
1628     while( my ($id, $hit) = each %{$res} ) {
1629         my $jobdb_id = $hit->{id};
1630         my $sql_statement = "DELETE FROM $job_queue_tn WHERE id=$jobdb_id"; 
1631         my $res = $job_db->del_dbentry($sql_statement); 
1632     }
1634     $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1638 sub watch_for_opsi_jobs {
1639     my ($kernel) = $_[KERNEL];
1641     # 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 
1642     # opsi install job is to parse the xml message. There is still the correct header.
1643     my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((xmlmessage LIKE '%opsi_install_client</header>%') AND (status='processing') AND (siserver='localhost'))";
1644         my $res = $job_db->select_dbentry( $sql_statement );
1646     # Ask OPSI for an update of the running jobs
1647     while (my ($id, $hit) = each %$res ) {
1648         # Determine current parameters of the job
1649         my $hostId = $hit->{'plainname'};
1650         my $macaddress = $hit->{'macaddress'};
1651         my $progress = $hit->{'progress'};
1653         my $result= {};
1654         
1655         # For hosts, only return the products that are or get installed
1656         my $callobj;
1657         $callobj = {
1658             method  => 'getProductStates_hash',
1659             params  => [ $hostId ],
1660             id  => 1,
1661         };
1662         
1663         my $hres = $opsi_client->call($opsi_url, $callobj);
1664         #my ($hres_err, $hres_err_string) = &check_opsi_res($hres);
1665         if (not &check_opsi_res($hres)) {
1666             my $htmp= $hres->result->{$hostId};
1667         
1668             # Check state != not_installed or action == setup -> load and add
1669             my $products= 0;
1670             my $installed= 0;
1671             my $installing = 0;
1672             my $error= 0;  
1673             my @installed_list;
1674             my @error_list;
1675             my $act_status = "none";
1676             foreach my $product (@{$htmp}){
1678                 if ($product->{'installationStatus'} ne "not_installed" or
1679                         $product->{'actionRequest'} eq "setup"){
1681                     # Increase number of products for this host
1682                     $products++;
1683         
1684                     if ($product->{'installationStatus'} eq "failed"){
1685                         $result->{$product->{'productId'}}= "error";
1686                         unshift(@error_list, $product->{'productId'});
1687                         $error++;
1688                     }
1689                     if ($product->{'installationStatus'} eq "installed" && $product->{'actionRequest'}  eq "none"){
1690                         $result->{$product->{'productId'}}= "installed";
1691                         unshift(@installed_list, $product->{'productId'});
1692                         $installed++;
1693                     }
1694                     if ($product->{'installationStatus'} eq "installing"){
1695                         $result->{$product->{'productId'}}= "installing";
1696                         $installing++;
1697                         $act_status = "installing - ".$product->{'productId'};
1698                     }
1699                 }
1700             }
1701         
1702             # Estimate "rough" progress, avoid division by zero
1703             if ($products == 0) {
1704                 $result->{'progress'}= 0;
1705             } else {
1706                 $result->{'progress'}= int($installed * 100 / $products);
1707             }
1709             # Set updates in job queue
1710             if ((not $error) && (not $installing) && ($installed)) {
1711                 $act_status = "installed - ".join(", ", @installed_list);
1712             }
1713             if ($error) {
1714                 $act_status = "error - ".join(", ", @error_list);
1715             }
1716             if ($progress ne $result->{'progress'} ) {
1717                 # Updating progress and result 
1718                 my $update_statement = "UPDATE $job_queue_tn SET modified='1', progress='".$result->{'progress'}."', result='$act_status' WHERE macaddress='$macaddress' AND siserver='localhost'";
1719                 my $update_res = $job_db->update_dbentry($update_statement);
1720             }
1721             if ($progress eq 100) { 
1722                 # Updateing status
1723                 my $done_statement = "UPDATE $job_queue_tn SET modified='1', ";
1724                 if ($error) {
1725                     $done_statement .= "status='error'";
1726                 } else {
1727                     $done_statement .= "status='done'";
1728                 }
1729                 $done_statement .= " WHERE macaddress='$macaddress' AND siserver='localhost'";
1730                 my $done_res = $job_db->update_dbentry($done_statement);
1731             }
1734         }
1735     }
1737     $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay);
1741 # If a job got an update or was modified anyway, send to all other si-server an update message of this jobs.
1742 sub watch_for_modified_jobs {
1743     my ($kernel,$heap) = @_[KERNEL, HEAP];
1745     my $sql_statement = "SELECT * FROM $job_queue_tn WHERE (modified='1')"; 
1746     my $res = $job_db->select_dbentry( $sql_statement );
1747     
1748     # if db contains no jobs which should be update, do nothing
1749     if (keys %$res != 0) {
1751         if ($job_synchronization  eq "true") {
1752             # make out of the db result a gosa-si message   
1753             my $update_msg = &db_res2si_msg ($res, "foreign_job_updates", "KNOWN_SERVER", "MY_LOCAL_ADDRESS");
1754  
1755             # update all other SI-server
1756             &inform_all_other_si_server($update_msg);
1757         }
1759         # set jobs all jobs to modified = 0, wait until the next modification for updates of other si-server
1760         $sql_statement = "UPDATE $job_queue_tn SET modified='0' ";
1761         $res = $job_db->update_dbentry($sql_statement);
1762     }
1764     $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay);
1768 sub watch_for_new_jobs {
1769         if($watch_for_new_jobs_in_progress == 0) {
1770                 $watch_for_new_jobs_in_progress = 1;
1771                 my ($kernel,$heap) = @_[KERNEL, HEAP];
1773                 # check gosa job quaeue for jobs with executable timestamp
1774                 my $timestamp = &get_time();
1775                 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE status='waiting' AND (CAST(timestamp AS UNSIGNED)) < $timestamp ORDER BY timestamp";
1776                 my $res = $job_db->exec_statement( $sql_statement );
1778                 # Merge all new jobs that would do the same actions
1779                 my @drops;
1780                 my $hits;
1781                 foreach my $hit (reverse @{$res} ) {
1782                         my $macaddress= lc @{$hit}[8];
1783                         my $headertag= @{$hit}[5];
1784                         if(
1785                                 defined($hits->{$macaddress}) &&
1786                                 defined($hits->{$macaddress}->{$headertag}) &&
1787                                 defined($hits->{$macaddress}->{$headertag}[0])
1788                         ) {
1789                                 push @drops, "DELETE FROM $job_queue_tn WHERE id = $hits->{$macaddress}->{$headertag}[0]";
1790                         }
1791                         $hits->{$macaddress}->{$headertag}= $hit;
1792                 }
1794                 # Delete new jobs with a matching job in state 'processing'
1795                 foreach my $macaddress (keys %{$hits}) {
1796                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1797                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1798                                 if(defined($jobdb_id)) {
1799                                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND headertag='$jobdb_headertag' AND status='processing'";
1800                                         my $res = $job_db->exec_statement( $sql_statement );
1801                                         foreach my $hit (@{$res}) {
1802                                                 push @drops, "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1803                                         }
1804                                 } else {
1805                                         daemon_log("J ERROR: Job without id exists for macaddress $macaddress!", 1);
1806                                 }
1807                         }
1808                 }
1810                 # Commit deletion
1811                 $job_db->exec_statementlist(\@drops);
1813                 # Look for new jobs that could be executed
1814                 foreach my $macaddress (keys %{$hits}) {
1816                         # Look if there is an executing job
1817                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='processing'";
1818                         my $res = $job_db->exec_statement( $sql_statement );
1820                         # Skip new jobs for host if there is a processing job
1821                         if(defined($res) and defined @{$res}[0]) {
1822                                 # Prevent race condition if there is a trigger_activate job waiting and a goto-activation job processing
1823                                 my $row = @{$res}[0] if (ref $res eq 'ARRAY');
1824                                 if(@{$row}[5] eq 'trigger_action_reinstall') {
1825                                         my $sql_statement_2 =  "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='waiting' AND headertag = 'trigger_activate_new'"; 
1826                                         my $res_2 = $job_db->exec_statement( $sql_statement_2 );
1827                                         if(defined($res_2) and defined @{$res_2}[0]) {
1828                                                 # Set status from goto-activation to 'waiting' and update timestamp
1829                                                 $job_db->exec_statement("UPDATE $job_queue_tn SET status='waiting' WHERE macaddress LIKE '$macaddress' AND headertag = 'trigger_action_reinstall'");
1830                                                 $job_db->exec_statement("UPDATE $job_queue_tn SET timestamp='".&calc_timestamp(&get_time(), 'plus', 30)."' WHERE macaddress LIKE '$macaddress' AND headertag = 'trigger_action_reinstall'");
1831                                         }
1832                                 }
1833                                 next;
1834                         }
1836                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1837                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1838                                 if(defined($jobdb_id)) {
1839                                         my $job_msg = @{$hits->{$macaddress}->{$jobdb_headertag}}[7];
1841                                         daemon_log("J DEBUG: its time to execute $job_msg", 7);
1842                                         my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1843                                         my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1845                                         # expect macaddress is unique!!!!!!
1846                                         my $target = $res_hash->{1}->{hostname};
1848                                         # change header
1849                                         $job_msg =~ s/<header>job_/<header>gosa_/;
1851                                         # add sqlite_id
1852                                         $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1854                                         $job_msg =~ /<header>(\S+)<\/header>/;
1855                                         my $header = $1 ;
1856                                         my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");                    
1858                                         # update status in job queue to ...
1859                     # ... 'processing', for jobs: 'reinstall', 'update'
1860                     if (($header =~ /gosa_trigger_action_reinstall/) 
1861                             || ($header =~ /gosa_trigger_activate_new/)
1862                             || ($header =~ /gosa_trigger_action_update/)) {
1863                         my $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
1864                         my $dbres = $job_db->update_dbentry($sql_statement);
1865                     }
1867                     # ... 'done', for all other jobs, they are no longer needed in the jobqueue
1868                     else {
1869                         my $sql_statement = "UPDATE $job_queue_tn SET status='done' WHERE id=$jobdb_id";
1870                         my $dbres = $job_db->update_dbentry($sql_statement);
1871                     }
1872                 
1874                                         # We don't want parallel processing
1875                                         last;
1876                                 }
1877                         }
1878                 }
1880                 $watch_for_new_jobs_in_progress = 0;
1881                 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1882         }
1886 sub watch_for_new_messages {
1887     my ($kernel,$heap) = @_[KERNEL, HEAP];
1888     my @coll_user_msg;   # collection list of outgoing messages
1889     
1890     # check messaging_db for new incoming messages with executable timestamp
1891     my $timestamp = &get_time();
1892     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS UNSIGNED))<$timestamp AND flag='n' AND direction='in' )";
1893     my $res = $messaging_db->exec_statement( $sql_statement );
1894         foreach my $hit (@{$res}) {
1896         # create outgoing messages
1897         my $message_to = @{$hit}[3];
1898         # translate message_to to plain login name
1899         my @message_to_l = split(/,/, $message_to);  
1900                 my %receiver_h; 
1901                 foreach my $receiver (@message_to_l) {
1902                         if ($receiver =~ /^u_([\s\S]*)$/) {
1903                                 $receiver_h{$1} = 0;
1904                         } elsif ($receiver =~ /^g_([\s\S]*)$/) {
1905                                 my $group_name = $1;
1906                                 # fetch all group members from ldap and add them to receiver hash
1907                                 my $ldap_handle = &get_ldap_handle();
1908                                 if (defined $ldap_handle) {
1909                                                 my $mesg = $ldap_handle->search(
1910                                                                                 base => $ldap_base,
1911                                                                                 scope => 'sub',
1912                                                                                 attrs => ['memberUid'],
1913                                                                                 filter => "cn=$group_name",
1914                                                                                 );
1915                                                 if ($mesg->count) {
1916                                                                 my @entries = $mesg->entries;
1917                                                                 foreach my $entry (@entries) {
1918                                                                                 my @receivers= $entry->get_value("memberUid");
1919                                                                                 foreach my $receiver (@receivers) { 
1920                                                                                                 $receiver_h{$receiver} = 0;
1921                                                                                 }
1922                                                                 }
1923                                                 } 
1924                                                 # translating errors ?
1925                                                 if ($mesg->code) {
1926                                                                 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: $mesg->error", 1);
1927                                                 }
1928                                 # ldap handle error ?           
1929                                 } else {
1930                                         daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: no ldap handle available", 1);
1931                                 }
1932                         } else {
1933                                 my $sbjct = &encode_base64(@{$hit}[1]);
1934                                 my $msg = &encode_base64(@{$hit}[7]);
1935                                 &daemon_log("M WARNING: unknown receiver '$receiver' for a user-message '$sbjct - $msg'", 3); 
1936                         }
1937                 }
1938                 my @receiver_l = keys(%receiver_h);
1940         my $message_id = @{$hit}[0];
1942         #add each outgoing msg to messaging_db
1943         my $receiver;
1944         foreach $receiver (@receiver_l) {
1945             my $sql_statement = "INSERT INTO $messaging_tn (id, subject, message_from, message_to, flag, direction, delivery_time, message, timestamp) ".
1946                 "VALUES ('".
1947                 $message_id."', '".    # id
1948                 @{$hit}[1]."', '".     # subject
1949                 @{$hit}[2]."', '".     # message_from
1950                 $receiver."', '".      # message_to
1951                 "none"."', '".         # flag
1952                 "out"."', '".          # direction
1953                 @{$hit}[6]."', '".     # delivery_time
1954                 @{$hit}[7]."', '".     # message
1955                 $timestamp."'".     # timestamp
1956                 ")";
1957             &daemon_log("M DEBUG: $sql_statement", 1);
1958             my $res = $messaging_db->exec_statement($sql_statement);
1959             &daemon_log("M INFO: message '".@{$hit}[0]."' is prepared for delivery to receiver '$receiver'", 5);
1960         }
1962         # set incoming message to flag d=deliverd
1963         $sql_statement = "UPDATE $messaging_tn SET flag='p' WHERE id='$message_id'"; 
1964         &daemon_log("M DEBUG: $sql_statement", 7);
1965         $res = $messaging_db->update_dbentry($sql_statement);
1966         &daemon_log("M INFO: message '$message_id' is set to flag 'p' (processed)", 5);
1967     }
1969     $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay); 
1970     return;
1973 sub watch_for_delivery_messages {
1974     my ($kernel, $heap) = @_[KERNEL, HEAP];
1976     # select outgoing messages
1977     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( flag='p' AND direction='out' )";
1978     #&daemon_log("0 DEBUG: $sql", 7);
1979     my $res = $messaging_db->exec_statement( $sql_statement );
1980     
1981     # build out msg for each    usr
1982     foreach my $hit (@{$res}) {
1983         my $receiver = @{$hit}[3];
1984         my $msg_id = @{$hit}[0];
1985         my $subject = @{$hit}[1];
1986         my $message = @{$hit}[7];
1988         # resolve usr -> host where usr is logged in
1989         my $sql = "SELECT * FROM $login_users_tn WHERE (user='$receiver')"; 
1990         #&daemon_log("0 DEBUG: $sql", 7);
1991         my $res = $login_users_db->exec_statement($sql);
1993         # receiver is logged in nowhere
1994         if (not ref(@$res[0]) eq "ARRAY") { next; }    
1996         # receiver ist logged in at a client registered at local server
1997                 my $send_succeed = 0;
1998                 foreach my $hit (@$res) {
1999                                 my $receiver_host = @$hit[0];
2000                 my $delivered2host = 0;
2001                                 &daemon_log("M DEBUG: user '$receiver' is logged in at host '$receiver_host'", 7);
2003                                 # Looking for host in know_clients_db 
2004                                 my $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$receiver_host')";
2005                                 my $res = $known_clients_db->exec_statement($sql);
2007                 # Host is known in known_clients_db
2008                 if (ref(@$res[0]) eq "ARRAY") {
2009                     my $receiver_key = @{@{$res}[0]}[2];
2010                     my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
2011                     my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
2012                     my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0); 
2013                     if ($error == 0 ) {
2014                         $send_succeed++ ;
2015                         $delivered2host++ ;
2016                         &daemon_log("M DEBUG: send message for user '$receiver' to host '$receiver_host'", 7); 
2017                     } else {
2018                         &daemon_log("M DEBUG: cannot send message for user '$receiver' to host '$receiver_host'", 7); 
2019                     }
2020                 }
2021                 
2022                 # Message already send, do not need to do anything more, otherwise ...
2023                 if ($delivered2host) { next;}
2024     
2025                 # ...looking for host in foreign_clients_db
2026                 $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$receiver_host')";
2027                 $res = $foreign_clients_db->exec_statement($sql);
2028   
2029                                 # Host is known in foreign_clients_db 
2030                                 if (ref(@$res[0]) eq "ARRAY") { 
2031                     my $registration_server = @{@{$res}[0]}[2];
2032                     
2033                     # Fetch encryption key for registration server
2034                     my $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$registration_server')";
2035                     my $res = $known_server_db->exec_statement($sql);
2036                     if (ref(@$res[0]) eq "ARRAY") { 
2037                         my $registration_server_key = @{@{$res}[0]}[3];
2038                         my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
2039                         my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
2040                         my $error = &send_msg_to_target($out_msg, $registration_server, $registration_server_key, "usr_msg", 0); 
2041                         if ($error == 0 ) {
2042                             $send_succeed++ ;
2043                             $delivered2host++ ;
2044                             &daemon_log("M DEBUG: send message for user '$receiver' to server '$registration_server'", 7); 
2045                         } else {
2046                             &daemon_log("M ERROR: cannot send message for user '$receiver' to server '$registration_server'", 1); 
2047                         }
2049                     } else {
2050                         &daemon_log("M ERROR: host '$receiver_host' is reported to be ".
2051                                 "registrated at server '$registration_server', ".
2052                                 "but no data available in known_server_db ", 1); 
2053                     }
2054                 }
2055                 
2056                 if (not $delivered2host) {
2057                     &daemon_log("M ERROR: unable to send user message to host '$receiver_host'", 1);
2058                 }
2059                 }
2061                 if ($send_succeed) {
2062                                 # set outgoing msg at db to deliverd
2063                                 my $sql = "UPDATE $messaging_tn SET flag='d' WHERE (id='$msg_id' AND direction='out' AND message_to='$receiver')"; 
2064                                 my $res = $messaging_db->exec_statement($sql); 
2065                 &daemon_log("M INFO: send message for user '$receiver' to logged in hosts", 5);
2066                 } else {
2067             &daemon_log("M WARNING: failed to deliver message for user '$receiver'", 3); 
2068         }
2069         }
2071     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay); 
2072     return;
2076 sub watch_for_done_messages {
2077     my ($kernel,$heap) = @_[KERNEL, HEAP];
2079     my $sql = "SELECT * FROM $messaging_tn WHERE (flag='p' AND direction='in')"; 
2080     #&daemon_log("0 DEBUG: $sql", 7);
2081     my $res = $messaging_db->exec_statement($sql); 
2083     foreach my $hit (@{$res}) {
2084         my $msg_id = @{$hit}[0];
2086         my $sql = "SELECT * FROM $messaging_tn WHERE (id='$msg_id' AND direction='out' AND (NOT flag='s'))"; 
2087         #&daemon_log("0 DEBUG: $sql", 7); 
2088         my $res = $messaging_db->exec_statement($sql);
2090         # not all usr msgs have been seen till now
2091         if ( ref(@$res[0]) eq "ARRAY") { next; }
2092         
2093         $sql = "DELETE FROM $messaging_tn WHERE (id='$msg_id')"; 
2094         #&daemon_log("0 DEBUG: $sql", 7);
2095         $res = $messaging_db->exec_statement($sql);
2096     
2097     }
2099     $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay); 
2100     return;
2104 sub watch_for_old_known_clients {
2105     my ($kernel,$heap) = @_[KERNEL, HEAP];
2107     my $sql_statement = "SELECT * FROM $known_clients_tn";
2108     my $res = $known_clients_db->select_dbentry( $sql_statement );
2110     my $act_time = int(&get_time());
2112     while ( my ($hit_num, $hit) = each %$res) {
2113         my $expired_timestamp = int($hit->{'timestamp'});
2114         $expired_timestamp =~ /(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/;
2115         my $dt = DateTime->new( year   => $1,
2116                 month  => $2,
2117                 day    => $3,
2118                 hour   => $4,
2119                 minute => $5,
2120                 second => $6,
2121                 );
2123         $dt->add( seconds => 2 * int($hit->{'keylifetime'}) );
2124         $expired_timestamp = $dt->ymd('').$dt->hms('');
2125         if ($act_time > $expired_timestamp) {
2126             my $hostname = $hit->{'hostname'};
2127             my $del_sql = "DELETE FROM $known_clients_tn WHERE hostname='$hostname'"; 
2128             my $del_res = $known_clients_db->exec_statement($del_sql);
2130             &main::daemon_log("0 INFO: timestamp '".$hit->{'timestamp'}."' of client '$hostname' is expired('$expired_timestamp'), client will be deleted from known_clients_db", 5);
2131         }
2133     }
2135     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
2139 sub watch_for_next_tasks {
2140     my ($kernel,$heap) = @_[KERNEL, HEAP];
2142     my $sql = "SELECT * FROM $incoming_tn";
2143     my $res = $incoming_db->select_dbentry($sql);
2144     
2145     while ( my ($hit_num, $hit) = each %$res) {
2146         my $headertag = $hit->{'headertag'};
2147         if ($headertag =~ /^answer_(\d+)/) {
2148             # do not start processing, this message is for a still running POE::Wheel
2149             next;
2150         }
2151         my $message_id = $hit->{'id'};
2152         my $session_id = $hit->{'sessionid'};
2153         &daemon_log("$session_id DEBUG: start processing for message with incoming id: '$message_id'", 7);
2154         $kernel->yield('next_task', $hit);
2156         my $sql = "DELETE FROM $incoming_tn WHERE id=$message_id";
2157         my $res = $incoming_db->exec_statement($sql);
2158     }
2160     $kernel->delay_set('watch_for_next_tasks', 1); 
2164 sub get_ldap_handle {
2165         my ($session_id) = @_;
2166         my $heap;
2167         my $ldap_handle;
2169         if (not defined $session_id ) { $session_id = 0 };
2170         if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
2172         if ($session_id == 0) {
2173                 daemon_log("$session_id DEBUG: get_ldap_handle invoked without a session_id, create a new ldap_handle", 7); 
2174                 $ldap_handle = Net::LDAP->new( $ldap_uri );
2175                 if (defined $ldap_handle) {
2176                         $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!"); 
2177                 } else {
2178                         daemon_log("$session_id ERROR: creation of a new LDAP handle failed (ldap_uri '$ldap_uri')");
2179                 }
2181         } else {
2182                 my $session_reference = $global_kernel->ID_id_to_session($session_id);
2183                 if( defined $session_reference ) {
2184                         $heap = $session_reference->get_heap();
2185                 }
2187                 if (not defined $heap) {
2188                         daemon_log("$session_id DEBUG: cannot get heap for session_id '$session_id'", 7); 
2189                         return;
2190                 }
2192                 # TODO: This "if" is nonsense, because it doesn't prove that the
2193                 #       used handle is still valid - or if we've to reconnect...
2194                 #if (not exists $heap->{ldap_handle}) {
2195                         $ldap_handle = Net::LDAP->new( $ldap_uri );
2196                         $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!"); 
2197                         $heap->{ldap_handle} = $ldap_handle;
2198                 #}
2199         }
2200         return $ldap_handle;
2204 sub change_fai_state {
2205     my ($st, $targets, $session_id) = @_;
2206     $session_id = 0 if not defined $session_id;
2207     # Set FAI state to localboot
2208     my %mapActions= (
2209         reboot    => '',
2210         update    => 'softupdate',
2211         localboot => 'localboot',
2212         reinstall => 'install',
2213         rescan    => '',
2214         wake      => '',
2215         memcheck  => 'memcheck',
2216         sysinfo   => 'sysinfo',
2217         install   => 'install',
2218     );
2220     # Return if this is unknown
2221     if (!exists $mapActions{ $st }){
2222         daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1); 
2223       return;
2224     }
2226     my $state= $mapActions{ $st };
2228     my $ldap_handle = &get_ldap_handle($session_id);
2229     if( defined($ldap_handle) ) {
2231       # Build search filter for hosts
2232         my $search= "(&(objectClass=GOhard)";
2233         foreach (@{$targets}){
2234             $search.= "(macAddress=$_)";
2235         }
2236         $search.= ")";
2238       # If there's any host inside of the search string, procress them
2239         if (!($search =~ /macAddress/)){
2240             daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);    
2241             return;
2242         }
2244       # Perform search for Unit Tag
2245       my $mesg = $ldap_handle->search(
2246           base   => $ldap_base,
2247           scope  => 'sub',
2248           attrs  => ['dn', 'FAIstate', 'objectClass'],
2249           filter => "$search"
2250           );
2252           if ($mesg->count) {
2253                   my @entries = $mesg->entries;
2254                   if (0 == @entries) {
2255                                   daemon_log("$session_id ERROR: ldap search failed: ldap_base=$ldap_base, filter=$search", 1); 
2256                   }
2258                   foreach my $entry (@entries) {
2259                           # Only modify entry if it is not set to '$state'
2260                           if ($entry->get_value("FAIstate") ne "$state"){
2261                                   daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
2262                                   my $result;
2263                                   my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
2264                                   if (exists $tmp{'FAIobject'}){
2265                                           if ($state eq ''){
2266                                                   $result= $ldap_handle->modify($entry->dn, changes => [ delete => [ FAIstate => [] ] ]);
2267                                           } else {
2268                                                   $result= $ldap_handle->modify($entry->dn, changes => [ replace => [ FAIstate => $state ] ]);
2269                                           }
2270                                   } elsif ($state ne ''){
2271                                           $result= $ldap_handle->modify($entry->dn, changes => [ add => [ objectClass => 'FAIobject' ], add => [ FAIstate => $state ] ]);
2272                                   }
2274                                   # Errors?
2275                                   if ($result->code){
2276                                           daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2277                                   }
2278                           } else {
2279                                   daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 7); 
2280                           }  
2281                   }
2282           } else {
2283                 daemon_log("$session_id ERROR: LDAP search failed: ldap_base=$ldap_base, filter=$search", 1);
2284           }
2286     # if no ldap handle defined
2287     } else {
2288         daemon_log("$session_id ERROR: no LDAP handle defined for update FAIstate", 1); 
2289     }
2291         return;
2295 sub change_goto_state {
2296     my ($st, $targets, $session_id) = @_;
2297     $session_id = 0  if not defined $session_id;
2299     # Switch on or off?
2300     my $state= $st eq 'active' ? 'active': 'locked';
2302     my $ldap_handle = &get_ldap_handle($session_id);
2303     if( defined($ldap_handle) ) {
2305       # Build search filter for hosts
2306       my $search= "(&(objectClass=GOhard)";
2307       foreach (@{$targets}){
2308         $search.= "(macAddress=$_)";
2309       }
2310       $search.= ")";
2312       # If there's any host inside of the search string, procress them
2313       if (!($search =~ /macAddress/)){
2314         return;
2315       }
2317       # Perform search for Unit Tag
2318       my $mesg = $ldap_handle->search(
2319           base   => $ldap_base,
2320           scope  => 'sub',
2321           attrs  => ['dn', 'gotoMode'],
2322           filter => "$search"
2323           );
2325       if ($mesg->count) {
2326         my @entries = $mesg->entries;
2327         foreach my $entry (@entries) {
2329           # Only modify entry if it is not set to '$state'
2330           if ($entry->get_value("gotoMode") ne $state){
2332             daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
2333             my $result;
2334             $result= $ldap_handle->modify($entry->dn, changes => [replace => [ gotoMode => $state ] ]);
2336             # Errors?
2337             if ($result->code){
2338               &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2339             }
2341           }
2342         }
2343       } else {
2344                 daemon_log("$session_id ERROR: LDAP search failed in function change_goto_state: ldap_base=$ldap_base, filter=$search", 1);
2345           }
2347     }
2351 sub run_recreate_packages_db {
2352     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2353     my $session_id = $session->ID;
2354         &main::daemon_log("$session_id INFO: Recreating FAI Packages DB ('$fai_release_tn', '$fai_server_tn', '$packages_list_tn')", 5);
2355         $kernel->yield('create_fai_release_db', $fai_release_tn);
2356         $kernel->yield('create_fai_server_db', $fai_server_tn);
2357         return;
2361 sub run_create_fai_server_db {
2362     my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2363     my $session_id = $session->ID;
2364     my $task = POE::Wheel::Run->new(
2365             Program => sub { &create_fai_server_db($table_name,$kernel, undef, $session_id) },
2366             StdoutEvent  => "session_run_result",
2367             StderrEvent  => "session_run_debug",
2368             CloseEvent   => "session_run_done",
2369             );
2371     $heap->{task}->{ $task->ID } = $task;
2372     return;
2376 sub create_fai_server_db {
2377         my ($table_name, $kernel, $dont_create_packages_list, $session_id) = @_;
2378         my $result;
2380         if (not defined $session_id) { $session_id = 0; }
2381         my $ldap_handle = &get_ldap_handle();
2382         if(defined($ldap_handle)) {
2383                 daemon_log("$session_id INFO: create_fai_server_db: start", 5);
2384                 my $mesg= $ldap_handle->search(
2385                         base   => $ldap_base,
2386                         scope  => 'sub',
2387                         attrs  => ['FAIrepository', 'gosaUnitTag'],
2388                         filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
2389                 );
2390                 if($mesg->{'resultCode'} == 0 &&
2391                         $mesg->count != 0) {
2392                         foreach my $entry (@{$mesg->{entries}}) {
2393                                 if($entry->exists('FAIrepository')) {
2394                                         # Add an entry for each Repository configured for server
2395                                         foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
2396                                                 my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
2397                                                 my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
2398                                                 $result= $fai_server_db->add_dbentry( { 
2399                                                                 table => $table_name,
2400                                                                 primkey => ['server', 'fai_release', 'tag'],
2401                                                                 server => $tmp_url,
2402                                                                 fai_release => $tmp_release,
2403                                                                 sections => $tmp_sections,
2404                                                                 tag => (length($tmp_tag)>0)?$tmp_tag:"",
2405                                                         } );
2406                                         }
2407                                 }
2408                         }
2409                 }
2410                 daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
2412                 # TODO: Find a way to post the 'create_packages_list_db' event
2413                 if(not defined($dont_create_packages_list)) {
2414                         &create_packages_list_db(undef, undef, $session_id);
2415                 }
2416         }       
2418         $ldap_handle->disconnect;
2419         return $result;
2423 sub run_create_fai_release_db {
2424         my ($session, $heap, $table_name) = @_[SESSION, HEAP, ARG0];
2425         my $session_id = $session->ID;
2426         my $task = POE::Wheel::Run->new(
2427                 Program => sub { &create_fai_release_db($table_name, $session_id) },
2428                 StdoutEvent  => "session_run_result",
2429                 StderrEvent  => "session_run_debug",
2430                 CloseEvent   => "session_run_done",
2431         );
2433         $heap->{task}->{ $task->ID } = $task;
2434         return;
2438 sub create_fai_release_db {
2439         my ($table_name, $session_id) = @_;
2440         my $result;
2442         # used for logging
2443         if (not defined $session_id) { $session_id = 0; }
2445         my $ldap_handle = &get_ldap_handle();
2446         if(defined($ldap_handle)) {
2447                 daemon_log("$session_id INFO: create_fai_release_db: start",5);
2448                 my $mesg= $ldap_handle->search(
2449                         base   => $ldap_base,
2450                         scope  => 'sub',
2451                         attrs  => [],
2452                         filter => "(&(objectClass=organizationalUnit)(ou=fai))",
2453                 );
2454                 if($mesg->{'resultCode'} == 0 &&
2455                         $mesg->count != 0) {
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: Inserting ".scalar @sql_list." entries to DB",8);
2482                         if(@sql_list) {
2483                                 unshift @sql_list, "DELETE FROM $table_name";
2484                                 $fai_release_db->exec_statementlist(\@sql_list);
2485                         }
2486                         daemon_log("$session_id DEBUG: Done with inserting",7);
2487                 }
2488                 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
2489         }
2490         $ldap_handle->disconnect;
2491         return $result;
2494 sub get_fai_types {
2495         my $tmp_classes = shift || return undef;
2496         my @result;
2498         foreach my $type(keys %{$tmp_classes}) {
2499                 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
2500                         my $entry = {
2501                                 type => $type,
2502                                 state => $tmp_classes->{$type}[0],
2503                         };
2504                         push @result, $entry;
2505                 }
2506         }
2508         return @result;
2511 sub get_fai_state {
2512         my $result = "";
2513         my $tmp_classes = shift || return $result;
2515         foreach my $type(keys %{$tmp_classes}) {
2516                 if(defined($tmp_classes->{$type}[0])) {
2517                         $result = $tmp_classes->{$type}[0];
2518                         
2519                 # State is equal for all types in class
2520                         last;
2521                 }
2522         }
2524         return $result;
2527 sub resolve_fai_classes {
2528         my ($fai_base, $ldap_handle, $session_id) = @_;
2529         if (not defined $session_id) { $session_id = 0; }
2530         my $result;
2531         my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
2532         my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
2533         my $fai_classes;
2535         daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base",7);
2536         my $mesg= $ldap_handle->search(
2537                 base   => $fai_base,
2538                 scope  => 'sub',
2539                 attrs  => ['cn','objectClass','FAIstate'],
2540                 filter => $fai_filter,
2541         );
2542         daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries",7);
2544         if($mesg->{'resultCode'} == 0 &&
2545                 $mesg->count != 0) {
2546                 foreach my $entry (@{$mesg->{entries}}) {
2547                         if($entry->exists('cn')) {
2548                                 my $tmp_dn= $entry->dn();
2550                                 # Skip classname and ou dn parts for class
2551                                 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?),$fai_base$/;
2553                                 # Skip classes without releases
2554                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2555                                         next;
2556                                 }
2558                                 my $tmp_cn= $entry->get_value('cn');
2559                                 my $tmp_state= $entry->get_value('FAIstate');
2561                                 my $tmp_type;
2562                                 # Get FAI type
2563                                 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
2564                                         if(grep $_ eq $oclass, @possible_fai_classes) {
2565                                                 $tmp_type= $oclass;
2566                                                 last;
2567                                         }
2568                                 }
2570                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2571                                         # A Subrelease
2572                                         my @sub_releases = split(/,/, $tmp_release);
2574                                         # Walk through subreleases and build hash tree
2575                                         my $hash;
2576                                         while(my $tmp_sub_release = pop @sub_releases) {
2577                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2578                                         }
2579                                         eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
2580                                 } else {
2581                                         # A branch, no subrelease
2582                                         push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
2583                                 }
2584                         } elsif (!$entry->exists('cn')) {
2585                                 my $tmp_dn= $entry->dn();
2586                                 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?),$fai_base$/;
2588                                 # Skip classes without releases
2589                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2590                                         next;
2591                                 }
2593                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2594                                         # A Subrelease
2595                                         my @sub_releases= split(/,/, $tmp_release);
2597                                         # Walk through subreleases and build hash tree
2598                                         my $hash;
2599                                         while(my $tmp_sub_release = pop @sub_releases) {
2600                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2601                                         }
2602                                         # Remove the last two characters
2603                                         chop($hash);
2604                                         chop($hash);
2606                                         eval('$fai_classes->'.$hash.'= {}');
2607                                 } else {
2608                                         # A branch, no subrelease
2609                                         if(!exists($fai_classes->{$tmp_release})) {
2610                                                 $fai_classes->{$tmp_release} = {};
2611                                         }
2612                                 }
2613                         }
2614                 }
2616                 # The hash is complete, now we can honor the copy-on-write based missing entries
2617                 foreach my $release (keys %$fai_classes) {
2618                         $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
2619                 }
2620         }
2621         return $result;
2624 sub apply_fai_inheritance {
2625        my $fai_classes = shift || return {};
2626        my $tmp_classes;
2628        # Get the classes from the branch
2629        foreach my $class (keys %{$fai_classes}) {
2630                # Skip subreleases
2631                if($class =~ /^ou=.*$/) {
2632                        next;
2633                } else {
2634                        $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
2635                }
2636        }
2638        # Apply to each subrelease
2639        foreach my $subrelease (keys %{$fai_classes}) {
2640                if($subrelease =~ /ou=/) {
2641                        foreach my $tmp_class (keys %{$tmp_classes}) {
2642                                if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
2643                                        $fai_classes->{$subrelease}->{$tmp_class} =
2644                                        deep_copy($tmp_classes->{$tmp_class});
2645                                } else {
2646                                        foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
2647                                                if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
2648                                                        $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
2649                                                        deep_copy($tmp_classes->{$tmp_class}->{$type});
2650                                                }
2651                                        }
2652                                }
2653                        }
2654                }
2655        }
2657        # Find subreleases in deeper levels
2658        foreach my $subrelease (keys %{$fai_classes}) {
2659                if($subrelease =~ /ou=/) {
2660                        foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
2661                                if($subsubrelease =~ /ou=/) {
2662                                        apply_fai_inheritance($fai_classes->{$subrelease});
2663                                }
2664                        }
2665                }
2666        }
2668        return $fai_classes;
2671 sub get_fai_release_entries {
2672         my $tmp_classes = shift || return;
2673         my $parent = shift || "";
2674         my @result = shift || ();
2676         foreach my $entry (keys %{$tmp_classes}) {
2677                 if(defined($entry)) {
2678                         if($entry =~ /^ou=.*$/) {
2679                                 my $release_name = $entry;
2680                                 $release_name =~ s/ou=//g;
2681                                 if(length($parent)>0) {
2682                                         $release_name = $parent."/".$release_name;
2683                                 }
2684                                 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2685                                 foreach my $bufentry(@bufentries) {
2686                                         push @result, $bufentry;
2687                                 }
2688                         } else {
2689                                 my @types = get_fai_types($tmp_classes->{$entry});
2690                                 foreach my $type (@types) {
2691                                         push @result, 
2692                                         {
2693                                                 'class' => $entry,
2694                                                 'type' => $type->{'type'},
2695                                                 'release' => $parent,
2696                                                 'state' => $type->{'state'},
2697                                         };
2698                                 }
2699                         }
2700                 }
2701         }
2703         return @result;
2706 sub deep_copy {
2707         my $this = shift;
2708         if (not ref $this) {
2709                 $this;
2710         } elsif (ref $this eq "ARRAY") {
2711                 [map deep_copy($_), @$this];
2712         } elsif (ref $this eq "HASH") {
2713                 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2714         } else { die "what type is $_?" }
2718 sub session_run_result {
2719     my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];    
2720     $kernel->sig(CHLD => "child_reap");
2723 sub session_run_debug {
2724     my $result = $_[ARG0];
2725     print STDERR "$result\n";
2728 sub session_run_done {
2729     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2730     delete $heap->{task}->{$task_id};
2734 sub create_sources_list {
2735         my $session_id = shift;
2736         my $ldap_handle = &main::get_ldap_handle;
2737         my $result="/tmp/gosa_si_tmp_sources_list";
2739         # Remove old file
2740         if(stat($result)) {
2741                 unlink($result);
2742                 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7); 
2743         }
2745         my $fh;
2746         open($fh, ">$result");
2747         if (not defined $fh) {
2748                 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7); 
2749                 return undef;
2750         }
2751         if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2752                 my $mesg=$ldap_handle->search(
2753                         base    => $main::ldap_server_dn,
2754                         scope   => 'base',
2755                         attrs   => 'FAIrepository',
2756                         filter  => 'objectClass=FAIrepositoryServer'
2757                 );
2758                 if($mesg->count) {
2759                         foreach my $entry(@{$mesg->{'entries'}}) {
2760                                 foreach my $value(@{$entry->get_value('FAIrepository', asref => 1)}) {
2761                                         my ($server, $tag, $release, $sections)= split /\|/, $value;
2762                                         my $line = "deb $server $release";
2763                                         $sections =~ s/,/ /g;
2764                                         $line.= " $sections";
2765                                         print $fh $line."\n";
2766                                 }
2767                         }
2768                 }
2769         } else {
2770                 if (defined $main::ldap_server_dn){
2771                         &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1); 
2772                 } else {
2773                         &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2774                 }
2775         }
2776         close($fh);
2778         return $result;
2782 sub run_create_packages_list_db {
2783     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2784         my $session_id = $session->ID;
2786         my $task = POE::Wheel::Run->new(
2787                                         Priority => +20,
2788                                         Program => sub {&create_packages_list_db(undef, undef, $session_id)},
2789                                         StdoutEvent  => "session_run_result",
2790                                         StderrEvent  => "session_run_debug",
2791                                         CloseEvent   => "session_run_done",
2792                                         );
2793         $heap->{task}->{ $task->ID } = $task;
2797 sub create_packages_list_db {
2798         my ($ldap_handle, $sources_file, $session_id) = @_;
2799         
2800         # it should not be possible to trigger a recreation of packages_list_db
2801         # while packages_list_db is under construction, so set flag packages_list_under_construction
2802         # which is tested befor recreation can be started
2803         if (-r $packages_list_under_construction) {
2804                 daemon_log("$session_id WARNING: packages_list_db is right now under construction, please wait until this process is finished", 3);
2805                 return;
2806         } else {
2807                 daemon_log("$session_id INFO: create_packages_list_db: start", 5); 
2808                 # set packages_list_under_construction to true
2809                 system("touch $packages_list_under_construction");
2810                 @packages_list_statements=();
2811         }
2813         if (not defined $session_id) { $session_id = 0; }
2814         if (not defined $ldap_handle) { 
2815                 $ldap_handle= &get_ldap_handle();
2817                 if (not defined $ldap_handle) {
2818                         daemon_log("$session_id ERROR: no ldap_handle available to create_packages_list_db", 1);
2819                         unlink($packages_list_under_construction);
2820                         return;
2821                 }
2822         }
2823         if (not defined $sources_file) { 
2824                 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5); 
2825                 $sources_file = &create_sources_list($session_id);
2826         }
2828         if (not defined $sources_file) {
2829                 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1); 
2830                 unlink($packages_list_under_construction);
2831                 return;
2832         }
2834         my $line;
2836         open(CONFIG, "<$sources_file") or do {
2837                 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2838                 unlink($packages_list_under_construction);
2839                 return;
2840         };
2842         # Read lines
2843         while ($line = <CONFIG>){
2844                 # Unify
2845                 chop($line);
2846                 $line =~ s/^\s+//;
2847                 $line =~ s/^\s+/ /;
2849                 # Strip comments
2850                 $line =~ s/#.*$//g;
2852                 # Skip empty lines
2853                 if ($line =~ /^\s*$/){
2854                         next;
2855                 }
2857                 # Interpret deb line
2858                 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2859                         my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2860                         my $section;
2861                         foreach $section (split(' ', $sections)){
2862                                 &parse_package_info( $baseurl, $dist, $section, $session_id );
2863                         }
2864                 }
2865         }
2867         close (CONFIG);
2869         if(keys(%repo_dirs)) {
2870                 find(\&cleanup_and_extract, keys( %repo_dirs ));
2871                 &main::strip_packages_list_statements();
2872                 $packages_list_db->exec_statementlist(\@packages_list_statements);
2873         }
2874         unlink($packages_list_under_construction);
2875         daemon_log("$session_id INFO: create_packages_list_db: finished", 5); 
2876         return;
2879 # This function should do some intensive task to minimize the db-traffic
2880 sub strip_packages_list_statements {
2881         my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2882         my @new_statement_list=();
2883         my $hash;
2884         my $insert_hash;
2885         my $update_hash;
2886         my $delete_hash;
2887         my $known_packages_hash;
2888         my $local_timestamp=get_time();
2890         foreach my $existing_entry (@existing_entries) {
2891                 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
2892         }
2894         foreach my $statement (@packages_list_statements) {
2895                 if($statement =~ /^INSERT/i) {
2896                         # Assign the values from the insert statement
2897                         my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~ 
2898                         /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
2899                         if(exists($hash->{$distribution}->{$package}->{$version})) {
2900                                 # If section or description has changed, update the DB
2901                                 if( 
2902                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or 
2903                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
2904                                 ) {
2905                                         @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
2906                                 } else {
2907                                         # package is already present in database. cache this knowledge for later use
2908                                         @{$known_packages_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2909                                 }
2910                         } else {
2911                                 # Insert a non-existing entry to db
2912                                 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2913                         }
2914                 } elsif ($statement =~ /^UPDATE/i) {
2915                         my ($template,$package,$version) = ($1,$2,$3) if $statement =~
2916                         /^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;
2917                         foreach my $distribution (keys %{$hash}) {
2918                                 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
2919                                         # update the insertion hash to execute only one query per package (insert instead insert+update)
2920                                         @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
2921                                 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
2922                                         if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
2923                                                 my $section;
2924                                                 my $description;
2925                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
2926                                                         length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
2927                                                         $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
2928                                                 }
2929                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2930                                                         $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
2931                                                 }
2932                                                 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2933                                         }
2934                                 }
2935                         }
2936                 }
2937         }
2939         # Check for orphaned entries
2940         foreach my $existing_entry (@existing_entries) {
2941                 my $distribution= @{$existing_entry}[0];
2942                 my $package= @{$existing_entry}[1];
2943                 my $version= @{$existing_entry}[2];
2944                 my $section= @{$existing_entry}[3];
2946                 if(
2947                         exists($insert_hash->{$distribution}->{$package}->{$version}) ||
2948                         exists($update_hash->{$distribution}->{$package}->{$version}) ||
2949                         exists($known_packages_hash->{$distribution}->{$package}->{$version})
2950                 ) {
2951                         next;
2952                 } else {
2953                         # Insert entry to delete hash
2954                         @{$delete_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section);
2955                 }
2956         }
2958         # unroll the insert hash
2959         foreach my $distribution (keys %{$insert_hash}) {
2960                 foreach my $package (keys %{$insert_hash->{$distribution}}) {
2961                         foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
2962                                 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
2963                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
2964                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
2965                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
2966                                 ."'$local_timestamp')";
2967                         }
2968                 }
2969         }
2971         # unroll the update hash
2972         foreach my $distribution (keys %{$update_hash}) {
2973                 foreach my $package (keys %{$update_hash->{$distribution}}) {
2974                         foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
2975                                 my $set = "";
2976                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
2977                                         $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
2978                                 }
2979                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2980                                         $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
2981                                 }
2982                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
2983                                         $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
2984                                 }
2985                                 if(defined($set) and length($set) > 0) {
2986                                         $set .= "timestamp = '$local_timestamp'";
2987                                 } else {
2988                                         next;
2989                                 }
2990                                 push @new_statement_list, 
2991                                 "UPDATE $main::packages_list_tn SET $set WHERE"
2992                                 ." distribution = '$distribution'"
2993                                 ." AND package = '$package'"
2994                                 ." AND version = '$version'";
2995                         }
2996                 }
2997         }
2998         
2999         # unroll the delete hash
3000         foreach my $distribution (keys %{$delete_hash}) {
3001                 foreach my $package (keys %{$delete_hash->{$distribution}}) {
3002                         foreach my $version (keys %{$delete_hash->{$distribution}->{$package}}) {
3003                                 my $section = @{$delete_hash->{$distribution}->{$package}->{$version}}[3];
3004                                 push @new_statement_list, "DELETE FROM $main::packages_list_tn WHERE distribution='$distribution' AND package='$package' AND version='$version' AND section='$section'";
3005                         }
3006                 }
3007         }
3009         @packages_list_statements = @new_statement_list;
3013 sub parse_package_info {
3014     my ($baseurl, $dist, $section, $session_id)= @_;
3015     my ($package);
3016     if (not defined $session_id) { $session_id = 0; }
3017     my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
3018     $repo_dirs{ "${repo_path}/pool" } = 1;
3020     foreach $package ("Packages.gz"){
3021         daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
3022         get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
3023         parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
3024     }
3025     
3029 sub get_package {
3030     my ($url, $dest, $session_id)= @_;
3031     if (not defined $session_id) { $session_id = 0; }
3033     my $tpath = dirname($dest);
3034     -d "$tpath" || mkpath "$tpath";
3036     # This is ugly, but I've no time to take a look at "how it works in perl"
3037     if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
3038         system("gunzip -cd '$dest' > '$dest.in'");
3039         daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 5);
3040         unlink($dest);
3041         daemon_log("$session_id DEBUG: delete file '$dest'", 5); 
3042     } else {
3043         daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' failed!", 1);
3044     }
3045     return 0;
3049 sub parse_package {
3050     my ($path, $dist, $srv_path, $session_id)= @_;
3051     if (not defined $session_id) { $session_id = 0;}
3052     my ($package, $version, $section, $description);
3053     my $PACKAGES;
3054     my $timestamp = &get_time();
3056     if(not stat("$path.in")) {
3057         daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
3058         return;
3059     }
3061     open($PACKAGES, "<$path.in");
3062     if(not defined($PACKAGES)) {
3063         daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1); 
3064         return;
3065     }
3067     # Read lines
3068     while (<$PACKAGES>){
3069         my $line = $_;
3070         # Unify
3071         chop($line);
3073         # Use empty lines as a trigger
3074         if ($line =~ /^\s*$/){
3075             my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
3076             push(@packages_list_statements, $sql);
3077             $package = "none";
3078             $version = "none";
3079             $section = "none";
3080             $description = "none"; 
3081             next;
3082         }
3084         # Trigger for package name
3085         if ($line =~ /^Package:\s/){
3086             ($package)= ($line =~ /^Package: (.*)$/);
3087             next;
3088         }
3090         # Trigger for version
3091         if ($line =~ /^Version:\s/){
3092             ($version)= ($line =~ /^Version: (.*)$/);
3093             next;
3094         }
3096         # Trigger for description
3097         if ($line =~ /^Description:\s/){
3098             ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
3099             next;
3100         }
3102         # Trigger for section
3103         if ($line =~ /^Section:\s/){
3104             ($section)= ($line =~ /^Section: (.*)$/);
3105             next;
3106         }
3108         # Trigger for filename
3109         if ($line =~ /^Filename:\s/){
3110             my ($filename) = ($line =~ /^Filename: (.*)$/);
3111             store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
3112             next;
3113         }
3114     }
3116     close( $PACKAGES );
3117     unlink( "$path.in" );
3121 sub store_fileinfo {
3122     my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
3124     my %fileinfo = (
3125         'package' => $package,
3126         'dist' => $dist,
3127         'version' => $vers,
3128     );
3130     $repo_files{ "${srvdir}/$file" } = \%fileinfo;
3134 sub cleanup_and_extract {
3135         my $fileinfo = $repo_files{ $File::Find::name };
3137         if( defined $fileinfo ) {
3138                 my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
3139                 my $sql;
3140                 my $package = $fileinfo->{ 'package' };
3141                 my $newver = $fileinfo->{ 'version' };
3143                 mkpath($dir);
3144                 system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
3146                 if( -f "$dir/DEBIAN/templates" ) {
3148                         daemon_log("0 DEBUG: Found debconf templates in '$package' - $newver", 7);
3150                         my $tmpl= ""; {
3151                                 local $/=undef;
3152                                 open FILE, "$dir/DEBIAN/templates";
3153                                 $tmpl = &encode_base64(<FILE>);
3154                                 close FILE;
3155                         }
3156                         rmtree("$dir/DEBIAN/templates");
3158                         $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
3159                         push @packages_list_statements, $sql;
3160                 }
3161         }
3163         return;
3167 sub register_at_foreign_servers {   
3168     my ($kernel) = $_[KERNEL];
3170     # hole alle bekannten server aus known_server_db
3171     my $server_sql = "SELECT * FROM $known_server_tn";
3172     my $server_res = $known_server_db->exec_statement($server_sql);
3174     # no entries in known_server_db
3175     if (not ref(@$server_res[0]) eq "ARRAY") { 
3176         # TODO
3177     }
3179     # detect already connected clients
3180     my $client_sql = "SELECT * FROM $known_clients_tn"; 
3181     my $client_res = $known_clients_db->exec_statement($client_sql);
3183     # send my server details to all other gosa-si-server within the network
3184     foreach my $hit (@$server_res) {
3185         my $hostname = @$hit[0];
3186         my $hostkey = &create_passwd;
3188         # add already connected clients to registration message 
3189         my $myhash = &create_xml_hash('new_server', $server_address, $hostname);
3190         &add_content2xml_hash($myhash, 'key', $hostkey);
3191         map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res);
3193         # add locally loaded gosa-si modules to registration message
3194         my $loaded_modules = {};
3195         while (my ($package, $pck_info) = each %$known_modules) {
3196                                                 next if ((!defined(@$pck_info[2])) || (!(ref (@$pck_info[2]) eq 'HASH')));
3197                                                 foreach my $act_module (keys(%{@$pck_info[2]})) {
3198                                                         $loaded_modules->{$act_module} = ""; 
3199                                                 }
3200         }
3202         map(&add_content2xml_hash($myhash, "loaded_modules", $_), keys(%$loaded_modules));
3204         # add macaddress to registration message
3205         my ($host_ip, $host_port) = split(/:/, $hostname);
3206         my $local_ip = &get_local_ip_for_remote_ip($host_ip);
3207         my $network_interface= &get_interface_for_ip($local_ip);
3208         my $host_mac = &get_mac_for_interface($network_interface);
3209         &add_content2xml_hash($myhash, 'macaddress', $host_mac);
3210         
3211         # build registration message and send it
3212         my $foreign_server_msg = &create_xml_string($myhash);
3213         my $error = &send_msg_to_target($foreign_server_msg, $hostname, $ServerPackages_key, "new_server", 0); 
3214     }
3215     
3216     $kernel->delay_set("register_at_foreign_servers", $foreign_servers_register_delay); 
3217     return;
3221 #==== MAIN = main ==============================================================
3222 #  parse commandline options
3223 Getopt::Long::Configure( "bundling" );
3224 GetOptions("h|help" => \&usage,
3225         "c|config=s" => \$cfg_file,
3226         "f|foreground" => \$foreground,
3227         "v|verbose+" => \$verbose,
3228         "no-arp+" => \$no_arp,
3229            );
3231 #  read and set config parameters
3232 &check_cmdline_param ;
3233 &read_configfile($cfg_file, %cfg_defaults);
3234 &check_pid;
3236 $SIG{CHLD} = 'IGNORE';
3238 # forward error messages to logfile
3239 if( ! $foreground ) {
3240   open( STDIN,  '+>/dev/null' );
3241   open( STDOUT, '+>&STDIN'    );
3242   open( STDERR, '+>&STDIN'    );
3245 # Just fork, if we are not in foreground mode
3246 if( ! $foreground ) { 
3247     chdir '/'                 or die "Can't chdir to /: $!";
3248     $pid = fork;
3249     setsid                    or die "Can't start a new session: $!";
3250     umask 0;
3251 } else { 
3252     $pid = $$; 
3255 # Do something useful - put our PID into the pid_file
3256 if( 0 != $pid ) {
3257     open( LOCK_FILE, ">$pid_file" );
3258     print LOCK_FILE "$pid\n";
3259     close( LOCK_FILE );
3260     if( !$foreground ) { 
3261         exit( 0 ) 
3262     };
3265 # parse head url and revision from svn
3266 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
3267 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
3268 $server_headURL = defined $1 ? $1 : 'unknown' ;
3269 $server_revision = defined $2 ? $2 : 'unknown' ;
3270 if ($server_headURL =~ /\/tag\// || 
3271         $server_headURL =~ /\/branches\// ) {
3272     $server_status = "stable"; 
3273 } else {
3274     $server_status = "developmental" ;
3277 # Prepare log file
3278 $root_uid = getpwnam('root');
3279 $adm_gid = getgrnam('adm');
3280 chmod(0640, $log_file);
3281 chown($root_uid, $adm_gid, $log_file);
3282 chown($root_uid, $adm_gid, "/var/lib/gosa-si");
3284 daemon_log(" ", 1);
3285 daemon_log("$0 started!", 1);
3286 daemon_log("status: $server_status", 1);
3287 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1); 
3290     no strict "refs";
3292     if ($db_module eq "DBmysql") {
3293         # connect to incoming_db
3294         $incoming_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3296         # connect to gosa-si job queue
3297         $job_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3299         # connect to known_clients_db
3300         $known_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3302         # connect to foreign_clients_db
3303         $foreign_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3305         # connect to known_server_db
3306         $known_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3308         # connect to login_usr_db
3309         $login_users_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3311         # connect to fai_server_db 
3312         $fai_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3314         # connect to fai_release_db
3315         $fai_release_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3317         # connect to packages_list_db
3318         $packages_list_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3320         # connect to messaging_db
3321         $messaging_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3323     } elsif ($db_module eq "DBsqlite") {
3324         # connect to incoming_db
3325         unlink($incoming_file_name);
3326         $incoming_db = GOSA::DBsqlite->new($incoming_file_name);
3327         
3328         # connect to gosa-si job queue
3329         unlink($job_queue_file_name);  ## just for debugging
3330         $job_db = GOSA::DBsqlite->new($job_queue_file_name);
3331         chmod(0660, $job_queue_file_name);
3332         chown($root_uid, $adm_gid, $job_queue_file_name);
3333         
3334         # connect to known_clients_db
3335         unlink($known_clients_file_name);   ## just for debugging
3336         $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
3337         chmod(0660, $known_clients_file_name);
3338         chown($root_uid, $adm_gid, $known_clients_file_name);
3339         
3340         # connect to foreign_clients_db
3341         unlink($foreign_clients_file_name);
3342         $foreign_clients_db = GOSA::DBsqlite->new($foreign_clients_file_name);
3343         chmod(0660, $foreign_clients_file_name);
3344         chown($root_uid, $adm_gid, $foreign_clients_file_name);
3345         
3346         # connect to known_server_db
3347         unlink($known_server_file_name);
3348         $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
3349         chmod(0660, $known_server_file_name);
3350         chown($root_uid, $adm_gid, $known_server_file_name);
3351         
3352         # connect to login_usr_db
3353         unlink($login_users_file_name);
3354         $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
3355         chmod(0660, $login_users_file_name);
3356         chown($root_uid, $adm_gid, $login_users_file_name);
3357         
3358         # connect to fai_server_db
3359         unlink($fai_server_file_name);
3360         $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
3361         chmod(0660, $fai_server_file_name);
3362         chown($root_uid, $adm_gid, $fai_server_file_name);
3363         
3364         # connect to fai_release_db
3365         unlink($fai_release_file_name);
3366         $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
3367         chmod(0660, $fai_release_file_name);
3368         chown($root_uid, $adm_gid, $fai_release_file_name);
3369         
3370         # connect to packages_list_db
3371         #unlink($packages_list_file_name);
3372         unlink($packages_list_under_construction);
3373         $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
3374         chmod(0660, $packages_list_file_name);
3375         chown($root_uid, $adm_gid, $packages_list_file_name);
3376         
3377         # connect to messaging_db
3378         unlink($messaging_file_name);
3379         $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
3380         chmod(0660, $messaging_file_name);
3381         chown($root_uid, $adm_gid, $messaging_file_name);
3382     }
3385 # Creating tables
3386 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
3387 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
3388 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
3389 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
3390 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
3391 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
3392 $foreign_clients_db->create_table($foreign_clients_tn, \@foreign_clients_col_names);
3393 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
3394 $incoming_db->create_table($incoming_tn, \@incoming_col_names);
3395 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
3398 # create xml object used for en/decrypting
3399 $xml = new XML::Simple();
3402 # foreign servers 
3403 my @foreign_server_list;
3405 # add foreign server from cfg file
3406 if ($foreign_server_string ne "") {
3407     my @cfg_foreign_server_list = split(",", $foreign_server_string);
3408     foreach my $foreign_server (@cfg_foreign_server_list) {
3409         push(@foreign_server_list, $foreign_server);
3410     }
3412     daemon_log("0 INFO: found foreign server in config file: ".join(", ", @foreign_server_list), 5);
3415 # Perform a DNS lookup for server registration if flag is true
3416 if ($dns_lookup eq "true") {
3417     # Add foreign server from dns
3418     my @tmp_servers;
3419     if (not $server_domain) {
3420         # Try our DNS Searchlist
3421         for my $domain(get_dns_domains()) {
3422             chomp($domain);
3423             my ($tmp_domains, $error_string) = &get_server_addresses($domain);
3424             if(@$tmp_domains) {
3425                 for my $tmp_server(@$tmp_domains) {
3426                     push @tmp_servers, $tmp_server;
3427                 }
3428             }
3429         }
3430         if(@tmp_servers && length(@tmp_servers)==0) {
3431             daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3432         }
3433     } else {
3434         @tmp_servers = &get_server_addresses($server_domain);
3435         if( 0 == @tmp_servers ) {
3436             daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3437         }
3438     }
3440     daemon_log("0 INFO: found foreign server via DNS ".join(", ", @tmp_servers), 5);    
3442     foreach my $server (@tmp_servers) { 
3443         unshift(@foreign_server_list, $server); 
3444     }
3445 } else {
3446     daemon_log("0 INFO: DNS lookup for server registration is disabled", 5);
3450 # eliminate duplicate entries
3451 @foreign_server_list = &del_doubles(@foreign_server_list);
3452 my $all_foreign_server = join(", ", @foreign_server_list);
3453 daemon_log("0 INFO: found foreign server in config file and DNS: '$all_foreign_server'", 5);
3455 # add all found foreign servers to known_server
3456 my $act_timestamp = &get_time();
3457 foreach my $foreign_server (@foreign_server_list) {
3459         # do not add myself to known_server_db
3460         if (&is_local($foreign_server)) { next; }
3461         ######################################
3463     my $res = $known_server_db->add_dbentry( {table=>$known_server_tn, 
3464             primkey=>['hostname'],
3465             hostname=>$foreign_server,
3466             macaddress=>"",
3467             status=>'not_jet_registered',
3468             hostkey=>"none",
3469             loaded_modules => "none", 
3470             timestamp=>$act_timestamp,
3471             } );
3475 # Import all modules
3476 &import_modules;
3478 # Check wether all modules are gosa-si valid passwd check
3479 &password_check;
3481 # Prepare for using Opsi 
3482 if ($opsi_enabled eq "true") {
3483     use JSON::RPC::Client;
3484     use XML::Quote qw(:all);
3485     $opsi_url= "https://".$opsi_admin.":".$opsi_password."@".$opsi_server.":4447/rpc";
3486     $opsi_client = new JSON::RPC::Client;
3490 POE::Component::Server::TCP->new(
3491         Alias => "TCP_SERVER",
3492         Port => $server_port,
3493         ClientInput => sub {
3494                 my ($kernel, $input, $heap, $session) = @_[KERNEL, ARG0, HEAP, SESSION];
3495         my $session_id = $session->ID;
3496         my $remote_ip = $heap->{'remote_ip'};
3497                 push(@msgs_to_decrypt, $input);
3498         &daemon_log("$session_id DEBUG: incoming message from '$remote_ip'", 7);
3499                 $kernel->yield("msg_to_decrypt");
3500         },
3501         InlineStates => {
3502                 msg_to_decrypt => \&msg_to_decrypt,
3503                 next_task => \&next_task,
3504                 task_result => \&handle_task_result,
3505                 task_done   => \&handle_task_done,
3506                 task_debug  => \&handle_task_debug,
3507                 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3508         }
3509 );
3511 daemon_log("0 INFO: start socket for incoming xml messages at port '$server_port' ", 1);
3513 # create session for repeatedly checking the job queue for jobs
3514 POE::Session->create(
3515         inline_states => {
3516                 _start => \&session_start,
3517         register_at_foreign_servers => \&register_at_foreign_servers,
3518         sig_handler => \&sig_handler,
3519         next_task => \&next_task,
3520         task_result => \&handle_task_result,
3521         task_done   => \&handle_task_done,
3522         task_debug  => \&handle_task_debug,
3523         watch_for_next_tasks => \&watch_for_next_tasks,
3524         watch_for_new_messages => \&watch_for_new_messages,
3525         watch_for_delivery_messages => \&watch_for_delivery_messages,
3526         watch_for_done_messages => \&watch_for_done_messages,
3527                 watch_for_new_jobs => \&watch_for_new_jobs,
3528         watch_for_modified_jobs => \&watch_for_modified_jobs,
3529         watch_for_done_jobs => \&watch_for_done_jobs,
3530         watch_for_opsi_jobs => \&watch_for_opsi_jobs,
3531         watch_for_old_known_clients => \&watch_for_old_known_clients,
3532         create_packages_list_db => \&run_create_packages_list_db,
3533         create_fai_server_db => \&run_create_fai_server_db,
3534         create_fai_release_db => \&run_create_fai_release_db,
3535                 recreate_packages_db => \&run_recreate_packages_db,
3536         session_run_result => \&session_run_result,
3537         session_run_debug => \&session_run_debug,
3538         session_run_done => \&session_run_done,
3539         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3540         }
3541 );
3544 POE::Kernel->run();
3545 exit;