Code

4cb878816532c0244ed44ff88afa3095446e9d65
[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)");
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     if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
1009         my $jobdb_id = $1;
1010     
1011         $answer =~ /<header>(.*)<\/header>/;
1012         my $job_header = $1;
1014         $answer =~ /<target>(.*)<\/target>/;
1015         my $job_target = $1;
1016             
1017         # sending msg failed
1018         if( $error ) {
1020             # JUST FOR TESTING
1021             daemon_log("D \n$error\n$answer");
1023             # set jobs to done, jobs do not need to deliver their message in any case
1024             if (($job_header eq "trigger_action_localboot")
1025                     ||($job_header eq "trigger_action_lock")
1026                     ||($job_header eq "trigger_action_halt") 
1027                     ) {
1028                 my $sql_statement = "UPDATE $job_queue_tn SET status='done' WHERE id=$jobdb_id";
1029                 my $res = $job_db->update_dbentry($sql_statement);
1031             # reactivate jobs, jobs need to deliver their message
1032             } elsif (($job_header eq "trigger_action_activate")
1033                     ||($job_header eq "trigger_action_update")
1034                     ||($job_header eq "trigger_action_reinstall") 
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                 my $res = $job_db->update_dbentry($sql_statement);
1044                 &daemon_log("D \n$sql_statement\n$answer", 1);
1046             }
1048         # sending msg was successful
1049         } else {
1050             # set jobs localboot, lock, activate, halt, reboot and wake to done
1051             # jobs reinstall, update, inst_update do themself setting to done
1052             if (($job_header eq "trigger_action_localboot")
1053                     ||($job_header eq "trigger_action_lock")
1054                     ||($job_header eq "trigger_action_activate")
1055                     ||($job_header eq "trigger_action_halt") 
1056                     ||($job_header eq "trigger_action_reboot")
1057                     ||($job_header eq "trigger_action_wake")
1058                     ) {
1060                 my $sql_statement = "UPDATE $job_queue_tn ".
1061                     "SET status='done' ".
1062                     "WHERE id=$jobdb_id AND status='processed'";
1063                 my $res = $job_db->update_dbentry($sql_statement);
1064             }
1065         }
1066     }
1069 sub reactivate_job_with_delay {
1070     my ($session_id, $target, $header, $delay) = @_ ;
1071     # 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
1072     
1073     if (not defined $delay) { $delay = 30 } ;
1074     my $delay_timestamp = &calc_timestamp(&get_time(), "plus", $delay);
1076     my $sql = "UPDATE $job_queue_tn Set timestamp='$delay_timestamp', status='waiting' WHERE (macaddress='$target' AND headertag='$header')"; 
1077     my $res = $job_db->update_dbentry($sql);
1078     daemon_log("$session_id INFO: '$header'-job will be reactivated at '$delay_timestamp' ".
1079             "cause client '$target' is currently not available", 5);
1080     daemon_log("$session_id $sql", 7);                             
1081     return;
1085 sub sig_handler {
1086         my ($kernel, $signal) = @_[KERNEL, ARG0] ;
1087         daemon_log("0 INFO got signal '$signal'", 1); 
1088         $kernel->sig_handled();
1089         return;
1093 sub msg_to_decrypt {
1094         my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
1095         my $session_id = $session->ID;
1096         my ($msg, $msg_hash, $module);
1097         my $error = 0;
1099         # hole neue msg aus @msgs_to_decrypt
1100         my $next_msg = shift @msgs_to_decrypt;
1102         # msg is from a new client or gosa
1103         ($msg, $msg_hash, $module) = &input_from_unknown_host($next_msg, $session_id);
1105         # msg is from a gosa-si-server
1106         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1107                 ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $heap->{'remote_ip'}, $session_id);
1108         }
1109         # msg is from a gosa-si-client
1110         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1111                 ($msg, $msg_hash, $module) = &input_from_known_client($next_msg, $heap->{'remote_ip'}, $session_id);
1112         }
1113         # an error occurred
1114         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1115                 # if an incoming msg could not be decrypted (maybe a wrong key), send client a ping. If the client
1116                 # could not understand a msg from its server the client cause a re-registering process
1117                 daemon_log("$session_id WARNING cannot understand incoming msg, send 'ping'-msg to all host with ip '".$heap->{remote_ip}.
1118                         "' to cause a re-registering of the client if necessary", 3);
1119                 my $sql_statement = "SELECT * FROM $main::known_clients_tn WHERE (hostname LIKE '".$heap->{'remote_ip'}."%')";
1120                 my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
1121                 while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1122                         my $host_name = $hit->{'hostname'};
1123                         my $host_key = $hit->{'hostkey'};
1124                         my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source> <target>$host_name</target></xml>";
1125                         my $error = &send_msg_to_target($ping_msg, $host_name, $host_key, "gosa_ping", $session_id);
1126                         &update_jobdb_status_for_send_msgs($ping_msg, $error);
1127                 }
1128                 $error++;
1129         }
1132         my $header;
1133         my $target;
1134         my $source;
1135         my $done = 0;
1136         my $sql;
1137         my $res;
1139         # check whether this message should be processed here
1140         if ($error == 0) {
1141                 $header = @{$msg_hash->{'header'}}[0];
1142                 $target = @{$msg_hash->{'target'}}[0];
1143                 $source = @{$msg_hash->{'source'}}[0];
1144                 my $not_found_in_known_clients_db = 0;
1145                 my $not_found_in_known_server_db = 0;
1146                 my $not_found_in_foreign_clients_db = 0;
1147                 my $local_address;
1148                 my $local_mac;
1149                 my ($target_ip, $target_port) = split(':', $target);
1151                 # Determine the local ip address if target is an ip address
1152                 if ($target =~ /^\d+\.\d+\.\d+\.\d+:\d+$/) {
1153                         $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1154                 } else {
1155                         $local_address = $server_address;
1156                 }
1158                 # Determine the local mac address if target is a mac address
1159                 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) {
1160                         my $loc_ip = &get_local_ip_for_remote_ip($heap->{'remote_ip'});
1161                         my $network_interface= &get_interface_for_ip($loc_ip);
1162                         $local_mac = &get_mac_for_interface($network_interface);
1163                 } else {
1164                         $local_mac = $server_mac_address;
1165                 }
1167                 # target and source is equal to GOSA -> process here
1168                 if (not $done) {
1169                         if ($target eq "GOSA" && $source eq "GOSA") {
1170                                 $done = 1;                    
1171                                 &daemon_log("$session_id DEBUG: target and source is 'GOSA' -> process here", 7);
1172                         }
1173                 }
1175                 # target is own address without forward_to_gosa-tag -> process here
1176                 if (not $done) {
1177                         #if ((($target eq $local_address) || ($target eq $local_mac) ) && (not exists $msg_hash->{'forward_to_gosa'})) {
1178                         if (($target eq $local_address) && (not exists $msg_hash->{'forward_to_gosa'})) {
1179                                 $done = 1;
1180                                 if ($source eq "GOSA") {
1181                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1182                                 }
1183                                 &daemon_log("$session_id DEBUG: target is own address without forward_to_gosa-tag -> process here", 7);
1184                         }
1185                 }
1187                 # target is a client address in known_clients -> process here
1188                 if (not $done) {
1189                         $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')"; 
1190                         $res = $known_clients_db->select_dbentry($sql);
1191                         if (keys(%$res) > 0) {
1192                                 $done = 1; 
1193                                 my $hostname = $res->{1}->{'hostname'};
1194                                 $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1195                                 my $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1196                                 if ($source eq "GOSA") {
1197                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1198                                 }
1199                                 &daemon_log("$session_id DEBUG: target is a client address in known_clients -> process here", 7);
1201                         } else {
1202                                 $not_found_in_known_clients_db = 1;
1203                         }
1204                 }
1206                 # target ist own address with forward_to_gosa-tag not pointing to myself -> process here
1207                 if (not $done) {
1208                         my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1209                         my $gosa_at;
1210                         my $gosa_session_id;
1211                         if (($target eq $local_address) && (defined $forward_to_gosa)){
1212                                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1213                                 if ($gosa_at ne $local_address) {
1214                                         $done = 1;
1215                                         &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag not pointing to myself -> process here", 7); 
1216                                 }
1217                         }
1218                 }
1220                 # if message should be processed here -> add message to incoming_db
1221                 if ($done) {
1222                         # if a job or a gosa message comes from a foreign server, fake module to GosaPackages
1223                         # so gosa-si-server knows how to process this kind of messages
1224                         if ($header =~ /^gosa_/ || $header =~ /^job_/) {
1225                                 $module = "GosaPackages";
1226                         }
1228                         my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1229                                         primkey=>[],
1230                                         headertag=>$header,
1231                                         targettag=>$target,
1232                                         xmlmessage=>&encode_base64($msg),
1233                                         timestamp=>&get_time,
1234                                         module=>$module,
1235                                         sessionid=>$session_id,
1236                                 } );
1238                 }
1240                 # target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa
1241                 if (not $done) {
1242                         my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1243                         my $gosa_at;
1244                         my $gosa_session_id;
1245                         if (($target eq $local_address) && (defined $forward_to_gosa)){
1246                                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1247                                 if ($gosa_at eq $local_address) {
1248                                         my $session_reference = $kernel->ID_id_to_session($gosa_session_id);
1249                                         if( defined $session_reference ) {
1250                                                 $heap = $session_reference->get_heap();
1251                                         }
1252                                         if(exists $heap->{'client'}) {
1253                                                 $msg = &encrypt_msg($msg, $GosaPackages_key);
1254                                                 $heap->{'client'}->put($msg);
1255                                                 &daemon_log("$session_id INFO: incoming '$header' message forwarded to GOsa", 5); 
1256                                         }
1257                                         $done = 1;
1258                                         &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa", 7);
1259                                 }
1260                         }
1262                 }
1264                 # target is a client address in foreign_clients -> forward to registration server
1265                 if (not $done) {
1266                         $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1267                         $res = $foreign_clients_db->select_dbentry($sql);
1268                         if (keys(%$res) > 0) {
1269                                 my $hostname = $res->{1}->{'hostname'};
1270                                 my ($host_ip, $host_port) = split(/:/, $hostname);
1271                                 my $local_address =  &get_local_ip_for_remote_ip($host_ip).":$server_port";
1272                                 my $regserver = $res->{1}->{'regserver'};
1273                                 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$regserver'"; 
1274                                 my $res = $known_server_db->select_dbentry($sql);
1275                                 if (keys(%$res) > 0) {
1276                                         my $regserver_key = $res->{1}->{'hostkey'};
1277                                         $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1278                                         $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1279                                         if ($source eq "GOSA") {
1280                                                 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1281                                         }
1282                                         &send_msg_to_target($msg, $regserver, $regserver_key, $header, $session_id);
1283                                 }
1284                                 $done = 1;
1285                                 &daemon_log("$session_id DEBUG: target is a client address in foreign_clients -> forward to registration server", 7);
1286                         } else {
1287                                 $not_found_in_foreign_clients_db = 1;
1288                         }
1289                 }
1291                 # target is a server address -> forward to server
1292                 if (not $done) {
1293                         $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1294                         $res = $known_server_db->select_dbentry($sql);
1295                         if (keys(%$res) > 0) {
1296                                 my $hostkey = $res->{1}->{'hostkey'};
1298                                 if ($source eq "GOSA") {
1299                                         $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1300                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1302                                 }
1304                                 &send_msg_to_target($msg, $target, $hostkey, $header, $session_id);
1305                                 $done = 1;
1306                                 &daemon_log("$session_id DEBUG: target is a server address -> forward to server", 7);
1307                         } else {
1308                                 $not_found_in_known_server_db = 1;
1309                         }
1310                 }
1313                 # target is not in foreign_clients_db, known_server_db or known_clients_db, maybe it is a complete new one -> process here
1314                 if ( $not_found_in_foreign_clients_db 
1315                         && $not_found_in_known_server_db
1316                         && $not_found_in_known_clients_db) {
1317                         &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);
1318             if ($header =~ /^gosa_/ || $header =~ /^job_/) { 
1319                 $module = "GosaPackages"; 
1320             }
1321                         my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1322                                         primkey=>[],
1323                                         headertag=>$header,
1324                                         targettag=>$target,
1325                                         xmlmessage=>&encode_base64($msg),
1326                                         timestamp=>&get_time,
1327                                         module=>$module,
1328                                         sessionid=>$session_id,
1329                                 } );
1330                         $done = 1;
1331                 }
1334                 if (not $done) {
1335                         daemon_log("$session_id ERROR: do not know what to do with this message: $msg", 1);
1336                         if ($source eq "GOSA") {
1337                                 my %data = ('error_msg' => &encode_base64($msg), 'error_string' => "Do not know what to do with this message!");
1338                                 my $error_msg = &build_msg("error", $local_address, "GOSA", \%data ); 
1340                                 my $session_reference = $kernel->ID_id_to_session($session_id);
1341                                 if( defined $session_reference ) {
1342                                         $heap = $session_reference->get_heap();
1343                                 }
1344                                 if(exists $heap->{'client'}) {
1345                                         $error_msg = &encrypt_msg($error_msg, $GosaPackages_key);
1346                                         $heap->{'client'}->put($error_msg);
1347                                 }
1348                         }
1349                 }
1351         }
1353         return;
1357 sub next_task {
1358     my ($session, $heap, $task) = @_[SESSION, HEAP, ARG0];
1359     my $running_task = POE::Wheel::Run->new(
1360             Program => sub { process_task($session, $heap, $task) },
1361             StdioFilter => POE::Filter::Reference->new(),
1362             StdoutEvent  => "task_result",
1363             StderrEvent  => "task_debug",
1364             CloseEvent   => "task_done",
1365             );
1366     $heap->{task}->{ $running_task->ID } = $running_task;
1369 sub handle_task_result {
1370     my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
1371     my $client_answer = $result->{'answer'};
1372     if( $client_answer =~ s/session_id=(\d+)$// ) {
1373         my $session_id = $1;
1374         if( defined $session_id ) {
1375             my $session_reference = $kernel->ID_id_to_session($session_id);
1376             if( defined $session_reference ) {
1377                 $heap = $session_reference->get_heap();
1378             }
1379         }
1381         if(exists $heap->{'client'}) {
1382             $heap->{'client'}->put($client_answer);
1383         }
1384     }
1385     $kernel->sig(CHLD => "child_reap");
1388 sub handle_task_debug {
1389     my $result = $_[ARG0];
1390     print STDERR "$result\n";
1393 sub handle_task_done {
1394     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1395     delete $heap->{task}->{$task_id};
1398 sub process_task {
1399     no strict "refs";
1400     #CHECK: Not @_[...]?
1401     my ($session, $heap, $task) = @_;
1402     my $error = 0;
1403     my $answer_l;
1404     my ($answer_header, @answer_target_l, $answer_source);
1405     my $client_answer = "";
1407     # prepare all variables needed to process message
1408     #my $msg = $task->{'xmlmessage'};
1409     my $msg = &decode_base64($task->{'xmlmessage'});
1410     my $incoming_id = $task->{'id'};
1411     my $module = $task->{'module'};
1412     my $header =  $task->{'headertag'};
1413     my $session_id = $task->{'sessionid'};
1414                 my $msg_hash;
1415                 eval {
1416         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
1417                 }; 
1418                 daemon_log("ERROR: XML failure '$@'") if ($@);
1419     my $source = @{$msg_hash->{'source'}}[0];
1420     
1421     # set timestamp of incoming client uptodate, so client will not 
1422     # be deleted from known_clients because of expiration
1423     my $act_time = &get_time();
1424     my $sql = "UPDATE $known_clients_tn SET timestamp='$act_time' WHERE hostname='$source'"; 
1425     my $res = $known_clients_db->exec_statement($sql);
1427     ######################
1428     # process incoming msg
1429     if( $error == 0) {
1430         daemon_log("$session_id INFO: Incoming msg (session_id=$session_id) with header '".@{$msg_hash->{'header'}}[0]."'", 5); 
1431         daemon_log("$session_id DEBUG: Processing module ".$module, 7);
1432         $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1434         if ( 0 < @{$answer_l} ) {
1435             my $answer_str = join("\n", @{$answer_l});
1436             while ($answer_str =~ /<header>(\w+)<\/header>/g) {
1437                 daemon_log("$session_id INFO: got answer message with header '$1'", 5);
1438             }
1439             daemon_log("$session_id DEBUG: $module: got answer from module: \n".$answer_str,9);
1440         } else {
1441             daemon_log("$session_id DEBUG: $module: got no answer from module!" ,7);
1442         }
1444     }
1445     if( !$answer_l ) { $error++ };
1447     ########
1448     # answer
1449     if( $error == 0 ) {
1451         foreach my $answer ( @{$answer_l} ) {
1452             # check outgoing msg to xml validity
1453             my $answer_hash = &check_outgoing_xml_validity($answer, $session_id);
1454             if( not defined $answer_hash ) { next; }
1455             
1456             $answer_header = @{$answer_hash->{'header'}}[0];
1457             @answer_target_l = @{$answer_hash->{'target'}};
1458             $answer_source = @{$answer_hash->{'source'}}[0];
1460             # deliver msg to all targets 
1461             foreach my $answer_target ( @answer_target_l ) {
1463                 # targets of msg are all gosa-si-clients in known_clients_db
1464                 if( $answer_target eq "*" ) {
1465                     # answer is for all clients
1466                     my $sql_statement= "SELECT * FROM known_clients";
1467                     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
1468                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1469                         my $host_name = $hit->{hostname};
1470                         my $host_key = $hit->{hostkey};
1471                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1472                         &update_jobdb_status_for_send_msgs($answer, $error);
1473                     }
1474                 }
1476                 # targets of msg are all gosa-si-server in known_server_db
1477                 elsif( $answer_target eq "KNOWN_SERVER" ) {
1478                     # answer is for all server in known_server
1479                     my $sql_statement= "SELECT * FROM $known_server_tn";
1480                     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
1481                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1482                         my $host_name = $hit->{hostname};
1483                         my $host_key = $hit->{hostkey};
1484                         $answer =~ s/<target>\S+<\/target>/<target>$host_name<\/target>/g;
1485                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1486                         &update_jobdb_status_for_send_msgs($answer, $error);
1487                     }
1488                 }
1490                 # target of msg is GOsa
1491                                 elsif( $answer_target eq "GOSA" ) {
1492                                         my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1493                                         my $add_on = "";
1494                     if( defined $session_id ) {
1495                         $add_on = ".session_id=$session_id";
1496                     }
1497                     # answer is for GOSA and has to returned to connected client
1498                     my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1499                     $client_answer = $gosa_answer.$add_on;
1500                 }
1502                 # target of msg is job queue at this host
1503                 elsif( $answer_target eq "JOBDB") {
1504                     $answer =~ /<header>(\S+)<\/header>/;   
1505                     my $header;
1506                     if( defined $1 ) { $header = $1; }
1507                     my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1508                     &update_jobdb_status_for_send_msgs($answer, $error);
1509                 }
1511                 # Target of msg is a mac address
1512                 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 ) {
1513                     daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients and foreign_clients", 5);
1515                     # Looking for macaddress in known_clients
1516                     my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1517                     my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1518                     my $found_ip_flag = 0;
1519                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1520                         my $host_name = $hit->{hostname};
1521                         my $host_key = $hit->{hostkey};
1522                         $answer =~ s/$answer_target/$host_name/g;
1523                         daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1524                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1525                         &update_jobdb_status_for_send_msgs($answer, $error);
1526                         $found_ip_flag++ ;
1527                     }   
1529                     # Looking for macaddress in foreign_clients
1530                     if ($found_ip_flag == 0) {
1531                         my $sql = "SELECT * FROM $foreign_clients_tn WHERE macaddress LIKE '$answer_target'";
1532                         my $res = $foreign_clients_db->select_dbentry($sql);
1533                         while( my ($hit_num, $hit) = each %{ $res } ) {
1534                             my $host_name = $hit->{hostname};
1535                             my $reg_server = $hit->{regserver};
1536                             daemon_log("$session_id INFO: found host '$host_name' with mac '$answer_target', registered at '$reg_server'", 5);
1537                             
1538                             # Fetch key for reg_server
1539                             my $reg_server_key;
1540                             my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$reg_server'";
1541                             my $res = $known_server_db->select_dbentry($sql);
1542                             if (exists $res->{1}) {
1543                                 $reg_server_key = $res->{1}->{'hostkey'}; 
1544                             } else {
1545                                 daemon_log("$session_id ERROR: cannot find hostkey for '$host_name' in '$known_server_tn'", 1); 
1546                                 daemon_log("$session_id ERROR: unable to forward answer to correct registration server, processing is aborted!", 1); 
1547                                 $reg_server_key = undef;
1548                             }
1550                             # Send answer to server where client is registered
1551                             if (defined $reg_server_key) {
1552                                 $answer =~ s/$answer_target/$host_name/g;
1553                                 my $error = &send_msg_to_target($answer, $reg_server, $reg_server_key, $answer_header, $session_id);
1554                                 &update_jobdb_status_for_send_msgs($answer, $error);
1555                                 $found_ip_flag++ ;
1556                             }
1557                         }
1558                     }
1560                     # No mac to ip matching found
1561                     if( $found_ip_flag == 0) {
1562                         daemon_log("$session_id WARNING: no host found in known_clients or foreign_clients with mac address '$answer_target'", 3);
1563                         &reactivate_job_with_delay($session_id, $answer_target, $answer_header, 30);
1564                     }
1566                 # Answer is for one specific host   
1567                 } else {
1568                     # get encrypt_key
1569                     my $encrypt_key = &get_encrypt_key($answer_target);
1570                     if( not defined $encrypt_key ) {
1571                         # unknown target
1572                         daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1573                         next;
1574                     }
1575                     my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1576                     &update_jobdb_status_for_send_msgs($answer, $error);
1577                 }
1578             }
1579         }
1580     }
1582     my $filter = POE::Filter::Reference->new();
1583     my %result = ( 
1584             status => "seems ok to me",
1585             answer => $client_answer,
1586             );
1588     my $output = $filter->put( [ \%result ] );
1589     print @$output;
1594 sub session_start {
1595     my ($kernel) = $_[KERNEL];
1596     $global_kernel = $kernel;
1597     $kernel->yield('register_at_foreign_servers');
1598         $kernel->yield('create_fai_server_db', $fai_server_tn );
1599         $kernel->yield('create_fai_release_db', $fai_release_tn );
1600     $kernel->yield('watch_for_next_tasks');
1601         $kernel->sig(USR1 => "sig_handler");
1602         $kernel->sig(USR2 => "recreate_packages_db");
1603         $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1604         $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay); 
1605     $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay); 
1606         $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1607     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1608         $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1609     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1611     # Start opsi check
1612     if ($opsi_enabled eq "true") {
1613         $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay); 
1614     }
1619 sub watch_for_done_jobs {
1620     #CHECK: $heap for what?
1621     my ($kernel,$heap) = @_[KERNEL, HEAP];
1623     my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((status='done') AND (modified='0'))";
1624         my $res = $job_db->select_dbentry( $sql_statement );
1626     while( my ($id, $hit) = each %{$res} ) {
1627         my $jobdb_id = $hit->{id};
1628         my $sql_statement = "DELETE FROM $job_queue_tn WHERE id=$jobdb_id"; 
1629         my $res = $job_db->del_dbentry($sql_statement); 
1630     }
1632     $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1636 sub watch_for_opsi_jobs {
1637     my ($kernel) = $_[KERNEL];
1639     # 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 
1640     # opsi install job is to parse the xml message. There is still the correct header.
1641     my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((xmlmessage LIKE '%opsi_install_client</header>%') AND (status='processing') AND (siserver='localhost'))";
1642         my $res = $job_db->select_dbentry( $sql_statement );
1644     # Ask OPSI for an update of the running jobs
1645     while (my ($id, $hit) = each %$res ) {
1646         # Determine current parameters of the job
1647         my $hostId = $hit->{'plainname'};
1648         my $macaddress = $hit->{'macaddress'};
1649         my $progress = $hit->{'progress'};
1651         my $result= {};
1652         
1653         # For hosts, only return the products that are or get installed
1654         my $callobj;
1655         $callobj = {
1656             method  => 'getProductStates_hash',
1657             params  => [ $hostId ],
1658             id  => 1,
1659         };
1660         
1661         my $hres = $opsi_client->call($opsi_url, $callobj);
1662         #my ($hres_err, $hres_err_string) = &check_opsi_res($hres);
1663         if (not &check_opsi_res($hres)) {
1664             my $htmp= $hres->result->{$hostId};
1665         
1666             # Check state != not_installed or action == setup -> load and add
1667             my $products= 0;
1668             my $installed= 0;
1669             my $installing = 0;
1670             my $error= 0;  
1671             my @installed_list;
1672             my @error_list;
1673             my $act_status = "none";
1674             foreach my $product (@{$htmp}){
1676                 if ($product->{'installationStatus'} ne "not_installed" or
1677                         $product->{'actionRequest'} eq "setup"){
1679                     # Increase number of products for this host
1680                     $products++;
1681         
1682                     if ($product->{'installationStatus'} eq "failed"){
1683                         $result->{$product->{'productId'}}= "error";
1684                         unshift(@error_list, $product->{'productId'});
1685                         $error++;
1686                     }
1687                     if ($product->{'installationStatus'} eq "installed" && $product->{'actionRequest'}  eq "none"){
1688                         $result->{$product->{'productId'}}= "installed";
1689                         unshift(@installed_list, $product->{'productId'});
1690                         $installed++;
1691                     }
1692                     if ($product->{'installationStatus'} eq "installing"){
1693                         $result->{$product->{'productId'}}= "installing";
1694                         $installing++;
1695                         $act_status = "installing - ".$product->{'productId'};
1696                     }
1697                 }
1698             }
1699         
1700             # Estimate "rough" progress, avoid division by zero
1701             if ($products == 0) {
1702                 $result->{'progress'}= 0;
1703             } else {
1704                 $result->{'progress'}= int($installed * 100 / $products);
1705             }
1707             # Set updates in job queue
1708             if ((not $error) && (not $installing) && ($installed)) {
1709                 $act_status = "installed - ".join(", ", @installed_list);
1710             }
1711             if ($error) {
1712                 $act_status = "error - ".join(", ", @error_list);
1713             }
1714             if ($progress ne $result->{'progress'} ) {
1715                 # Updating progress and result 
1716                 my $update_statement = "UPDATE $job_queue_tn SET modified='1', progress='".$result->{'progress'}."', result='$act_status' WHERE macaddress='$macaddress' AND siserver='localhost'";
1717                 my $update_res = $job_db->update_dbentry($update_statement);
1718             }
1719             if ($progress eq 100) { 
1720                 # Updateing status
1721                 my $done_statement = "UPDATE $job_queue_tn SET modified='1', ";
1722                 if ($error) {
1723                     $done_statement .= "status='error'";
1724                 } else {
1725                     $done_statement .= "status='done'";
1726                 }
1727                 $done_statement .= " WHERE macaddress='$macaddress' AND siserver='localhost'";
1728                 my $done_res = $job_db->update_dbentry($done_statement);
1729             }
1732         }
1733     }
1735     $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay);
1739 # If a job got an update or was modified anyway, send to all other si-server an update message of this jobs.
1740 sub watch_for_modified_jobs {
1741     my ($kernel,$heap) = @_[KERNEL, HEAP];
1743     my $sql_statement = "SELECT * FROM $job_queue_tn WHERE (modified='1')"; 
1744     my $res = $job_db->select_dbentry( $sql_statement );
1745     
1746     # if db contains no jobs which should be update, do nothing
1747     if (keys %$res != 0) {
1749         if ($job_synchronization  eq "true") {
1750             # make out of the db result a gosa-si message   
1751             my $update_msg = &db_res2si_msg ($res, "foreign_job_updates", "KNOWN_SERVER", "MY_LOCAL_ADDRESS");
1752  
1753             # update all other SI-server
1754             &inform_all_other_si_server($update_msg);
1755         }
1757         # set jobs all jobs to modified = 0, wait until the next modification for updates of other si-server
1758         $sql_statement = "UPDATE $job_queue_tn SET modified='0' ";
1759         $res = $job_db->update_dbentry($sql_statement);
1760     }
1762     $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay);
1766 sub watch_for_new_jobs {
1767         if($watch_for_new_jobs_in_progress == 0) {
1768                 $watch_for_new_jobs_in_progress = 1;
1769                 my ($kernel,$heap) = @_[KERNEL, HEAP];
1771                 # check gosa job quaeue for jobs with executable timestamp
1772                 my $timestamp = &get_time();
1773                 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE status='waiting' AND (CAST(timestamp AS UNSIGNED)) < $timestamp ORDER BY timestamp";
1774                 my $res = $job_db->exec_statement( $sql_statement );
1776                 # Merge all new jobs that would do the same actions
1777                 my @drops;
1778                 my $hits;
1779                 foreach my $hit (reverse @{$res} ) {
1780                         my $macaddress= lc @{$hit}[8];
1781                         my $headertag= @{$hit}[5];
1782                         if(
1783                                 defined($hits->{$macaddress}) &&
1784                                 defined($hits->{$macaddress}->{$headertag}) &&
1785                                 defined($hits->{$macaddress}->{$headertag}[0])
1786                         ) {
1787                                 push @drops, "DELETE FROM $job_queue_tn WHERE id = $hits->{$macaddress}->{$headertag}[0]";
1788                         }
1789                         $hits->{$macaddress}->{$headertag}= $hit;
1790                 }
1792                 # Delete new jobs with a matching job in state 'processing'
1793                 foreach my $macaddress (keys %{$hits}) {
1794                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1795                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1796                                 if(defined($jobdb_id)) {
1797                                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND headertag='$jobdb_headertag' AND status='processing'";
1798                                         my $res = $job_db->exec_statement( $sql_statement );
1799                                         foreach my $hit (@{$res}) {
1800                                                 push @drops, "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1801                                         }
1802                                 } else {
1803                                         daemon_log("J ERROR: Job without id exists for macaddress $macaddress!", 1);
1804                                 }
1805                         }
1806                 }
1808                 # Commit deletion
1809                 $job_db->exec_statementlist(\@drops);
1811                 # Look for new jobs that could be executed
1812                 foreach my $macaddress (keys %{$hits}) {
1814                         # Look if there is an executing job
1815                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='processing'";
1816                         my $res = $job_db->exec_statement( $sql_statement );
1818                         # Skip new jobs for host if there is a processing job
1819                         if(defined($res) and defined @{$res}[0]) {
1820                                 # Prevent race condition if there is a trigger_activate job waiting and a goto-activation job processing
1821                                 my $row = @{$res}[0] if (ref $res eq 'ARRAY');
1822                                 if(@{$row}[5] eq 'trigger_action_reinstall') {
1823                                         my $sql_statement_2 =  "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='waiting' AND headertag = 'trigger_activate_new'"; 
1824                                         my $res_2 = $job_db->exec_statement( $sql_statement_2 );
1825                                         if(defined($res_2) and defined @{$res_2}[0]) {
1826                                                 # Set status from goto-activation to 'waiting' and update timestamp
1827                                                 $job_db->exec_statement("UPDATE $job_queue_tn SET status='waiting' WHERE macaddress LIKE '$macaddress' AND headertag = 'trigger_action_reinstall'");
1828                                                 $job_db->exec_statement("UPDATE $job_queue_tn SET timestamp='".&get_time(30)."' WHERE macaddress LIKE '$macaddress' AND headertag = 'trigger_action_reinstall'");
1829                                         }
1830                                 }
1831                                 next;
1832                         }
1834                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1835                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1836                                 if(defined($jobdb_id)) {
1837                                         my $job_msg = @{$hits->{$macaddress}->{$jobdb_headertag}}[7];
1839                                         daemon_log("J DEBUG: its time to execute $job_msg", 7);
1840                                         my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1841                                         my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1843                                         # expect macaddress is unique!!!!!!
1844                                         my $target = $res_hash->{1}->{hostname};
1846                                         # change header
1847                                         $job_msg =~ s/<header>job_/<header>gosa_/;
1849                                         # add sqlite_id
1850                                         $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1852                                         $job_msg =~ /<header>(\S+)<\/header>/;
1853                                         my $header = $1 ;
1854                                         my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");                    
1856                                         # update status in job queue to ...
1857                     # ... 'processing', for jobs: 'reinstall', 'update'
1858                     if (($header =~ /gosa_trigger_action_reinstall/) 
1859                             || ($header =~ /gosa_trigger_activate_new/)
1860                             || ($header =~ /gosa_trigger_action_update/)) {
1861                         my $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
1862                         my $dbres = $job_db->update_dbentry($sql_statement);
1863                     }
1865                     # ... 'done', for all other jobs, they are no longer needed in the jobqueue
1866                     else {
1867                         my $sql_statement = "UPDATE $job_queue_tn SET status='done' WHERE id=$jobdb_id";
1868                         my $dbres = $job_db->update_dbentry($sql_statement);
1869                     }
1870                 
1872                                         # We don't want parallel processing
1873                                         last;
1874                                 }
1875                         }
1876                 }
1878                 $watch_for_new_jobs_in_progress = 0;
1879                 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1880         }
1884 sub watch_for_new_messages {
1885     my ($kernel,$heap) = @_[KERNEL, HEAP];
1886     my @coll_user_msg;   # collection list of outgoing messages
1887     
1888     # check messaging_db for new incoming messages with executable timestamp
1889     my $timestamp = &get_time();
1890     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS UNSIGNED))<$timestamp AND flag='n' AND direction='in' )";
1891     my $res = $messaging_db->exec_statement( $sql_statement );
1892         foreach my $hit (@{$res}) {
1894         # create outgoing messages
1895         my $message_to = @{$hit}[3];
1896         # translate message_to to plain login name
1897         my @message_to_l = split(/,/, $message_to);  
1898                 my %receiver_h; 
1899                 foreach my $receiver (@message_to_l) {
1900                         if ($receiver =~ /^u_([\s\S]*)$/) {
1901                                 $receiver_h{$receiver} = 0;
1902                         } elsif ($receiver =~ /^g_([\s\S]*)$/) {
1903                                 my $group_name = $1;
1904                                 # fetch all group members from ldap and add them to receiver hash
1905                                 my $ldap_handle = &get_ldap_handle();
1906                                 if (defined $ldap_handle) {
1907                                                 my $mesg = $ldap_handle->search(
1908                                                                                 base => $ldap_base,
1909                                                                                 scope => 'sub',
1910                                                                                 attrs => ['memberUid'],
1911                                                                                 filter => "cn=$group_name",
1912                                                                                 );
1913                                                 if ($mesg->count) {
1914                                                                 my @entries = $mesg->entries;
1915                                                                 foreach my $entry (@entries) {
1916                                                                                 my @receivers= $entry->get_value("memberUid");
1917                                                                                 foreach my $receiver (@receivers) { 
1918                                                                                                 $receiver_h{$receiver} = 0;
1919                                                                                 }
1920                                                                 }
1921                                                 } 
1922                                                 # translating errors ?
1923                                                 if ($mesg->code) {
1924                                                                 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: $mesg->error", 1);
1925                                                 }
1926                                 # ldap handle error ?           
1927                                 } else {
1928                                         daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: no ldap handle available", 1);
1929                                 }
1930                         } else {
1931                                 my $sbjct = &encode_base64(@{$hit}[1]);
1932                                 my $msg = &encode_base64(@{$hit}[7]);
1933                                 &daemon_log("M WARNING: unknown receiver '$receiver' for a user-message '$sbjct - $msg'", 3); 
1934                         }
1935                 }
1936                 my @receiver_l = keys(%receiver_h);
1938         my $message_id = @{$hit}[0];
1940         #add each outgoing msg to messaging_db
1941         my $receiver;
1942         foreach $receiver (@receiver_l) {
1943             my $sql_statement = "INSERT INTO $messaging_tn (id, subject, message_from, message_to, flag, direction, delivery_time, message, timestamp) ".
1944                 "VALUES ('".
1945                 $message_id."', '".    # id
1946                 @{$hit}[1]."', '".     # subject
1947                 @{$hit}[2]."', '".     # message_from
1948                 $receiver."', '".      # message_to
1949                 "none"."', '".         # flag
1950                 "out"."', '".          # direction
1951                 @{$hit}[6]."', '".     # delivery_time
1952                 @{$hit}[7]."', '".     # message
1953                 $timestamp."'".     # timestamp
1954                 ")";
1955             &daemon_log("M DEBUG: $sql_statement", 1);
1956             my $res = $messaging_db->exec_statement($sql_statement);
1957             &daemon_log("M INFO: message '".@{$hit}[0]."' is prepared for delivery to receiver '$receiver'", 5);
1958         }
1960         # set incoming message to flag d=deliverd
1961         $sql_statement = "UPDATE $messaging_tn SET flag='p' WHERE id='$message_id'"; 
1962         &daemon_log("M DEBUG: $sql_statement", 7);
1963         $res = $messaging_db->update_dbentry($sql_statement);
1964         &daemon_log("M INFO: message '$message_id' is set to flag 'p' (processed)", 5);
1965     }
1967     $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay); 
1968     return;
1971 sub watch_for_delivery_messages {
1972     my ($kernel, $heap) = @_[KERNEL, HEAP];
1974     # select outgoing messages
1975     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( flag='p' AND direction='out' )";
1976     #&daemon_log("0 DEBUG: $sql", 7);
1977     my $res = $messaging_db->exec_statement( $sql_statement );
1978     
1979     # build out msg for each    usr
1980     foreach my $hit (@{$res}) {
1981         my $receiver = @{$hit}[3];
1982         my $msg_id = @{$hit}[0];
1983         my $subject = @{$hit}[1];
1984         my $message = @{$hit}[7];
1986         # resolve usr -> host where usr is logged in
1987         my $sql = "SELECT * FROM $login_users_tn WHERE (user='$receiver')"; 
1988         #&daemon_log("0 DEBUG: $sql", 7);
1989         my $res = $login_users_db->exec_statement($sql);
1991         # receiver is logged in nowhere
1992         if (not ref(@$res[0]) eq "ARRAY") { next; }    
1994         # receiver ist logged in at a client registered at local server
1995                 my $send_succeed = 0;
1996                 foreach my $hit (@$res) {
1997                                 my $receiver_host = @$hit[0];
1998                 my $delivered2host = 0;
1999                                 &daemon_log("M DEBUG: user '$receiver' is logged in at host '$receiver_host'", 7);
2001                                 # Looking for host in know_clients_db 
2002                                 my $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$receiver_host')";
2003                                 my $res = $known_clients_db->exec_statement($sql);
2005                 # Host is known in known_clients_db
2006                 if (ref(@$res[0]) eq "ARRAY") {
2007                     my $receiver_key = @{@{$res}[0]}[2];
2008                     my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
2009                     my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
2010                     my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0); 
2011                     if ($error == 0 ) {
2012                         $send_succeed++ ;
2013                         $delivered2host++ ;
2014                         &daemon_log("M DEBUG: send message for user '$receiver' to host '$receiver_host'", 7); 
2015                     } else {
2016                         &daemon_log("M DEBUG: cannot send message for user '$receiver' to host '$receiver_host'", 7); 
2017                     }
2018                 }
2019                 
2020                 # Message already send, do not need to do anything more, otherwise ...
2021                 if ($delivered2host) { next;}
2022     
2023                 # ...looking for host in foreign_clients_db
2024                 $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$receiver_host')";
2025                 $res = $foreign_clients_db->exec_statement($sql);
2026   
2027                                 # Host is known in foreign_clients_db 
2028                                 if (ref(@$res[0]) eq "ARRAY") { 
2029                     my $registration_server = @{@{$res}[0]}[2];
2030                     
2031                     # Fetch encryption key for registration server
2032                     my $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$registration_server')";
2033                     my $res = $known_server_db->exec_statement($sql);
2034                     if (ref(@$res[0]) eq "ARRAY") { 
2035                         my $registration_server_key = @{@{$res}[0]}[3];
2036                         my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
2037                         my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
2038                         my $error = &send_msg_to_target($out_msg, $registration_server, $registration_server_key, "usr_msg", 0); 
2039                         if ($error == 0 ) {
2040                             $send_succeed++ ;
2041                             $delivered2host++ ;
2042                             &daemon_log("M DEBUG: send message for user '$receiver' to server '$registration_server'", 7); 
2043                         } else {
2044                             &daemon_log("M ERROR: cannot send message for user '$receiver' to server '$registration_server'", 1); 
2045                         }
2047                     } else {
2048                         &daemon_log("M ERROR: host '$receiver_host' is reported to be ".
2049                                 "registrated at server '$registration_server', ".
2050                                 "but no data available in known_server_db ", 1); 
2051                     }
2052                 }
2053                 
2054                 if (not $delivered2host) {
2055                     &daemon_log("M ERROR: unable to send user message to host '$receiver_host'", 1);
2056                 }
2057                 }
2059                 if ($send_succeed) {
2060                                 # set outgoing msg at db to deliverd
2061                                 my $sql = "UPDATE $messaging_tn SET flag='d' WHERE (id='$msg_id' AND direction='out' AND message_to='$receiver')"; 
2062                                 my $res = $messaging_db->exec_statement($sql); 
2063                 &daemon_log("M INFO: send message for user '$receiver' to logged in hosts", 5);
2064                 } else {
2065             &daemon_log("M WARNING: failed to deliver message for user '$receiver'", 3); 
2066         }
2067         }
2069     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay); 
2070     return;
2074 sub watch_for_done_messages {
2075     my ($kernel,$heap) = @_[KERNEL, HEAP];
2077     my $sql = "SELECT * FROM $messaging_tn WHERE (flag='p' AND direction='in')"; 
2078     #&daemon_log("0 DEBUG: $sql", 7);
2079     my $res = $messaging_db->exec_statement($sql); 
2081     foreach my $hit (@{$res}) {
2082         my $msg_id = @{$hit}[0];
2084         my $sql = "SELECT * FROM $messaging_tn WHERE (id='$msg_id' AND direction='out' AND (NOT flag='s'))"; 
2085         #&daemon_log("0 DEBUG: $sql", 7); 
2086         my $res = $messaging_db->exec_statement($sql);
2088         # not all usr msgs have been seen till now
2089         if ( ref(@$res[0]) eq "ARRAY") { next; }
2090         
2091         $sql = "DELETE FROM $messaging_tn WHERE (id='$msg_id')"; 
2092         #&daemon_log("0 DEBUG: $sql", 7);
2093         $res = $messaging_db->exec_statement($sql);
2094     
2095     }
2097     $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay); 
2098     return;
2102 sub watch_for_old_known_clients {
2103     my ($kernel,$heap) = @_[KERNEL, HEAP];
2105     my $sql_statement = "SELECT * FROM $known_clients_tn";
2106     my $res = $known_clients_db->select_dbentry( $sql_statement );
2108     my $act_time = int(&get_time());
2110     while ( my ($hit_num, $hit) = each %$res) {
2111         my $expired_timestamp = int($hit->{'timestamp'});
2112         $expired_timestamp =~ /(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/;
2113         my $dt = DateTime->new( year   => $1,
2114                 month  => $2,
2115                 day    => $3,
2116                 hour   => $4,
2117                 minute => $5,
2118                 second => $6,
2119                 );
2121         $dt->add( seconds => 2 * int($hit->{'keylifetime'}) );
2122         $expired_timestamp = $dt->ymd('').$dt->hms('');
2123         if ($act_time > $expired_timestamp) {
2124             my $hostname = $hit->{'hostname'};
2125             my $del_sql = "DELETE FROM $known_clients_tn WHERE hostname='$hostname'"; 
2126             my $del_res = $known_clients_db->exec_statement($del_sql);
2128             &main::daemon_log("0 INFO: timestamp '".$hit->{'timestamp'}."' of client '$hostname' is expired('$expired_timestamp'), client will be deleted from known_clients_db", 5);
2129         }
2131     }
2133     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
2137 sub watch_for_next_tasks {
2138     my ($kernel,$heap) = @_[KERNEL, HEAP];
2140     my $sql = "SELECT * FROM $incoming_tn";
2141     my $res = $incoming_db->select_dbentry($sql);
2142     
2143     while ( my ($hit_num, $hit) = each %$res) {
2144         my $headertag = $hit->{'headertag'};
2145         if ($headertag =~ /^answer_(\d+)/) {
2146             # do not start processing, this message is for a still running POE::Wheel
2147             next;
2148         }
2149         my $message_id = $hit->{'id'};
2150         my $session_id = $hit->{'sessionid'};
2151         &daemon_log("$session_id DEBUG: start processing for message with incoming id: '$message_id'", 7);
2152         $kernel->yield('next_task', $hit);
2154         my $sql = "DELETE FROM $incoming_tn WHERE id=$message_id";
2155         my $res = $incoming_db->exec_statement($sql);
2156     }
2158     $kernel->delay_set('watch_for_next_tasks', 1); 
2162 sub get_ldap_handle {
2163         my ($session_id) = @_;
2164         my $heap;
2165         my $ldap_handle;
2167         if (not defined $session_id ) { $session_id = 0 };
2168         if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
2170         if ($session_id == 0) {
2171                 daemon_log("$session_id DEBUG: get_ldap_handle invoked without a session_id, create a new ldap_handle", 7); 
2172                 $ldap_handle = Net::LDAP->new( $ldap_uri );
2173                 if (defined $ldap_handle) {
2174                         $ldap_handle->bind($ldap_admin_dn, password => $ldap_admin_password) or daemon_log("$session_id ERROR: Bind to LDAP $ldap_uri as $ldap_admin_dn failed!"); 
2175                 } else {
2176                         daemon_log("$session_id ERROR: creation of a new LDAP handle failed (ldap_uri '$ldap_uri')");
2177                 }
2179         } else {
2180                 my $session_reference = $global_kernel->ID_id_to_session($session_id);
2181                 if( defined $session_reference ) {
2182                         $heap = $session_reference->get_heap();
2183                 }
2185                 if (not defined $heap) {
2186                         daemon_log("$session_id DEBUG: cannot get heap for session_id '$session_id'", 7); 
2187                         return;
2188                 }
2190                 # TODO: This "if" is nonsense, because it doesn't prove that the
2191                 #       used handle is still valid - or if we've to reconnect...
2192                 #if (not exists $heap->{ldap_handle}) {
2193                         $ldap_handle = Net::LDAP->new( $ldap_uri );
2194                         $ldap_handle->bind($ldap_admin_dn, password => $ldap_admin_password) or daemon_log("$session_id ERROR: Bind to LDAP $ldap_uri as $ldap_admin_dn failed!"); 
2195                         $heap->{ldap_handle} = $ldap_handle;
2196                 #}
2197         }
2198         return $ldap_handle;
2202 sub change_fai_state {
2203     my ($st, $targets, $session_id) = @_;
2204     $session_id = 0 if not defined $session_id;
2205     # Set FAI state to localboot
2206     my %mapActions= (
2207         reboot    => '',
2208         update    => 'softupdate',
2209         localboot => 'localboot',
2210         reinstall => 'install',
2211         rescan    => '',
2212         wake      => '',
2213         memcheck  => 'memcheck',
2214         sysinfo   => 'sysinfo',
2215         install   => 'install',
2216     );
2218     # Return if this is unknown
2219     if (!exists $mapActions{ $st }){
2220         daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1); 
2221       return;
2222     }
2224     my $state= $mapActions{ $st };
2226     my $ldap_handle = &get_ldap_handle($session_id);
2227     if( defined($ldap_handle) ) {
2229       # Build search filter for hosts
2230         my $search= "(&(objectClass=GOhard)";
2231         foreach (@{$targets}){
2232             $search.= "(macAddress=$_)";
2233         }
2234         $search.= ")";
2236       # If there's any host inside of the search string, procress them
2237         if (!($search =~ /macAddress/)){
2238             daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);    
2239             return;
2240         }
2242       # Perform search for Unit Tag
2243       my $mesg = $ldap_handle->search(
2244           base   => $ldap_base,
2245           scope  => 'sub',
2246           attrs  => ['dn', 'FAIstate', 'objectClass'],
2247           filter => "$search"
2248           );
2250           if ($mesg->count) {
2251                   my @entries = $mesg->entries;
2252                   if (0 == @entries) {
2253                                   daemon_log("$session_id ERROR: ldap search failed: ldap_base=$ldap_base, filter=$search", 1); 
2254                   }
2256                   foreach my $entry (@entries) {
2257                           # Only modify entry if it is not set to '$state'
2258                           if ($entry->get_value("FAIstate") ne "$state"){
2259                                   daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
2260                                   my $result;
2261                                   my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
2262                                   if (exists $tmp{'FAIobject'}){
2263                                           if ($state eq ''){
2264                                                   $result= $ldap_handle->modify($entry->dn, changes => [ delete => [ FAIstate => [] ] ]);
2265                                           } else {
2266                                                   $result= $ldap_handle->modify($entry->dn, changes => [ replace => [ FAIstate => $state ] ]);
2267                                           }
2268                                   } elsif ($state ne ''){
2269                                           $result= $ldap_handle->modify($entry->dn, changes => [ add => [ objectClass => 'FAIobject' ], add => [ FAIstate => $state ] ]);
2270                                   }
2272                                   # Errors?
2273                                   if ($result->code){
2274                                           daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2275                                   }
2276                           } else {
2277                                   daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 7); 
2278                           }  
2279                   }
2280           } else {
2281                 daemon_log("$session_id ERROR: LDAP search failed: ldap_base=$ldap_base, filter=$search", 1);
2282           }
2284     # if no ldap handle defined
2285     } else {
2286         daemon_log("$session_id ERROR: no LDAP handle defined for update FAIstate", 1); 
2287     }
2289         return;
2293 sub change_goto_state {
2294     my ($st, $targets, $session_id) = @_;
2295     $session_id = 0  if not defined $session_id;
2297     # Switch on or off?
2298     my $state= $st eq 'active' ? 'active': 'locked';
2300     my $ldap_handle = &get_ldap_handle($session_id);
2301     if( defined($ldap_handle) ) {
2303       # Build search filter for hosts
2304       my $search= "(&(objectClass=GOhard)";
2305       foreach (@{$targets}){
2306         $search.= "(macAddress=$_)";
2307       }
2308       $search.= ")";
2310       # If there's any host inside of the search string, procress them
2311       if (!($search =~ /macAddress/)){
2312         return;
2313       }
2315       # Perform search for Unit Tag
2316       my $mesg = $ldap_handle->search(
2317           base   => $ldap_base,
2318           scope  => 'sub',
2319           attrs  => ['dn', 'gotoMode'],
2320           filter => "$search"
2321           );
2323       if ($mesg->count) {
2324         my @entries = $mesg->entries;
2325         foreach my $entry (@entries) {
2327           # Only modify entry if it is not set to '$state'
2328           if ($entry->get_value("gotoMode") ne $state){
2330             daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
2331             my $result;
2332             $result= $ldap_handle->modify($entry->dn, changes => [replace => [ gotoMode => $state ] ]);
2334             # Errors?
2335             if ($result->code){
2336               &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2337             }
2339           }
2340         }
2341       } else {
2342                 daemon_log("$session_id ERROR: LDAP search failed in function change_goto_state: ldap_base=$ldap_base, filter=$search", 1);
2343           }
2345     }
2349 sub run_recreate_packages_db {
2350     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2351     my $session_id = $session->ID;
2352         &main::daemon_log("$session_id INFO: Recreating FAI Packages DB ('$fai_release_tn', '$fai_server_tn', '$packages_list_tn')", 5);
2353         $kernel->yield('create_fai_release_db', $fai_release_tn);
2354         $kernel->yield('create_fai_server_db', $fai_server_tn);
2355         return;
2359 sub run_create_fai_server_db {
2360     my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2361     my $session_id = $session->ID;
2362     my $task = POE::Wheel::Run->new(
2363             Program => sub { &create_fai_server_db($table_name,$kernel, undef, $session_id) },
2364             StdoutEvent  => "session_run_result",
2365             StderrEvent  => "session_run_debug",
2366             CloseEvent   => "session_run_done",
2367             );
2369     $heap->{task}->{ $task->ID } = $task;
2370     return;
2374 sub create_fai_server_db {
2375         my ($table_name, $kernel, $dont_create_packages_list, $session_id) = @_;
2376         my $result;
2378         if (not defined $session_id) { $session_id = 0; }
2379         my $ldap_handle = &get_ldap_handle();
2380         if(defined($ldap_handle)) {
2381                 daemon_log("$session_id INFO: create_fai_server_db: start", 5);
2382                 my $mesg= $ldap_handle->search(
2383                         base   => $ldap_base,
2384                         scope  => 'sub',
2385                         attrs  => ['FAIrepository', 'gosaUnitTag'],
2386                         filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
2387                 );
2388                 if($mesg->{'resultCode'} == 0 &&
2389                         $mesg->count != 0) {
2390                         foreach my $entry (@{$mesg->{entries}}) {
2391                                 if($entry->exists('FAIrepository')) {
2392                                         # Add an entry for each Repository configured for server
2393                                         foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
2394                                                 my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
2395                                                 my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
2396                                                 $result= $fai_server_db->add_dbentry( { 
2397                                                                 table => $table_name,
2398                                                                 primkey => ['server', 'fai_release', 'tag'],
2399                                                                 server => $tmp_url,
2400                                                                 fai_release => $tmp_release,
2401                                                                 sections => $tmp_sections,
2402                                                                 tag => (length($tmp_tag)>0)?$tmp_tag:"",
2403                                                         } );
2404                                         }
2405                                 }
2406                         }
2407                 }
2408                 daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
2410                 # TODO: Find a way to post the 'create_packages_list_db' event
2411                 if(not defined($dont_create_packages_list)) {
2412                         &create_packages_list_db(undef, undef, $session_id);
2413                 }
2414         }       
2416         $ldap_handle->disconnect;
2417         return $result;
2421 sub run_create_fai_release_db {
2422         my ($session, $heap, $table_name) = @_[SESSION, HEAP, ARG0];
2423         my $session_id = $session->ID;
2424         my $task = POE::Wheel::Run->new(
2425                 Program => sub { &create_fai_release_db($table_name, $session_id) },
2426                 StdoutEvent  => "session_run_result",
2427                 StderrEvent  => "session_run_debug",
2428                 CloseEvent   => "session_run_done",
2429         );
2431         $heap->{task}->{ $task->ID } = $task;
2432         return;
2436 sub create_fai_release_db {
2437         my ($table_name, $session_id) = @_;
2438         my $result;
2440         # used for logging
2441         if (not defined $session_id) { $session_id = 0; }
2443         my $ldap_handle = &get_ldap_handle();
2444         if(defined($ldap_handle)) {
2445                 daemon_log("$session_id INFO: create_fai_release_db: start",5);
2446                 my $mesg= $ldap_handle->search(
2447                         base   => $ldap_base,
2448                         scope  => 'sub',
2449                         attrs  => [],
2450                         filter => "(&(objectClass=organizationalUnit)(ou=fai))",
2451                 );
2452                 if($mesg->{'resultCode'} == 0 &&
2453                         $mesg->count != 0) {
2454                         # Walk through all possible FAI container ou's
2455                         my @sql_list;
2456                         my $timestamp= &get_time();
2457                         foreach my $ou (@{$mesg->{entries}}) {
2458                                 my $tmp_classes= resolve_fai_classes($ou->dn, $ldap_handle, $session_id);
2459                                 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
2460                                         my @tmp_array=get_fai_release_entries($tmp_classes);
2461                                         if(@tmp_array) {
2462                                                 foreach my $entry (@tmp_array) {
2463                                                         if(defined($entry) && ref($entry) eq 'HASH') {
2464                                                                 my $sql= 
2465                                                                 "INSERT INTO $table_name "
2466                                                                 ."(timestamp, fai_release, class, type, state) VALUES ("
2467                                                                 .$timestamp.","
2468                                                                 ."'".$entry->{'release'}."',"
2469                                                                 ."'".$entry->{'class'}."',"
2470                                                                 ."'".$entry->{'type'}."',"
2471                                                                 ."'".$entry->{'state'}."')";
2472                                                                 push @sql_list, $sql;
2473                                                         }
2474                                                 }
2475                                         }
2476                                 }
2477                         }
2479                         daemon_log("$session_id DEBUG: Inserting ".scalar @sql_list." entries to DB",8);
2480                         if(@sql_list) {
2481                                 unshift @sql_list, "DELETE FROM $table_name";
2482                                 $fai_release_db->exec_statementlist(\@sql_list);
2483                         }
2484                         daemon_log("$session_id DEBUG: Done with inserting",7);
2485                 }
2486                 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
2487         }
2488         $ldap_handle->disconnect;
2489         return $result;
2492 sub get_fai_types {
2493         my $tmp_classes = shift || return undef;
2494         my @result;
2496         foreach my $type(keys %{$tmp_classes}) {
2497                 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
2498                         my $entry = {
2499                                 type => $type,
2500                                 state => $tmp_classes->{$type}[0],
2501                         };
2502                         push @result, $entry;
2503                 }
2504         }
2506         return @result;
2509 sub get_fai_state {
2510         my $result = "";
2511         my $tmp_classes = shift || return $result;
2513         foreach my $type(keys %{$tmp_classes}) {
2514                 if(defined($tmp_classes->{$type}[0])) {
2515                         $result = $tmp_classes->{$type}[0];
2516                         
2517                 # State is equal for all types in class
2518                         last;
2519                 }
2520         }
2522         return $result;
2525 sub resolve_fai_classes {
2526         my ($fai_base, $ldap_handle, $session_id) = @_;
2527         if (not defined $session_id) { $session_id = 0; }
2528         my $result;
2529         my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
2530         my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
2531         my $fai_classes;
2533         daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base",7);
2534         my $mesg= $ldap_handle->search(
2535                 base   => $fai_base,
2536                 scope  => 'sub',
2537                 attrs  => ['cn','objectClass','FAIstate'],
2538                 filter => $fai_filter,
2539         );
2540         daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries",7);
2542         if($mesg->{'resultCode'} == 0 &&
2543                 $mesg->count != 0) {
2544                 foreach my $entry (@{$mesg->{entries}}) {
2545                         if($entry->exists('cn')) {
2546                                 my $tmp_dn= $entry->dn();
2548                                 # Skip classname and ou dn parts for class
2549                                 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?),$fai_base$/;
2551                                 # Skip classes without releases
2552                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2553                                         next;
2554                                 }
2556                                 my $tmp_cn= $entry->get_value('cn');
2557                                 my $tmp_state= $entry->get_value('FAIstate');
2559                                 my $tmp_type;
2560                                 # Get FAI type
2561                                 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
2562                                         if(grep $_ eq $oclass, @possible_fai_classes) {
2563                                                 $tmp_type= $oclass;
2564                                                 last;
2565                                         }
2566                                 }
2568                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2569                                         # A Subrelease
2570                                         my @sub_releases = split(/,/, $tmp_release);
2572                                         # Walk through subreleases and build hash tree
2573                                         my $hash;
2574                                         while(my $tmp_sub_release = pop @sub_releases) {
2575                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2576                                         }
2577                                         eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
2578                                 } else {
2579                                         # A branch, no subrelease
2580                                         push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
2581                                 }
2582                         } elsif (!$entry->exists('cn')) {
2583                                 my $tmp_dn= $entry->dn();
2584                                 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?),$fai_base$/;
2586                                 # Skip classes without releases
2587                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2588                                         next;
2589                                 }
2591                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2592                                         # A Subrelease
2593                                         my @sub_releases= split(/,/, $tmp_release);
2595                                         # Walk through subreleases and build hash tree
2596                                         my $hash;
2597                                         while(my $tmp_sub_release = pop @sub_releases) {
2598                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2599                                         }
2600                                         # Remove the last two characters
2601                                         chop($hash);
2602                                         chop($hash);
2604                                         eval('$fai_classes->'.$hash.'= {}');
2605                                 } else {
2606                                         # A branch, no subrelease
2607                                         if(!exists($fai_classes->{$tmp_release})) {
2608                                                 $fai_classes->{$tmp_release} = {};
2609                                         }
2610                                 }
2611                         }
2612                 }
2614                 # The hash is complete, now we can honor the copy-on-write based missing entries
2615                 foreach my $release (keys %$fai_classes) {
2616                         $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
2617                 }
2618         }
2619         return $result;
2622 sub apply_fai_inheritance {
2623        my $fai_classes = shift || return {};
2624        my $tmp_classes;
2626        # Get the classes from the branch
2627        foreach my $class (keys %{$fai_classes}) {
2628                # Skip subreleases
2629                if($class =~ /^ou=.*$/) {
2630                        next;
2631                } else {
2632                        $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
2633                }
2634        }
2636        # Apply to each subrelease
2637        foreach my $subrelease (keys %{$fai_classes}) {
2638                if($subrelease =~ /ou=/) {
2639                        foreach my $tmp_class (keys %{$tmp_classes}) {
2640                                if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
2641                                        $fai_classes->{$subrelease}->{$tmp_class} =
2642                                        deep_copy($tmp_classes->{$tmp_class});
2643                                } else {
2644                                        foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
2645                                                if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
2646                                                        $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
2647                                                        deep_copy($tmp_classes->{$tmp_class}->{$type});
2648                                                }
2649                                        }
2650                                }
2651                        }
2652                }
2653        }
2655        # Find subreleases in deeper levels
2656        foreach my $subrelease (keys %{$fai_classes}) {
2657                if($subrelease =~ /ou=/) {
2658                        foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
2659                                if($subsubrelease =~ /ou=/) {
2660                                        apply_fai_inheritance($fai_classes->{$subrelease});
2661                                }
2662                        }
2663                }
2664        }
2666        return $fai_classes;
2669 sub get_fai_release_entries {
2670         my $tmp_classes = shift || return;
2671         my $parent = shift || "";
2672         my @result = shift || ();
2674         foreach my $entry (keys %{$tmp_classes}) {
2675                 if(defined($entry)) {
2676                         if($entry =~ /^ou=.*$/) {
2677                                 my $release_name = $entry;
2678                                 $release_name =~ s/ou=//g;
2679                                 if(length($parent)>0) {
2680                                         $release_name = $parent."/".$release_name;
2681                                 }
2682                                 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2683                                 foreach my $bufentry(@bufentries) {
2684                                         push @result, $bufentry;
2685                                 }
2686                         } else {
2687                                 my @types = get_fai_types($tmp_classes->{$entry});
2688                                 foreach my $type (@types) {
2689                                         push @result, 
2690                                         {
2691                                                 'class' => $entry,
2692                                                 'type' => $type->{'type'},
2693                                                 'release' => $parent,
2694                                                 'state' => $type->{'state'},
2695                                         };
2696                                 }
2697                         }
2698                 }
2699         }
2701         return @result;
2704 sub deep_copy {
2705         my $this = shift;
2706         if (not ref $this) {
2707                 $this;
2708         } elsif (ref $this eq "ARRAY") {
2709                 [map deep_copy($_), @$this];
2710         } elsif (ref $this eq "HASH") {
2711                 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2712         } else { die "what type is $_?" }
2716 sub session_run_result {
2717     my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];    
2718     $kernel->sig(CHLD => "child_reap");
2721 sub session_run_debug {
2722     my $result = $_[ARG0];
2723     print STDERR "$result\n";
2726 sub session_run_done {
2727     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2728     delete $heap->{task}->{$task_id};
2732 sub create_sources_list {
2733         my $session_id = shift;
2734         my $ldap_handle = &main::get_ldap_handle;
2735         my $result="/tmp/gosa_si_tmp_sources_list";
2737         # Remove old file
2738         if(stat($result)) {
2739                 unlink($result);
2740                 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7); 
2741         }
2743         my $fh;
2744         open($fh, ">$result");
2745         if (not defined $fh) {
2746                 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7); 
2747                 return undef;
2748         }
2749         if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2750                 my $mesg=$ldap_handle->search(
2751                         base    => $main::ldap_server_dn,
2752                         scope   => 'base',
2753                         attrs   => 'FAIrepository',
2754                         filter  => 'objectClass=FAIrepositoryServer'
2755                 );
2756                 if($mesg->count) {
2757                         foreach my $entry(@{$mesg->{'entries'}}) {
2758                                 foreach my $value(@{$entry->get_value('FAIrepository', asref => 1)}) {
2759                                         my ($server, $tag, $release, $sections)= split /\|/, $value;
2760                                         my $line = "deb $server $release";
2761                                         $sections =~ s/,/ /g;
2762                                         $line.= " $sections";
2763                                         print $fh $line."\n";
2764                                 }
2765                         }
2766                 }
2767         } else {
2768                 if (defined $main::ldap_server_dn){
2769                         &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1); 
2770                 } else {
2771                         &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2772                 }
2773         }
2774         close($fh);
2776         return $result;
2780 sub run_create_packages_list_db {
2781     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2782         my $session_id = $session->ID;
2784         my $task = POE::Wheel::Run->new(
2785                                         Priority => +20,
2786                                         Program => sub {&create_packages_list_db(undef, undef, $session_id)},
2787                                         StdoutEvent  => "session_run_result",
2788                                         StderrEvent  => "session_run_debug",
2789                                         CloseEvent   => "session_run_done",
2790                                         );
2791         $heap->{task}->{ $task->ID } = $task;
2795 sub create_packages_list_db {
2796         my ($ldap_handle, $sources_file, $session_id) = @_;
2797         
2798         # it should not be possible to trigger a recreation of packages_list_db
2799         # while packages_list_db is under construction, so set flag packages_list_under_construction
2800         # which is tested befor recreation can be started
2801         if (-r $packages_list_under_construction) {
2802                 daemon_log("$session_id WARNING: packages_list_db is right now under construction, please wait until this process is finished", 3);
2803                 return;
2804         } else {
2805                 daemon_log("$session_id INFO: create_packages_list_db: start", 5); 
2806                 # set packages_list_under_construction to true
2807                 system("touch $packages_list_under_construction");
2808                 @packages_list_statements=();
2809         }
2811         if (not defined $session_id) { $session_id = 0; }
2812         if (not defined $ldap_handle) { 
2813                 $ldap_handle= &get_ldap_handle();
2815                 if (not defined $ldap_handle) {
2816                         daemon_log("$session_id ERROR: no ldap_handle available to create_packages_list_db", 1);
2817                         unlink($packages_list_under_construction);
2818                         return;
2819                 }
2820         }
2821         if (not defined $sources_file) { 
2822                 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5); 
2823                 $sources_file = &create_sources_list($session_id);
2824         }
2826         if (not defined $sources_file) {
2827                 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1); 
2828                 unlink($packages_list_under_construction);
2829                 return;
2830         }
2832         my $line;
2834         open(CONFIG, "<$sources_file") or do {
2835                 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2836                 unlink($packages_list_under_construction);
2837                 return;
2838         };
2840         # Read lines
2841         while ($line = <CONFIG>){
2842                 # Unify
2843                 chop($line);
2844                 $line =~ s/^\s+//;
2845                 $line =~ s/^\s+/ /;
2847                 # Strip comments
2848                 $line =~ s/#.*$//g;
2850                 # Skip empty lines
2851                 if ($line =~ /^\s*$/){
2852                         next;
2853                 }
2855                 # Interpret deb line
2856                 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2857                         my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2858                         my $section;
2859                         foreach $section (split(' ', $sections)){
2860                                 &parse_package_info( $baseurl, $dist, $section, $session_id );
2861                         }
2862                 }
2863         }
2865         close (CONFIG);
2867         if(keys(%repo_dirs)) {
2868                 find(\&cleanup_and_extract, keys( %repo_dirs ));
2869                 &main::strip_packages_list_statements();
2870                 $packages_list_db->exec_statementlist(\@packages_list_statements);
2871         }
2872         unlink($packages_list_under_construction);
2873         daemon_log("$session_id INFO: create_packages_list_db: finished", 5); 
2874         return;
2877 # This function should do some intensive task to minimize the db-traffic
2878 sub strip_packages_list_statements {
2879         my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2880         my @new_statement_list=();
2881         my $hash;
2882         my $insert_hash;
2883         my $update_hash;
2884         my $delete_hash;
2885         my $known_packages_hash;
2886         my $local_timestamp=get_time();
2888         foreach my $existing_entry (@existing_entries) {
2889                 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
2890         }
2892         foreach my $statement (@packages_list_statements) {
2893                 if($statement =~ /^INSERT/i) {
2894                         # Assign the values from the insert statement
2895                         my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~ 
2896                         /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
2897                         if(exists($hash->{$distribution}->{$package}->{$version})) {
2898                                 # If section or description has changed, update the DB
2899                                 if( 
2900                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or 
2901                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
2902                                 ) {
2903                                         @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
2904                                 } else {
2905                                         # package is already present in database. cache this knowledge for later use
2906                                         @{$known_packages_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2907                                 }
2908                         } else {
2909                                 # Insert a non-existing entry to db
2910                                 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2911                         }
2912                 } elsif ($statement =~ /^UPDATE/i) {
2913                         my ($template,$package,$version) = ($1,$2,$3) if $statement =~
2914                         /^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;
2915                         foreach my $distribution (keys %{$hash}) {
2916                                 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
2917                                         # update the insertion hash to execute only one query per package (insert instead insert+update)
2918                                         @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
2919                                 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
2920                                         if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
2921                                                 my $section;
2922                                                 my $description;
2923                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
2924                                                         length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
2925                                                         $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
2926                                                 }
2927                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2928                                                         $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
2929                                                 }
2930                                                 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2931                                         }
2932                                 }
2933                         }
2934                 }
2935         }
2937         # Check for orphaned entries
2938         foreach my $existing_entry (@existing_entries) {
2939                 my $distribution= @{$existing_entry}[0];
2940                 my $package= @{$existing_entry}[1];
2941                 my $version= @{$existing_entry}[2];
2942                 my $section= @{$existing_entry}[3];
2944                 if(
2945                         exists($insert_hash->{$distribution}->{$package}->{$version}) ||
2946                         exists($update_hash->{$distribution}->{$package}->{$version}) ||
2947                         exists($known_packages_hash->{$distribution}->{$package}->{$version})
2948                 ) {
2949                         next;
2950                 } else {
2951                         # Insert entry to delete hash
2952                         @{$delete_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section);
2953                 }
2954         }
2956         # unroll the insert hash
2957         foreach my $distribution (keys %{$insert_hash}) {
2958                 foreach my $package (keys %{$insert_hash->{$distribution}}) {
2959                         foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
2960                                 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
2961                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
2962                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
2963                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
2964                                 ."'$local_timestamp')";
2965                         }
2966                 }
2967         }
2969         # unroll the update hash
2970         foreach my $distribution (keys %{$update_hash}) {
2971                 foreach my $package (keys %{$update_hash->{$distribution}}) {
2972                         foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
2973                                 my $set = "";
2974                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
2975                                         $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
2976                                 }
2977                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2978                                         $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
2979                                 }
2980                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
2981                                         $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
2982                                 }
2983                                 if(defined($set) and length($set) > 0) {
2984                                         $set .= "timestamp = '$local_timestamp'";
2985                                 } else {
2986                                         next;
2987                                 }
2988                                 push @new_statement_list, 
2989                                 "UPDATE $main::packages_list_tn SET $set WHERE"
2990                                 ." distribution = '$distribution'"
2991                                 ." AND package = '$package'"
2992                                 ." AND version = '$version'";
2993                         }
2994                 }
2995         }
2996         
2997         # unroll the delete hash
2998         foreach my $distribution (keys %{$delete_hash}) {
2999                 foreach my $package (keys %{$delete_hash->{$distribution}}) {
3000                         foreach my $version (keys %{$delete_hash->{$distribution}->{$package}}) {
3001                                 my $section = @{$delete_hash->{$distribution}->{$package}->{$version}}[3];
3002                                 push @new_statement_list, "DELETE FROM $main::packages_list_tn WHERE distribution='$distribution' AND package='$package' AND version='$version' AND section='$section'";
3003                         }
3004                 }
3005         }
3007         @packages_list_statements = @new_statement_list;
3011 sub parse_package_info {
3012     my ($baseurl, $dist, $section, $session_id)= @_;
3013     my ($package);
3014     if (not defined $session_id) { $session_id = 0; }
3015     my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
3016     $repo_dirs{ "${repo_path}/pool" } = 1;
3018     foreach $package ("Packages.gz"){
3019         daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
3020         get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
3021         parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
3022     }
3023     
3027 sub get_package {
3028     my ($url, $dest, $session_id)= @_;
3029     if (not defined $session_id) { $session_id = 0; }
3031     my $tpath = dirname($dest);
3032     -d "$tpath" || mkpath "$tpath";
3034     # This is ugly, but I've no time to take a look at "how it works in perl"
3035     if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
3036         system("gunzip -cd '$dest' > '$dest.in'");
3037         daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 5);
3038         unlink($dest);
3039         daemon_log("$session_id DEBUG: delete file '$dest'", 5); 
3040     } else {
3041         daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' failed!", 1);
3042     }
3043     return 0;
3047 sub parse_package {
3048     my ($path, $dist, $srv_path, $session_id)= @_;
3049     if (not defined $session_id) { $session_id = 0;}
3050     my ($package, $version, $section, $description);
3051     my $PACKAGES;
3052     my $timestamp = &get_time();
3054     if(not stat("$path.in")) {
3055         daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
3056         return;
3057     }
3059     open($PACKAGES, "<$path.in");
3060     if(not defined($PACKAGES)) {
3061         daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1); 
3062         return;
3063     }
3065     # Read lines
3066     while (<$PACKAGES>){
3067         my $line = $_;
3068         # Unify
3069         chop($line);
3071         # Use empty lines as a trigger
3072         if ($line =~ /^\s*$/){
3073             my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
3074             push(@packages_list_statements, $sql);
3075             $package = "none";
3076             $version = "none";
3077             $section = "none";
3078             $description = "none"; 
3079             next;
3080         }
3082         # Trigger for package name
3083         if ($line =~ /^Package:\s/){
3084             ($package)= ($line =~ /^Package: (.*)$/);
3085             next;
3086         }
3088         # Trigger for version
3089         if ($line =~ /^Version:\s/){
3090             ($version)= ($line =~ /^Version: (.*)$/);
3091             next;
3092         }
3094         # Trigger for description
3095         if ($line =~ /^Description:\s/){
3096             ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
3097             next;
3098         }
3100         # Trigger for section
3101         if ($line =~ /^Section:\s/){
3102             ($section)= ($line =~ /^Section: (.*)$/);
3103             next;
3104         }
3106         # Trigger for filename
3107         if ($line =~ /^Filename:\s/){
3108             my ($filename) = ($line =~ /^Filename: (.*)$/);
3109             store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
3110             next;
3111         }
3112     }
3114     close( $PACKAGES );
3115     unlink( "$path.in" );
3119 sub store_fileinfo {
3120     my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
3122     my %fileinfo = (
3123         'package' => $package,
3124         'dist' => $dist,
3125         'version' => $vers,
3126     );
3128     $repo_files{ "${srvdir}/$file" } = \%fileinfo;
3132 sub cleanup_and_extract {
3133         my $fileinfo = $repo_files{ $File::Find::name };
3135         if( defined $fileinfo ) {
3136                 my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
3137                 my $sql;
3138                 my $package = $fileinfo->{ 'package' };
3139                 my $newver = $fileinfo->{ 'version' };
3141                 mkpath($dir);
3142                 system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
3144                 if( -f "$dir/DEBIAN/templates" ) {
3146                         daemon_log("0 DEBUG: Found debconf templates in '$package' - $newver", 7);
3148                         my $tmpl= ""; {
3149                                 local $/=undef;
3150                                 open FILE, "$dir/DEBIAN/templates";
3151                                 $tmpl = &encode_base64(<FILE>);
3152                                 close FILE;
3153                         }
3154                         rmtree("$dir/DEBIAN/templates");
3156                         $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
3157                         push @packages_list_statements, $sql;
3158                 }
3159         }
3161         return;
3165 sub register_at_foreign_servers {   
3166     my ($kernel) = $_[KERNEL];
3168     # hole alle bekannten server aus known_server_db
3169     my $server_sql = "SELECT * FROM $known_server_tn";
3170     my $server_res = $known_server_db->exec_statement($server_sql);
3172     # no entries in known_server_db
3173     if (not ref(@$server_res[0]) eq "ARRAY") { 
3174         # TODO
3175     }
3177     # detect already connected clients
3178     my $client_sql = "SELECT * FROM $known_clients_tn"; 
3179     my $client_res = $known_clients_db->exec_statement($client_sql);
3181     # send my server details to all other gosa-si-server within the network
3182     foreach my $hit (@$server_res) {
3183         my $hostname = @$hit[0];
3184         my $hostkey = &create_passwd;
3186         # add already connected clients to registration message 
3187         my $myhash = &create_xml_hash('new_server', $server_address, $hostname);
3188         &add_content2xml_hash($myhash, 'key', $hostkey);
3189         map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res);
3191         # add locally loaded gosa-si modules to registration message
3192         my $loaded_modules = {};
3193         while (my ($package, $pck_info) = each %$known_modules) {
3194                                                 next if ((!defined(@$pck_info[2])) || (!(ref (@$pck_info[2]) eq 'HASH')));
3195                                                 foreach my $act_module (keys(%{@$pck_info[2]})) {
3196                                                         $loaded_modules->{$act_module} = ""; 
3197                                                 }
3198         }
3200         map(&add_content2xml_hash($myhash, "loaded_modules", $_), keys(%$loaded_modules));
3202         # add macaddress to registration message
3203         my ($host_ip, $host_port) = split(/:/, $hostname);
3204         my $local_ip = &get_local_ip_for_remote_ip($host_ip);
3205         my $network_interface= &get_interface_for_ip($local_ip);
3206         my $host_mac = &get_mac_for_interface($network_interface);
3207         &add_content2xml_hash($myhash, 'macaddress', $host_mac);
3208         
3209         # build registration message and send it
3210         my $foreign_server_msg = &create_xml_string($myhash);
3211         my $error = &send_msg_to_target($foreign_server_msg, $hostname, $ServerPackages_key, "new_server", 0); 
3212     }
3213     
3214     $kernel->delay_set("register_at_foreign_servers", $foreign_servers_register_delay); 
3215     return;
3219 #==== MAIN = main ==============================================================
3220 #  parse commandline options
3221 Getopt::Long::Configure( "bundling" );
3222 GetOptions("h|help" => \&usage,
3223         "c|config=s" => \$cfg_file,
3224         "f|foreground" => \$foreground,
3225         "v|verbose+" => \$verbose,
3226         "no-arp+" => \$no_arp,
3227            );
3229 #  read and set config parameters
3230 &check_cmdline_param ;
3231 &read_configfile($cfg_file, %cfg_defaults);
3232 &check_pid;
3234 $SIG{CHLD} = 'IGNORE';
3236 # forward error messages to logfile
3237 if( ! $foreground ) {
3238   open( STDIN,  '+>/dev/null' );
3239   open( STDOUT, '+>&STDIN'    );
3240   open( STDERR, '+>&STDIN'    );
3243 # Just fork, if we are not in foreground mode
3244 if( ! $foreground ) { 
3245     chdir '/'                 or die "Can't chdir to /: $!";
3246     $pid = fork;
3247     setsid                    or die "Can't start a new session: $!";
3248     umask 0;
3249 } else { 
3250     $pid = $$; 
3253 # Do something useful - put our PID into the pid_file
3254 if( 0 != $pid ) {
3255     open( LOCK_FILE, ">$pid_file" );
3256     print LOCK_FILE "$pid\n";
3257     close( LOCK_FILE );
3258     if( !$foreground ) { 
3259         exit( 0 ) 
3260     };
3263 # parse head url and revision from svn
3264 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
3265 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
3266 $server_headURL = defined $1 ? $1 : 'unknown' ;
3267 $server_revision = defined $2 ? $2 : 'unknown' ;
3268 if ($server_headURL =~ /\/tag\// || 
3269         $server_headURL =~ /\/branches\// ) {
3270     $server_status = "stable"; 
3271 } else {
3272     $server_status = "developmental" ;
3275 # Prepare log file
3276 $root_uid = getpwnam('root');
3277 $adm_gid = getgrnam('adm');
3278 chmod(0640, $log_file);
3279 chown($root_uid, $adm_gid, $log_file);
3280 chown($root_uid, $adm_gid, "/var/lib/gosa-si");
3282 daemon_log(" ", 1);
3283 daemon_log("$0 started!", 1);
3284 daemon_log("status: $server_status", 1);
3285 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1); 
3288     no strict "refs";
3290     if ($db_module eq "DBmysql") {
3291         # connect to incoming_db
3292         $incoming_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3294         # connect to gosa-si job queue
3295         $job_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3297         # connect to known_clients_db
3298         $known_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3300         # connect to foreign_clients_db
3301         $foreign_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3303         # connect to known_server_db
3304         $known_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3306         # connect to login_usr_db
3307         $login_users_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3309         # connect to fai_server_db 
3310         $fai_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3312         # connect to fai_release_db
3313         $fai_release_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3315         # connect to packages_list_db
3316         $packages_list_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3318         # connect to messaging_db
3319         $messaging_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3321     } elsif ($db_module eq "DBsqlite") {
3322         # connect to incoming_db
3323         unlink($incoming_file_name);
3324         $incoming_db = GOSA::DBsqlite->new($incoming_file_name);
3325         
3326         # connect to gosa-si job queue
3327         unlink($job_queue_file_name);  ## just for debugging
3328         $job_db = GOSA::DBsqlite->new($job_queue_file_name);
3329         chmod(0660, $job_queue_file_name);
3330         chown($root_uid, $adm_gid, $job_queue_file_name);
3331         
3332         # connect to known_clients_db
3333         unlink($known_clients_file_name);   ## just for debugging
3334         $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
3335         chmod(0660, $known_clients_file_name);
3336         chown($root_uid, $adm_gid, $known_clients_file_name);
3337         
3338         # connect to foreign_clients_db
3339         unlink($foreign_clients_file_name);
3340         $foreign_clients_db = GOSA::DBsqlite->new($foreign_clients_file_name);
3341         chmod(0660, $foreign_clients_file_name);
3342         chown($root_uid, $adm_gid, $foreign_clients_file_name);
3343         
3344         # connect to known_server_db
3345         unlink($known_server_file_name);
3346         $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
3347         chmod(0660, $known_server_file_name);
3348         chown($root_uid, $adm_gid, $known_server_file_name);
3349         
3350         # connect to login_usr_db
3351         unlink($login_users_file_name);
3352         $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
3353         chmod(0660, $login_users_file_name);
3354         chown($root_uid, $adm_gid, $login_users_file_name);
3355         
3356         # connect to fai_server_db
3357         unlink($fai_server_file_name);
3358         $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
3359         chmod(0660, $fai_server_file_name);
3360         chown($root_uid, $adm_gid, $fai_server_file_name);
3361         
3362         # connect to fai_release_db
3363         unlink($fai_release_file_name);
3364         $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
3365         chmod(0660, $fai_release_file_name);
3366         chown($root_uid, $adm_gid, $fai_release_file_name);
3367         
3368         # connect to packages_list_db
3369         #unlink($packages_list_file_name);
3370         unlink($packages_list_under_construction);
3371         $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
3372         chmod(0660, $packages_list_file_name);
3373         chown($root_uid, $adm_gid, $packages_list_file_name);
3374         
3375         # connect to messaging_db
3376         unlink($messaging_file_name);
3377         $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
3378         chmod(0660, $messaging_file_name);
3379         chown($root_uid, $adm_gid, $messaging_file_name);
3380     }
3383 # Creating tables
3384 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
3385 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
3386 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
3387 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
3388 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
3389 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
3390 $foreign_clients_db->create_table($foreign_clients_tn, \@foreign_clients_col_names);
3391 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
3392 $incoming_db->create_table($incoming_tn, \@incoming_col_names);
3393 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
3396 # create xml object used for en/decrypting
3397 $xml = new XML::Simple();
3400 # foreign servers 
3401 my @foreign_server_list;
3403 # add foreign server from cfg file
3404 if ($foreign_server_string ne "") {
3405     my @cfg_foreign_server_list = split(",", $foreign_server_string);
3406     foreach my $foreign_server (@cfg_foreign_server_list) {
3407         push(@foreign_server_list, $foreign_server);
3408     }
3410     daemon_log("0 INFO: found foreign server in config file: ".join(", ", @foreign_server_list), 5);
3413 # Perform a DNS lookup for server registration if flag is true
3414 if ($dns_lookup eq "true") {
3415     # Add foreign server from dns
3416     my @tmp_servers;
3417     if (not $server_domain) {
3418         # Try our DNS Searchlist
3419         for my $domain(get_dns_domains()) {
3420             chomp($domain);
3421             my ($tmp_domains, $error_string) = &get_server_addresses($domain);
3422             if(@$tmp_domains) {
3423                 for my $tmp_server(@$tmp_domains) {
3424                     push @tmp_servers, $tmp_server;
3425                 }
3426             }
3427         }
3428         if(@tmp_servers && length(@tmp_servers)==0) {
3429             daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3430         }
3431     } else {
3432         @tmp_servers = &get_server_addresses($server_domain);
3433         if( 0 == @tmp_servers ) {
3434             daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3435         }
3436     }
3438     daemon_log("0 INFO: found foreign server via DNS ".join(", ", @tmp_servers), 5);    
3440     foreach my $server (@tmp_servers) { 
3441         unshift(@foreign_server_list, $server); 
3442     }
3443 } else {
3444     daemon_log("0 INFO: DNS lookup for server registration is disabled", 5);
3448 # eliminate duplicate entries
3449 @foreign_server_list = &del_doubles(@foreign_server_list);
3450 my $all_foreign_server = join(", ", @foreign_server_list);
3451 daemon_log("0 INFO: found foreign server in config file and DNS: '$all_foreign_server'", 5);
3453 # add all found foreign servers to known_server
3454 my $act_timestamp = &get_time();
3455 foreach my $foreign_server (@foreign_server_list) {
3457         # do not add myself to known_server_db
3458         if (&is_local($foreign_server)) { next; }
3459         ######################################
3461     my $res = $known_server_db->add_dbentry( {table=>$known_server_tn, 
3462             primkey=>['hostname'],
3463             hostname=>$foreign_server,
3464             macaddress=>"",
3465             status=>'not_jet_registered',
3466             hostkey=>"none",
3467             loaded_modules => "none", 
3468             timestamp=>$act_timestamp,
3469             } );
3473 # Import all modules
3474 &import_modules;
3476 # Check wether all modules are gosa-si valid passwd check
3477 &password_check;
3479 # Prepare for using Opsi 
3480 if ($opsi_enabled eq "true") {
3481     use JSON::RPC::Client;
3482     use XML::Quote qw(:all);
3483     $opsi_url= "https://".$opsi_admin.":".$opsi_password."@".$opsi_server.":4447/rpc";
3484     $opsi_client = new JSON::RPC::Client;
3488 POE::Component::Server::TCP->new(
3489         Alias => "TCP_SERVER",
3490         Port => $server_port,
3491         ClientInput => sub {
3492                 my ($kernel, $input, $heap, $session) = @_[KERNEL, ARG0, HEAP, SESSION];
3493         my $session_id = $session->ID;
3494         my $remote_ip = $heap->{'remote_ip'};
3495                 push(@msgs_to_decrypt, $input);
3496         &daemon_log("$session_id DEBUG: incoming message from '$remote_ip'", 7);
3497                 $kernel->yield("msg_to_decrypt");
3498         },
3499         InlineStates => {
3500                 msg_to_decrypt => \&msg_to_decrypt,
3501                 next_task => \&next_task,
3502                 task_result => \&handle_task_result,
3503                 task_done   => \&handle_task_done,
3504                 task_debug  => \&handle_task_debug,
3505                 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3506         }
3507 );
3509 daemon_log("0 INFO: start socket for incoming xml messages at port '$server_port' ", 1);
3511 # create session for repeatedly checking the job queue for jobs
3512 POE::Session->create(
3513         inline_states => {
3514                 _start => \&session_start,
3515         register_at_foreign_servers => \&register_at_foreign_servers,
3516         sig_handler => \&sig_handler,
3517         next_task => \&next_task,
3518         task_result => \&handle_task_result,
3519         task_done   => \&handle_task_done,
3520         task_debug  => \&handle_task_debug,
3521         watch_for_next_tasks => \&watch_for_next_tasks,
3522         watch_for_new_messages => \&watch_for_new_messages,
3523         watch_for_delivery_messages => \&watch_for_delivery_messages,
3524         watch_for_done_messages => \&watch_for_done_messages,
3525                 watch_for_new_jobs => \&watch_for_new_jobs,
3526         watch_for_modified_jobs => \&watch_for_modified_jobs,
3527         watch_for_done_jobs => \&watch_for_done_jobs,
3528         watch_for_opsi_jobs => \&watch_for_opsi_jobs,
3529         watch_for_old_known_clients => \&watch_for_old_known_clients,
3530         create_packages_list_db => \&run_create_packages_list_db,
3531         create_fai_server_db => \&run_create_fai_server_db,
3532         create_fai_release_db => \&run_create_fai_release_db,
3533                 recreate_packages_db => \&run_recreate_packages_db,
3534         session_run_result => \&session_run_result,
3535         session_run_debug => \&session_run_debug,
3536         session_run_done => \&session_run_done,
3537         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3538         }
3539 );
3542 POE::Kernel->run();
3543 exit;