Code

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