Code

removed images.
[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::DBsqlite;
52 use GOSA::GosaSupportDaemon;
53 use POE qw(Component::Server::TCP Wheel::Run Filter::Reference);
54 use Net::LDAP;
55 use Net::LDAP::Util qw(:escape);
56 use Time::HiRes qw( usleep);
57 use DateTime;
59 my $modules_path = "/usr/lib/gosa-si/modules";
60 use lib "/usr/lib/gosa-si/modules";
62 # revision number of server and program name
63 my $server_version = '$HeadURL: https://oss.gonicus.de/repositories/gosa/trunk/gosa-si/gosa-si-server $:$Rev: 10826 $';
64 my $server_headURL;
65 my $server_revision;
66 my $server_status;
67 our $prg= basename($0);
69 our $global_kernel;
70 my ($foreground, $ping_timeout);
71 my ($server);
72 my ($gosa_server, $job_queue_timeout, $job_queue_loop_delay);
73 my ($messaging_db_loop_delay);
74 my ($known_modules);
75 my ($procid, $pid);
76 my ($arp_fifo);
77 my ($xml);
78 my $sources_list;
79 my $max_clients;
80 my %repo_files=();
81 my $repo_path;
82 my %repo_dirs=();
83 # variables declared in config file are always set to 'our'
84 our (%cfg_defaults, $log_file, $pid_file, 
85     $server_ip, $server_port, $ClientPackages_key, 
86     $arp_activ, $gosa_unit_tag,
87     $GosaPackages_key, $gosa_ip, $gosa_port, $gosa_timeout,
88     $foreign_server_string, $server_domain, $ServerPackages_key, $foreign_servers_register_delay,
89     $wake_on_lan_passwd, $job_synchronization, $modified_jobs_loop_delay,
90 );
92 # additional variable which should be globaly accessable
93 our $server_address;
94 our $server_mac_address;
95 our $gosa_address;
96 our $no_arp;
97 our $verbose;
98 our $forground;
99 our $cfg_file;
100 our ($ldap_uri, $ldap_base, $ldap_admin_dn, $ldap_admin_password, $ldap_server_dn);
102 # dak variables
103 our $dak_base_directory;
104 our $dak_signing_keys_directory;
105 our $dak_queue_directory;
106 our $dak_user;
108 # specifies the verbosity of the daemon_log
109 $verbose = 0 ;
111 # if foreground is not null, script will be not forked to background
112 $foreground = 0 ;
114 # specifies the timeout seconds while checking the online status of a registrating client
115 $ping_timeout = 5;
117 $no_arp = 0;
118 my $packages_list_under_construction = "/tmp/packages_list_creation_in_progress";
119 my @packages_list_statements;
120 my $watch_for_new_jobs_in_progress = 0;
122 # holds all incoming decrypted messages
123 our $incoming_db;
124 our $incoming_tn = 'incoming';
125 my $incoming_file_name;
126 my @incoming_col_names = ("id INTEGER PRIMARY KEY", 
127         "timestamp DEFAULT 'none'", 
128         "headertag DEFAULT 'none'",
129                 "targettag DEFAULT 'none'",
130         "xmlmessage DEFAULT 'none'",
131         "module DEFAULT 'none'",
132         "sessionid DEFAULT '0'",
133         );
135 # holds all gosa jobs
136 our $job_db;
137 our $job_queue_tn = 'jobs';
138 my $job_queue_file_name;
139 my @job_queue_col_names = ("id INTEGER PRIMARY KEY", 
140                 "timestamp DEFAULT 'none'", 
141                 "status DEFAULT 'none'", 
142                 "result DEFAULT 'none'", 
143                 "progress DEFAULT 'none'", 
144         "headertag DEFAULT 'none'", 
145                 "targettag DEFAULT 'none'", 
146                 "xmlmessage DEFAULT 'none'", 
147                 "macaddress DEFAULT 'none'",
148                 "plainname DEFAULT 'none'",
149         "siserver DEFAULT 'none'",
150         "modified DEFAULT '0'",
151                 );
153 # holds all other gosa-si-server
154 our $known_server_db;
155 our $known_server_tn = "known_server";
156 my $known_server_file_name;
157 my @known_server_col_names = ("hostname", "status", "hostkey", "timestamp");
159 # holds all registrated clients
160 our $known_clients_db;
161 our $known_clients_tn = "known_clients";
162 my $known_clients_file_name;
163 my @known_clients_col_names = ("hostname", "status", "hostkey", "timestamp", "macaddress", "events", "keylifetime");
165 # holds all registered clients at a foreign server
166 our $foreign_clients_db;
167 our $foreign_clients_tn = "foreign_clients"; 
168 my $foreign_clients_file_name;
169 my @foreign_clients_col_names = ("hostname", "macaddress", "regserver", "timestamp");
171 # holds all logged in user at each client 
172 our $login_users_db;
173 our $login_users_tn = "login_users";
174 my $login_users_file_name;
175 my @login_users_col_names = ("client", "user", "timestamp");
177 # holds all fai server, the debian release and tag
178 our $fai_server_db;
179 our $fai_server_tn = "fai_server"; 
180 my $fai_server_file_name;
181 our @fai_server_col_names = ("timestamp", "server", "release", "sections", "tag"); 
183 our $fai_release_db;
184 our $fai_release_tn = "fai_release"; 
185 my $fai_release_file_name;
186 our @fai_release_col_names = ("timestamp", "release", "class", "type", "state"); 
188 # holds all packages available from different repositories
189 our $packages_list_db;
190 our $packages_list_tn = "packages_list";
191 my $packages_list_file_name;
192 our @packages_list_col_names = ("distribution", "package", "version", "section", "description", "template", "timestamp");
193 my $outdir = "/tmp/packages_list_db";
194 my $arch = "i386"; 
196 # holds all messages which should be delivered to a user
197 our $messaging_db;
198 our $messaging_tn = "messaging"; 
199 our @messaging_col_names = ("id INTEGER", "subject", "message_from", "message_to", 
200         "flag", "direction", "delivery_time", "message", "timestamp" );
201 my $messaging_file_name;
203 # path to directory to store client install log files
204 our $client_fai_log_dir = "/var/log/fai"; 
206 # queue which stores taskes until one of the $max_children children are ready to process the task
207 my @tasks = qw();
208 my @msgs_to_decrypt = qw();
209 my $max_children = 2;
212 %cfg_defaults = (
213 "general" => {
214     "log-file" => [\$log_file, "/var/run/".$prg.".log"],
215     "pid-file" => [\$pid_file, "/var/run/".$prg.".pid"],
216     },
217 "server" => {
218     "port" => [\$server_port, "20081"],
219     "known-clients"        => [\$known_clients_file_name, '/var/lib/gosa-si/clients.db' ],
220     "known-servers"        => [\$known_server_file_name, '/var/lib/gosa-si/servers.db'],
221     "incoming"             => [\$incoming_file_name, '/var/lib/gosa-si/incoming.db'],
222     "login-users"          => [\$login_users_file_name, '/var/lib/gosa-si/users.db'],
223     "fai-server"           => [\$fai_server_file_name, '/var/lib/gosa-si/fai_server.db'],
224     "fai-release"          => [\$fai_release_file_name, '/var/lib/gosa-si/fai_release.db'],
225     "packages-list"        => [\$packages_list_file_name, '/var/lib/gosa-si/packages.db'],
226     "messaging"            => [\$messaging_file_name, '/var/lib/gosa-si/messaging.db'],
227     "foreign-clients"      => [\$foreign_clients_file_name, '/var/lib/gosa-si/foreign_clients.db'],
228     "source-list"          => [\$sources_list, '/etc/apt/sources.list'],
229     "repo-path"            => [\$repo_path, '/srv/www/repository'],
230     "ldap-uri"             => [\$ldap_uri, ""],
231     "ldap-base"            => [\$ldap_base, ""],
232     "ldap-admin-dn"        => [\$ldap_admin_dn, ""],
233     "ldap-admin-password"  => [\$ldap_admin_password, ""],
234     "gosa-unit-tag"        => [\$gosa_unit_tag, ""],
235     "max-clients"          => [\$max_clients, 10],
236     "wol-password"           => [\$wake_on_lan_passwd, ""],
237     },
238 "GOsaPackages" => {
239     "ip" => [\$gosa_ip, "0.0.0.0"],
240     "port" => [\$gosa_port, "20082"],
241     "job-queue" => [\$job_queue_file_name, '/var/lib/gosa-si/jobs.db'],
242     "job-queue-loop-delay" => [\$job_queue_loop_delay, 3],
243     "messaging-db-loop-delay" => [\$messaging_db_loop_delay, 3],
244     "key" => [\$GosaPackages_key, "none"],
245         "dak-base" => [\$dak_base_directory, "/srv/archive"],
246         "dak-keyring" => [\$dak_signing_keys_directory, "/srv/archive/keyrings"],
247         "dak-queue" => [\$dak_queue_directory, "/srv/archive/queue"],
248         "dak-user" => [\$dak_user, "deb-dak"],
249     },
250 "ClientPackages" => {
251     "key" => [\$ClientPackages_key, "none"],
252     },
253 "ServerPackages"=> {
254     "address"      => [\$foreign_server_string, ""],
255     "domain"  => [\$server_domain, ""],
256     "key"     => [\$ServerPackages_key, "none"],
257     "key-lifetime" => [\$foreign_servers_register_delay, 120],
258     "job-synchronization-enabled" => [\$job_synchronization, "true"],
259     "synchronization-loop" => [\$modified_jobs_loop_delay, 5],
261 );
264 #===  FUNCTION  ================================================================
265 #         NAME:  usage
266 #   PARAMETERS:  nothing
267 #      RETURNS:  nothing
268 #  DESCRIPTION:  print out usage text to STDERR
269 #===============================================================================
270 sub usage {
271     print STDERR << "EOF" ;
272 usage: $prg [-hvf] [-c config]
274            -h        : this (help) message
275            -c <file> : config file
276            -f        : foreground, process will not be forked to background
277            -v        : be verbose (multiple to increase verbosity)
278            -no-arp   : starts $prg without connection to arp module
279  
280 EOF
281     print "\n" ;
285 #===  FUNCTION  ================================================================
286 #         NAME:  read_configfile
287 #   PARAMETERS:  cfg_file - string -
288 #      RETURNS:  nothing
289 #  DESCRIPTION:  read cfg_file and set variables
290 #===============================================================================
291 sub read_configfile {
292     my $cfg;
293     if( defined( $cfg_file) && ( (-s $cfg_file) > 0 )) {
294         if( -r $cfg_file ) {
295             $cfg = Config::IniFiles->new( -file => $cfg_file );
296         } else {
297             print STDERR "Couldn't read config file!\n";
298         }
299     } else {
300         $cfg = Config::IniFiles->new() ;
301     }
302     foreach my $section (keys %cfg_defaults) {
303         foreach my $param (keys %{$cfg_defaults{ $section }}) {
304             my $pinfo = $cfg_defaults{ $section }{ $param };
305             ${@$pinfo[ 0 ]} = $cfg->val( $section, $param, @$pinfo[ 1 ] );
306         }
307     }
311 #===  FUNCTION  ================================================================
312 #         NAME:  logging
313 #   PARAMETERS:  level - string - default 'info'
314 #                msg - string -
315 #                facility - string - default 'LOG_DAEMON'
316 #      RETURNS:  nothing
317 #  DESCRIPTION:  function for logging
318 #===============================================================================
319 sub daemon_log {
320     # log into log_file
321     my( $msg, $level ) = @_;
322     if(not defined $msg) { return }
323     if(not defined $level) { $level = 1 }
324     if(defined $log_file){
325         open(LOG_HANDLE, ">>$log_file");
326         chmod 0600, $log_file;
327         if(not defined open( LOG_HANDLE, ">>$log_file" )) {
328             print STDERR "cannot open $log_file: $!";
329             return 
330         }
331         chomp($msg);
332         $msg =~s/\n//g;   # no newlines are allowed in log messages, this is important for later log parsing
333         if($level <= $verbose){
334             my ($seconds, $minutes, $hours, $monthday, $month,
335                     $year, $weekday, $yearday, $sommertime) = localtime(time);
336             $hours = $hours < 10 ? $hours = "0".$hours : $hours;
337             $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
338             $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
339             my @monthnames = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
340             $month = $monthnames[$month];
341             $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
342             $year+=1900;
343             my $name = $prg;
345             my $log_msg = "$month $monthday $hours:$minutes:$seconds $name $msg\n";
346             print LOG_HANDLE $log_msg;
347             if( $foreground ) { 
348                 print STDERR $log_msg;
349             }
350         }
351         close( LOG_HANDLE );
352     }
356 #===  FUNCTION  ================================================================
357 #         NAME:  check_cmdline_param
358 #   PARAMETERS:  nothing
359 #      RETURNS:  nothing
360 #  DESCRIPTION:  validates commandline parameter
361 #===============================================================================
362 sub check_cmdline_param () {
363     my $err_config;
364     my $err_counter = 0;
365         if(not defined($cfg_file)) {
366                 $cfg_file = "/etc/gosa-si/server.conf";
367                 if(! -r $cfg_file) {
368                         $err_config = "please specify a config file";
369                         $err_counter += 1;
370                 }
371     }
372     if( $err_counter > 0 ) {
373         &usage( "", 1 );
374         if( defined( $err_config)) { print STDERR "$err_config\n"}
375         print STDERR "\n";
376         exit( -1 );
377     }
381 #===  FUNCTION  ================================================================
382 #         NAME:  check_pid
383 #   PARAMETERS:  nothing
384 #      RETURNS:  nothing
385 #  DESCRIPTION:  handels pid processing
386 #===============================================================================
387 sub check_pid {
388     $pid = -1;
389     # Check, if we are already running
390     if( open(LOCK_FILE, "<$pid_file") ) {
391         $pid = <LOCK_FILE>;
392         if( defined $pid ) {
393             chomp( $pid );
394             if( -f "/proc/$pid/stat" ) {
395                 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
396                 if( $stat ) {
397                                         daemon_log("ERROR: Already running",1);
398                     close( LOCK_FILE );
399                     exit -1;
400                 }
401             }
402         }
403         close( LOCK_FILE );
404         unlink( $pid_file );
405     }
407     # create a syslog msg if it is not to possible to open PID file
408     if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
409         my($msg) = "Couldn't obtain lockfile '$pid_file' ";
410         if (open(LOCK_FILE, '<', $pid_file)
411                 && ($pid = <LOCK_FILE>))
412         {
413             chomp($pid);
414             $msg .= "(PID $pid)\n";
415         } else {
416             $msg .= "(unable to read PID)\n";
417         }
418         if( ! ($foreground) ) {
419             openlog( $0, "cons,pid", "daemon" );
420             syslog( "warning", $msg );
421             closelog();
422         }
423         else {
424             print( STDERR " $msg " );
425         }
426         exit( -1 );
427     }
430 #===  FUNCTION  ================================================================
431 #         NAME:  import_modules
432 #   PARAMETERS:  module_path - string - abs. path to the directory the modules 
433 #                are stored
434 #      RETURNS:  nothing
435 #  DESCRIPTION:  each file in module_path which ends with '.pm' and activation 
436 #                state is on is imported by "require 'file';"
437 #===============================================================================
438 sub import_modules {
439     daemon_log(" ", 1);
441     if (not -e $modules_path) {
442         daemon_log("0 ERROR: cannot find directory or directory is not readable: $modules_path", 1);   
443     }
445     opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
446     while (defined (my $file = readdir (DIR))) {
447         if (not $file =~ /(\S*?).pm$/) {
448             next;
449         }
450                 my $mod_name = $1;
452         if( $file =~ /ArpHandler.pm/ ) {
453             if( $no_arp > 0 ) {
454                 next;
455             }
456         }
457         
458         eval { require $file; };
459         if ($@) {
460             daemon_log("0 ERROR: gosa-si-server could not load module $file", 1);
461             daemon_log("$@", 5);
462                 } else {
463                         my $info = eval($mod_name.'::get_module_info()');
464                         # Only load module if get_module_info() returns a non-null object
465                         if( $info ) {
466                                 my ($input_address, $input_key, $input, $input_active, $input_type) = @{$info};
467                                 $known_modules->{$mod_name} = $info;
468                                 daemon_log("0 INFO: module $mod_name loaded", 5);
469                         }
470                 }
471     }   
472     close (DIR);
475 #===  FUNCTION  ================================================================
476 #         NAME:  password_check
477 #   PARAMETERS:  nothing
478 #      RETURNS:  nothing
479 #  DESCRIPTION:  escalates an critical error if two modules exist which are avaialable by 
480 #                the same password
481 #===============================================================================
482 sub password_check {
483     my $passwd_hash = {};
484     while (my ($mod_name, $mod_info) = each %$known_modules) {
485         my $mod_passwd = @$mod_info[1];
486         if (not defined $mod_passwd) { next; }
487         if (not exists $passwd_hash->{$mod_passwd}) {
488             $passwd_hash->{$mod_passwd} = $mod_name;
490         # escalates critical error
491         } else {
492             &daemon_log("0 ERROR: two loaded modules do have the same password. Please modify the 'key'-parameter in config file");
493             &daemon_log("0 ERROR: module='$mod_name' and module='".$passwd_hash->{$mod_passwd}."'");
494             exit( -1 );
495         }
496     }
501 #===  FUNCTION  ================================================================
502 #         NAME:  sig_int_handler
503 #   PARAMETERS:  signal - string - signal arose from system
504 #      RETURNS:  nothing
505 #  DESCRIPTION:  handels tasks to be done befor signal becomes active
506 #===============================================================================
507 sub sig_int_handler {
508     my ($signal) = @_;
510 #       if (defined($ldap_handle)) {
511 #               $ldap_handle->disconnect;
512 #       }
513     # TODO alle verbliebenden ldap verbindungen aus allen heaps beenden
514     
516     daemon_log("shutting down gosa-si-server", 1);
517     system("kill `ps -C gosa-si-server -o pid=`");
519 $SIG{INT} = \&sig_int_handler;
522 sub check_key_and_xml_validity {
523     my ($crypted_msg, $module_key, $session_id) = @_;
524     my $msg;
525     my $msg_hash;
526     my $error_string;
527     eval{
528         $msg = &decrypt_msg($crypted_msg, $module_key);
530         if ($msg =~ /<xml>/i){
531             $msg =~ s/\s+/ /g;  # just for better daemon_log
532             daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 8);
533             $msg_hash = $xml->XMLin($msg, ForceArray=>1);
535             ##############
536             # check header
537             if( not exists $msg_hash->{'header'} ) { die "no header specified"; }
538             my $header_l = $msg_hash->{'header'};
539             if( 1 > @{$header_l} ) { die 'empty header tag'; }
540             if( 1 < @{$header_l} ) { die 'more than one header specified'; }
541             my $header = @{$header_l}[0];
542             if( 0 == length $header) { die 'empty string in header tag'; }
544             ##############
545             # check source
546             if( not exists $msg_hash->{'source'} ) { die "no source specified"; }
547             my $source_l = $msg_hash->{'source'};
548             if( 1 > @{$source_l} ) { die 'empty source tag'; }
549             if( 1 < @{$source_l} ) { die 'more than one source specified'; }
550             my $source = @{$source_l}[0];
551             if( 0 == length $source) { die 'source error'; }
553             ##############
554             # check target
555             if( not exists $msg_hash->{'target'} ) { die "no target specified"; }
556             my $target_l = $msg_hash->{'target'};
557             if( 1 > @{$target_l} ) { die 'empty target tag'; }
558         }
559     };
560     if($@) {
561         daemon_log("$session_id DEBUG: do not understand the message: $@", 7);
562         $msg = undef;
563         $msg_hash = undef;
564     }
566     return ($msg, $msg_hash);
570 sub check_outgoing_xml_validity {
571     my ($msg, $session_id) = @_;
573     my $msg_hash;
574     eval{
575         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
577         ##############
578         # check header
579         my $header_l = $msg_hash->{'header'};
580         if( 1 != @{$header_l} ) {
581             die 'no or more than one headers specified';
582         }
583         my $header = @{$header_l}[0];
584         if( 0 == length $header) {
585             die 'header has length 0';
586         }
588         ##############
589         # check source
590         my $source_l = $msg_hash->{'source'};
591         if( 1 != @{$source_l} ) {
592             die 'no or more than 1 sources specified';
593         }
594         my $source = @{$source_l}[0];
595         if( 0 == length $source) {
596             die 'source has length 0';
597         }
598         unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
599                 $source =~ /^GOSA$/i ) {
600             die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
601         }
602         
603         ##############
604         # check target  
605         my $target_l = $msg_hash->{'target'};
606         if( 0 == @{$target_l} ) {
607             die "no targets specified";
608         }
609         foreach my $target (@$target_l) {
610             if( 0 == length $target) {
611                 die "target has length 0";
612             }
613             unless( $target =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
614                     $target =~ /^GOSA$/i ||
615                     $target =~ /^\*$/ ||
616                     $target =~ /KNOWN_SERVER/i ||
617                     $target =~ /JOBDB/i ||
618                     $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 ){
619                 die "target '$target' is not a complete ip-address with port or a valid target name or a mac-address";
620             }
621         }
622     };
623     if($@) {
624         daemon_log("$session_id ERROR: outgoing msg is not gosa-si envelope conform: ", 1);
625         daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 1);
626         $msg_hash = undef;
627     }
629     return ($msg_hash);
633 sub input_from_known_server {
634     my ($input, $remote_ip, $session_id) = @_ ;  
635     my ($msg, $msg_hash, $module);
637     my $sql_statement= "SELECT * FROM known_server";
638     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
640     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
641         my $host_name = $hit->{hostname};
642         if( not $host_name =~ "^$remote_ip") {
643             next;
644         }
645         my $host_key = $hit->{hostkey};
646         daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 7);
647         daemon_log("$session_id DEBUG: input_from_known_server: host_key: $host_key", 7);
649         # check if module can open msg envelope with module key
650         my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
651         if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
652             daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 7);
653             daemon_log("$@", 8);
654             next;
655         }
656         else {
657             $msg = $tmp_msg;
658             $msg_hash = $tmp_msg_hash;
659             $module = "ServerPackages";
660             last;
661         }
662     }
664     if( (!$msg) || (!$msg_hash) || (!$module) ) {
665         daemon_log("$session_id DEBUG: Incoming message is not from a known server", 7);
666     }
667   
668     return ($msg, $msg_hash, $module);
672 sub input_from_known_client {
673     my ($input, $remote_ip, $session_id) = @_ ;  
674     my ($msg, $msg_hash, $module);
676     my $sql_statement= "SELECT * FROM known_clients";
677     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
678     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
679         my $host_name = $hit->{hostname};
680         if( not $host_name =~ /^$remote_ip:\d*$/) {
681                 next;
682                 }
683         my $host_key = $hit->{hostkey};
684         &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 7);
685         &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 7);
687         # check if module can open msg envelope with module key
688         ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
690         if( (!$msg) || (!$msg_hash) ) {
691             &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 7);
692             &daemon_log("$@", 8);
693             next;
694         }
695         else {
696             $module = "ClientPackages";
697             last;
698         }
699     }
701     if( (!$msg) || (!$msg_hash) || (!$module) ) {
702         &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 7);
703     }
705     return ($msg, $msg_hash, $module);
709 sub input_from_unknown_host {
710     no strict "refs";
711     my ($input, $session_id) = @_ ;
712     my ($msg, $msg_hash, $module);
713     my $error_string;
714     
715         my %act_modules = %$known_modules;
716         
717     while( my ($mod, $info) = each(%act_modules)) {
719         # check a key exists for this module
720         my $module_key = ${$mod."_key"};
721         if( not defined $module_key ) {
722             if( $mod eq 'ArpHandler' ) {
723                 next;
724             }
725             daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
726             next;
727         }
728         daemon_log("$session_id DEBUG: $mod: $module_key", 7);
730         # check if module can open msg envelope with module key
731         ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
732         if( (not defined $msg) || (not defined $msg_hash) ) {
733             next;
734         }
735         else {
736             $module = $mod;
737             last;
738         }
739     }
741     if( (!$msg) || (!$msg_hash) || (!$module)) {
742         daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 7);
743     }
745     return ($msg, $msg_hash, $module);
749 sub create_ciphering {
750     my ($passwd) = @_;
751         if((!defined($passwd)) || length($passwd)==0) {
752                 $passwd = "";
753         }
754     $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
755     my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
756     my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
757     $my_cipher->set_iv($iv);
758     return $my_cipher;
762 sub encrypt_msg {
763     my ($msg, $key) = @_;
764     my $my_cipher = &create_ciphering($key);
765     my $len;
766     {
767             use bytes;
768             $len= 16-length($msg)%16;
769     }
770     $msg = "\0"x($len).$msg;
771     $msg = $my_cipher->encrypt($msg);
772     chomp($msg = &encode_base64($msg));
773     # there are no newlines allowed inside msg
774     $msg=~ s/\n//g;
775     return $msg;
779 sub decrypt_msg {
781     my ($msg, $key) = @_ ;
782     $msg = &decode_base64($msg);
783     my $my_cipher = &create_ciphering($key);
784     $msg = $my_cipher->decrypt($msg); 
785     $msg =~ s/\0*//g;
786     return $msg;
790 sub get_encrypt_key {
791     my ($target) = @_ ;
792     my $encrypt_key;
793     my $error = 0;
795     # target can be in known_server
796     if( not defined $encrypt_key ) {
797         my $sql_statement= "SELECT * FROM known_server WHERE hostname='$target'";
798         my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
799         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
800             my $host_name = $hit->{hostname};
801             if( $host_name ne $target ) {
802                 next;
803             }
804             $encrypt_key = $hit->{hostkey};
805             last;
806         }
807     }
809     # target can be in known_client
810     if( not defined $encrypt_key ) {
811         my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$target'";
812         my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
813         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
814             my $host_name = $hit->{hostname};
815             if( $host_name ne $target ) {
816                 next;
817             }
818             $encrypt_key = $hit->{hostkey};
819             last;
820         }
821     }
823     return $encrypt_key;
827 #===  FUNCTION  ================================================================
828 #         NAME:  open_socket
829 #   PARAMETERS:  PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
830 #                [PeerPort] string necessary if port not appended by PeerAddr
831 #      RETURNS:  socket IO::Socket::INET
832 #  DESCRIPTION:  open a socket to PeerAddr
833 #===============================================================================
834 sub open_socket {
835     my ($PeerAddr, $PeerPort) = @_ ;
836     if(defined($PeerPort)){
837         $PeerAddr = $PeerAddr.":".$PeerPort;
838     }
839     my $socket;
840     $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
841             Porto => "tcp",
842             Type => SOCK_STREAM,
843             Timeout => 5,
844             );
845     if(not defined $socket) {
846         return;
847     }
848 #    &daemon_log("DEBUG: open_socket: $PeerAddr", 7);
849     return $socket;
853 sub get_local_ip_for_remote_ip {
854         my $remote_ip= shift;
855         my $result="0.0.0.0";
857         if($remote_ip =~ /^(\d\d?\d?\.){3}\d\d?\d?$/) {
858                 if($remote_ip eq "127.0.0.1") {
859                         $result = "127.0.0.1";
860                 } else {
861                         my $PROC_NET_ROUTE= ('/proc/net/route');
863                         open(PROC_NET_ROUTE, "<$PROC_NET_ROUTE")
864                                 or die "Could not open $PROC_NET_ROUTE";
866                         my @ifs = <PROC_NET_ROUTE>;
868                         close(PROC_NET_ROUTE);
870                         # Eat header line
871                         shift @ifs;
872                         chomp @ifs;
873                         foreach my $line(@ifs) {
874                                 my ($Iface,$Destination,$Gateway,$Flags,$RefCnt,$Use,$Metric,$Mask,$MTU,$Window,$IRTT)=split(/\s/, $line);
875                                 my $destination;
876                                 my $mask;
877                                 my ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Destination);
878                                 $destination= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
879                                 ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Mask);
880                                 $mask= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
881                                 if(new NetAddr::IP($remote_ip)->within(new NetAddr::IP($destination, $mask))) {
882                                         # destination matches route, save mac and exit
883                                         $result= &get_ip($Iface);
884                                         last;
885                                 }
886                         }
887                 }
888         } else {
889                 daemon_log("get_local_ip_for_remote_ip was called with a non-ip parameter: $remote_ip", 1);
890         }
891         return $result;
895 sub send_msg_to_target {
896     my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
897     my $error = 0;
898     my $header;
899     my $timestamp = &get_time();
900     my $new_status;
901     my $act_status;
902     my ($sql_statement, $res);
903   
904     if( $msg_header ) {
905         $header = "'$msg_header'-";
906     } else {
907         $header = "";
908     }
910         # Patch the source ip
911         if($msg =~ /<source>0\.0\.0\.0:\d*?<\/source>/) {
912                 my $remote_ip = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
913                 $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$remote_ip:$2<\/source>/s;
914         }
916     # encrypt xml msg
917     my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
919     # opensocket
920     my $socket = &open_socket($address);
921     if( !$socket ) {
922         daemon_log("$session_id ERROR: cannot send ".$header."msg to $address , host not reachable", 1);
923         $error++;
924     }
925     
926     if( $error == 0 ) {
927         # send xml msg
928         print $socket $crypted_msg."\n";
930         daemon_log("$session_id INFO: send ".$header."msg to $address", 5);
931         daemon_log("$session_id DEBUG: message:\n$msg", 9);
932         
933     }
935     # close socket in any case
936     if( $socket ) {
937         close $socket;
938     }
940     if( $error > 0 ) { $new_status = "down"; }
941     else { $new_status = $msg_header; }
944     # known_clients
945     $sql_statement = "SELECT * FROM $known_clients_tn WHERE hostname='$address'";
946     $res = $known_clients_db->select_dbentry($sql_statement);
947     if( keys(%$res) == 1) {
948         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
949         if ($act_status eq "down" && $new_status eq "down") {
950             $sql_statement = "DELETE FROM known_clients WHERE hostname='$address'";
951             $res = $known_clients_db->del_dbentry($sql_statement);
952             daemon_log("$session_id WARNING: failed 2x to send msg to host '$address', delete host from known_clients", 3);
953         } else { 
954             $sql_statement = "UPDATE known_clients SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
955             $res = $known_clients_db->update_dbentry($sql_statement);
956             if($new_status eq "down"){
957                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
958             } else {
959                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
960             }
961         }
962     }
964     # known_server
965     $sql_statement = "SELECT * FROM $known_server_tn WHERE hostname='$address'";
966     $res = $known_server_db->select_dbentry($sql_statement);
967     if( keys(%$res) == 1) {
968         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
969         if ($act_status eq "down" && $new_status eq "down") {
970             $sql_statement = "DELETE FROM known_server WHERE hostname='$address'";
971             $res = $known_server_db->del_dbentry($sql_statement);
972             daemon_log("$session_id WARNING: failed 2x to send a message to host '$address', delete host from known_server", 3);
973         } 
974         else { 
975             $sql_statement = "UPDATE known_server SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
976             $res = $known_server_db->update_dbentry($sql_statement);
977             if($new_status eq "down"){
978                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
979             } else {
980                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
981             }
982         }
983     }
984     return $error; 
988 sub update_jobdb_status_for_send_msgs {
989     my ($answer, $error) = @_;
990     if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
991         my $jobdb_id = $1;
992             
993         # sending msg faild
994         if( $error ) {
995             if (not $answer =~ /<header>trigger_action_reinstall<\/header>/) {
996                 my $sql_statement = "UPDATE $job_queue_tn ".
997                     "SET status='error', result='can not deliver msg, please consult log file' ".
998                     "WHERE id=$jobdb_id";
999                 my $res = $job_db->update_dbentry($sql_statement);
1000             }
1002         # sending msg was successful
1003         } else {
1004             my $sql_statement = "UPDATE $job_queue_tn ".
1005                 "SET status='done' ".
1006                 "WHERE id=$jobdb_id AND status='processed'";
1007             my $res = $job_db->update_dbentry($sql_statement);
1008         }
1009     }
1013 sub sig_handler {
1014         my ($kernel, $signal) = @_[KERNEL, ARG0] ;
1015         daemon_log("0 INFO got signal '$signal'", 1); 
1016         $kernel->sig_handled();
1017         return;
1021 sub msg_to_decrypt {
1022     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
1023     my $session_id = $session->ID;
1024     my ($msg, $msg_hash, $module);
1025     my $error = 0;
1027     # hole neue msg aus @msgs_to_decrypt
1028     my $next_msg = shift @msgs_to_decrypt;
1029     
1030     # entschlüssle sie
1032     # msg is from a new client or gosa
1033     ($msg, $msg_hash, $module) = &input_from_unknown_host($next_msg, $session_id);
1034     # msg is from a gosa-si-server
1035     if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1036         ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $heap->{'remote_ip'}, $session_id);
1037     }
1038     # msg is from a gosa-si-client
1039     if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1040         ($msg, $msg_hash, $module) = &input_from_known_client($next_msg, $heap->{'remote_ip'}, $session_id);
1041     }
1042     # an error occurred
1043     if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1044         # if an incoming msg could not be decrypted (maybe a wrong key), send client a ping. If the client
1045         # could not understand a msg from its server the client cause a re-registering process
1046         daemon_log("$session_id WARNING cannot understand incoming msg, send 'ping'-msg to all host with ip '".$heap->{remote_ip}.
1047                 "' to cause a re-registering of the client if necessary", 3);
1048         my $sql_statement = "SELECT * FROM $main::known_clients_tn WHERE (hostname LIKE '".$heap->{'remote_ip'}."%')";
1049         my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
1050         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1051             my $host_name = $hit->{'hostname'};
1052             my $host_key = $hit->{'hostkey'};
1053             my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source> <target>$host_name</target></xml>";
1054             my $error = &send_msg_to_target($ping_msg, $host_name, $host_key, "gosa_ping", $session_id);
1055             &update_jobdb_status_for_send_msgs($ping_msg, $error);
1056         }
1057         $error++;
1058     }
1061     my $header;
1062     my $target;
1063     my $source;
1064     my $done = 0;
1065     my $sql;
1066     my $res;
1068     # check whether this message should be processed here
1069     if ($error == 0) {
1070         $header = @{$msg_hash->{'header'}}[0];
1071         $target = @{$msg_hash->{'target'}}[0];
1072         $source = @{$msg_hash->{'source'}}[0];
1073                 my $not_found_in_known_clients_db = 0;
1074                 my $not_found_in_known_server_db = 0;
1075                 my $not_found_in_foreign_clients_db = 0;
1076         my $local_address;
1077         my ($target_ip, $target_port) = split(':', $target);
1078                 if ($target =~ /^\d+\.\d+\.\d+\.\d+:\d+$/) {
1079                         $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1080                 } else {
1081             $local_address = $server_address;
1082         }
1084         # target and source is equal to GOSA -> process here
1085         if (not $done) {
1086             if ($target eq "GOSA" && $source eq "GOSA") {
1087                 $done = 1;                    
1088             }
1089         }
1091         # target is own address without forward_to_gosa-tag -> process here
1092         if (not $done) {
1093             if (($target eq $local_address) && (not exists $msg_hash->{'forward_to_gosa'})) {
1094                 $done = 1;
1095                 if ($source eq "GOSA") {
1096                     $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1097                 }
1098                 #print STDERR "target is own address without forward_to_gosa-tag -> process here\n";
1099             }
1100         }
1102         # target is a client address in known_clients -> process here
1103                 if (not $done) {
1104                                 $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')"; 
1105                                 $res = $known_clients_db->select_dbentry($sql);
1106                                 if (keys(%$res) > 0) {
1107                                                 $done = 1; 
1108                                                 my $hostname = $res->{1}->{'hostname'};
1109                                                 $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1110                                                 #print STDERR "target is a client address in known_clients -> process here\n";
1111                                 } else {
1112                                                 $not_found_in_known_clients_db = 1;
1113                                 }
1114                 }
1115         
1116         # target ist own address with forward_to_gosa-tag not pointing to myself -> process here
1117         if (not $done) {
1118             my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1119             my $gosa_at;
1120             my $gosa_session_id;
1121             if (($target eq $local_address) && (defined $forward_to_gosa)){
1122                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1123                 if ($gosa_at ne $local_address) {
1124                     $done = 1;
1125                     #print STDERR "target is own address with forward_to_gosa-tag not pointing to myself -> process here\n"; 
1126                 }
1127             }
1128         }
1130         # if message should be processed here -> add message to incoming_db
1131                 if ($done) {
1132                                 # if a job or a gosa message comes from a foreign server, fake module to GosaPackages
1133                                 # so gosa-si-server knows how to process this kind of messages
1134                                 if ($header =~ /^gosa_/ || $header =~ /^job_/) {
1135                                                 $module = "GosaPackages";
1136                                 }
1138                                 my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1139                                                                 primkey=>[],
1140                                                                 headertag=>$header,
1141                                                                 targettag=>$target,
1142                                                                 xmlmessage=>&encode_base64($msg),
1143                                                                 timestamp=>&get_time,
1144                                                                 module=>$module,
1145                                                                 sessionid=>$session_id,
1146                                                                 } );
1147                 }
1149         # target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa
1150         if (not $done) {
1151             my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1152             my $gosa_at;
1153             my $gosa_session_id;
1154             if (($target eq $local_address) && (defined $forward_to_gosa)){
1155                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1156                 if ($gosa_at eq $local_address) {
1157                     my $session_reference = $kernel->ID_id_to_session($gosa_session_id);
1158                     if( defined $session_reference ) {
1159                         $heap = $session_reference->get_heap();
1160                     }
1161                     if(exists $heap->{'client'}) {
1162                         $msg = &encrypt_msg($msg, $GosaPackages_key);
1163                         $heap->{'client'}->put($msg);
1164                     }
1165                     $done = 1;
1166                     #print STDERR "target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa\n";
1167                 }
1168             }
1170         }
1172         # target is a client address in foreign_clients -> forward to registration server
1173         if (not $done) {
1174             $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1175             $res = $foreign_clients_db->select_dbentry($sql);
1176             if (keys(%$res) > 0) {
1177                     my $hostname = $res->{1}->{'hostname'};
1178                     my ($host_ip, $host_port) = split(/:/, $hostname);
1179                     my $local_address =  &get_local_ip_for_remote_ip($host_ip).":$server_port";
1180                 my $regserver = $res->{1}->{'regserver'};
1181                 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$regserver'"; 
1182                 my $res = $known_server_db->select_dbentry($sql);
1183                 if (keys(%$res) > 0) {
1184                     my $regserver_key = $res->{1}->{'hostkey'};
1185                     $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1186                     $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1187                     if ($source eq "GOSA") {
1188                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1189                     }
1190                     &send_msg_to_target($msg, $regserver, $regserver_key, $header, $session_id);
1191                 }
1192                 $done = 1;
1193                 #print STDERR "target is a client address in foreign_clients -> forward to registration server\n";
1194             } else {
1195                                 $not_found_in_foreign_clients_db = 1;
1196                         }
1197         }
1199         # target is a server address -> forward to server
1200         if (not $done) {
1201             $sql = "SELECT * FROM $known_server_tn WHERE hostname='$target'";
1202             $res = $known_server_db->select_dbentry($sql);
1203             if (keys(%$res) > 0) {
1204                 my $hostkey = $res->{1}->{'hostkey'};
1206                 if ($source eq "GOSA") {
1207                     $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1208                     $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1210                 }
1212                 &send_msg_to_target($msg, $target, $hostkey, $header, $session_id);
1213                 $done = 1;
1214                 #print STDERR "target is a server address -> forward to server\n";
1215             } else {
1216                                 $not_found_in_known_server_db = 1;
1217                         }
1218         }
1220                 
1221                 # target is not in foreign_clients_db, known_server_db or known_clients_db, maybe it is a complete new one -> process here
1222                 if ( $not_found_in_foreign_clients_db 
1223                                                 && $not_found_in_known_server_db
1224                                                 && $not_found_in_known_clients_db) {
1225                                 my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1226                                                                 primkey=>[],
1227                                                                 headertag=>$header,
1228                                                                 targettag=>$target,
1229                                                                 xmlmessage=>&encode_base64($msg),
1230                                                                 timestamp=>&get_time,
1231                                                                 module=>$module,
1232                                                                 sessionid=>$session_id,
1233                                                                 } );
1234                                 $done = 1;
1235                 }
1238         if (not $done) {
1239             daemon_log("$session_id ERROR: do not know what to do with this message: $msg", 1);
1240             if ($source eq "GOSA") {
1241                 my %data = ('error_msg' => &encode_base64($msg), 'error_string' => "Do not know what to do with this message!");
1242                 my $error_msg = &build_msg("error", $local_address, "GOSA", \%data ); 
1244                 my $session_reference = $kernel->ID_id_to_session($session_id);
1245                 if( defined $session_reference ) {
1246                     $heap = $session_reference->get_heap();
1247                 }
1248                 if(exists $heap->{'client'}) {
1249                     $error_msg = &encrypt_msg($error_msg, $GosaPackages_key);
1250                     $heap->{'client'}->put($error_msg);
1251                 }
1252             }
1253         }
1255     }
1257     return;
1261 sub next_task {
1262     my ($session, $heap, $task) = @_[SESSION, HEAP, ARG0];
1263     my $running_task = POE::Wheel::Run->new(
1264             Program => sub { process_task($session, $heap, $task) },
1265             StdioFilter => POE::Filter::Reference->new(),
1266             StdoutEvent  => "task_result",
1267             StderrEvent  => "task_debug",
1268             CloseEvent   => "task_done",
1269             );
1270     $heap->{task}->{ $running_task->ID } = $running_task;
1273 sub handle_task_result {
1274     my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
1275     my $client_answer = $result->{'answer'};
1276     if( $client_answer =~ s/session_id=(\d+)$// ) {
1277         my $session_id = $1;
1278         if( defined $session_id ) {
1279             my $session_reference = $kernel->ID_id_to_session($session_id);
1280             if( defined $session_reference ) {
1281                 $heap = $session_reference->get_heap();
1282             }
1283         }
1285         if(exists $heap->{'client'}) {
1286             $heap->{'client'}->put($client_answer);
1287         }
1288     }
1289     $kernel->sig(CHLD => "child_reap");
1292 sub handle_task_debug {
1293     my $result = $_[ARG0];
1294     print STDERR "$result\n";
1297 sub handle_task_done {
1298     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1299     delete $heap->{task}->{$task_id};
1302 sub process_task {
1303     no strict "refs";
1304     #CHECK: Not @_[...]?
1305     my ($session, $heap, $task) = @_;
1306     my $error = 0;
1307     my $answer_l;
1308     my ($answer_header, @answer_target_l, $answer_source);
1309     my $client_answer = "";
1311     # prepare all variables needed to process message
1312     #my $msg = $task->{'xmlmessage'};
1313     my $msg = &decode_base64($task->{'xmlmessage'});
1314     my $incoming_id = $task->{'id'};
1315     my $module = $task->{'module'};
1316     my $header =  $task->{'headertag'};
1317     my $session_id = $task->{'sessionid'};
1318     my $msg_hash = $xml->XMLin($msg, ForceArray=>1);
1319     my $source = @{$msg_hash->{'source'}}[0];
1320     
1321     # set timestamp of incoming client uptodate, so client will not 
1322     # be deleted from known_clients because of expiration
1323     my $act_time = &get_time();
1324     my $sql = "UPDATE $known_clients_tn SET timestamp='$act_time' WHERE hostname='$source'"; 
1325     my $res = $known_clients_db->exec_statement($sql);
1327     ######################
1328     # process incoming msg
1329     if( $error == 0) {
1330         daemon_log("$session_id INFO: Incoming msg (session_id=$session_id) with header '".@{$msg_hash->{'header'}}[0]."'", 5); 
1331         daemon_log("$session_id DEBUG: Processing module ".$module, 7);
1332         $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1334         if ( 0 < @{$answer_l} ) {
1335             my $answer_str = join("\n", @{$answer_l});
1336             while ($answer_str =~ /<header>(\w+)<\/header>/g) {
1337                 daemon_log("$session_id INFO: got answer message with header '$1'", 5);
1338             }
1339             daemon_log("$session_id DEBUG: $module: got answer from module: \n".$answer_str,8);
1340         } else {
1341             daemon_log("$session_id DEBUG: $module: got no answer from module!" ,8);
1342         }
1344     }
1345     if( !$answer_l ) { $error++ };
1347     ########
1348     # answer
1349     if( $error == 0 ) {
1351         foreach my $answer ( @{$answer_l} ) {
1352             # check outgoing msg to xml validity
1353             my $answer_hash = &check_outgoing_xml_validity($answer, $session_id);
1354             if( not defined $answer_hash ) { next; }
1355             
1356             $answer_header = @{$answer_hash->{'header'}}[0];
1357             @answer_target_l = @{$answer_hash->{'target'}};
1358             $answer_source = @{$answer_hash->{'source'}}[0];
1360             # deliver msg to all targets 
1361             foreach my $answer_target ( @answer_target_l ) {
1363                 # targets of msg are all gosa-si-clients in known_clients_db
1364                 if( $answer_target eq "*" ) {
1365                     # answer is for all clients
1366                     my $sql_statement= "SELECT * FROM known_clients";
1367                     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
1368                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1369                         my $host_name = $hit->{hostname};
1370                         my $host_key = $hit->{hostkey};
1371                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1372                         &update_jobdb_status_for_send_msgs($answer, $error);
1373                     }
1374                 }
1376                 # targets of msg are all gosa-si-server in known_server_db
1377                 elsif( $answer_target eq "KNOWN_SERVER" ) {
1378                     # answer is for all server in known_server
1379                     my $sql_statement= "SELECT * FROM $known_server_tn";
1380                     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
1381                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1382                         my $host_name = $hit->{hostname};
1383                         my $host_key = $hit->{hostkey};
1384                         $answer =~ s/<target>\S+<\/target>/<target>$host_name<\/target>/g;
1385                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1386                         &update_jobdb_status_for_send_msgs($answer, $error);
1387                     }
1388                 }
1390                 # target of msg is GOsa
1391                                 elsif( $answer_target eq "GOSA" ) {
1392                                         my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1393                                         my $add_on = "";
1394                     if( defined $session_id ) {
1395                         $add_on = ".session_id=$session_id";
1396                     }
1397                     # answer is for GOSA and has to returned to connected client
1398                     my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1399                     $client_answer = $gosa_answer.$add_on;
1400                 }
1402                 # target of msg is job queue at this host
1403                 elsif( $answer_target eq "JOBDB") {
1404                     $answer =~ /<header>(\S+)<\/header>/;   
1405                     my $header;
1406                     if( defined $1 ) { $header = $1; }
1407                     my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1408                     &update_jobdb_status_for_send_msgs($answer, $error);
1409                 }
1411                 # target of msg is a mac address
1412                 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 ) {
1413                     daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients", 5);
1414                     my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1415                     my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1416                     my $found_ip_flag = 0;
1417                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1418                         my $host_name = $hit->{hostname};
1419                         my $host_key = $hit->{hostkey};
1420                         $answer =~ s/$answer_target/$host_name/g;
1421                         daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1422                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1423                         &update_jobdb_status_for_send_msgs($answer, $error);
1424                         $found_ip_flag++ ;
1425                     }   
1426                     if( $found_ip_flag == 0) {
1427                         daemon_log("$session_id WARNING: no host found in known_clients with mac address '$answer_target'", 3);
1428                     }
1430                 #  answer is for one specific host   
1431                 } else {
1432                     # get encrypt_key
1433                     my $encrypt_key = &get_encrypt_key($answer_target);
1434                     if( not defined $encrypt_key ) {
1435                         # unknown target
1436                         daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1437                         next;
1438                     }
1439                     my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1440                     &update_jobdb_status_for_send_msgs($answer, $error);
1441                 }
1442             }
1443         }
1444     }
1446     my $filter = POE::Filter::Reference->new();
1447     my %result = ( 
1448             status => "seems ok to me",
1449             answer => $client_answer,
1450             );
1452     my $output = $filter->put( [ \%result ] );
1453     print @$output;
1458 sub session_start {
1459     my ($kernel) = $_[KERNEL];
1460     $global_kernel = $kernel;
1461     $kernel->yield('register_at_foreign_servers');
1462         $kernel->yield('create_fai_server_db', $fai_server_tn );
1463         $kernel->yield('create_fai_release_db', $fai_release_tn );
1464     $kernel->yield('watch_for_next_tasks');
1465         $kernel->sig(USR1 => "sig_handler");
1466         $kernel->sig(USR2 => "recreate_packages_db");
1467         $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1468         $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay); 
1469     $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay); 
1470         $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1471     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1472         $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1473     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1479 sub watch_for_done_jobs {
1480     #CHECK: $heap for what?
1481     my ($kernel,$heap) = @_[KERNEL, HEAP];
1483     my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((status='done') AND (modified='0'))";
1484         my $res = $job_db->select_dbentry( $sql_statement );
1486     while( my ($id, $hit) = each %{$res} ) {
1487         my $jobdb_id = $hit->{id};
1488         my $sql_statement = "DELETE FROM $job_queue_tn WHERE id=$jobdb_id"; 
1489         my $res = $job_db->del_dbentry($sql_statement); 
1490     }
1492     $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1496 # if a job got an update or was modified anyway, send to all other si-server an update message
1497 # of this jobs
1498 sub watch_for_modified_jobs {
1499     my ($kernel,$heap) = @_[KERNEL, HEAP];
1501     my $sql_statement = "SELECT * FROM $job_queue_tn WHERE ((siserver='localhost') AND (modified='1'))"; 
1502     my $res = $job_db->select_dbentry( $sql_statement );
1503     
1504     # if db contains no jobs which should be update, do nothing
1505     if (keys %$res != 0) {
1507         if ($job_synchronization  eq "true") {
1508             # make out of the db result a gosa-si message   
1509             my $update_msg = &db_res2si_msg ($res, "foreign_job_updates", "KNOWN_SERVER", "MY_LOCAL_ADDRESS");
1510  
1511             # update all other SI-server
1512             &inform_all_other_si_server($update_msg);
1513         }
1515         # set jobs all jobs to modified = 0, wait until the next modification for updates of other si-server
1516         $sql_statement = "UPDATE $job_queue_tn SET modified='0' ";
1517         $res = $job_db->update_dbentry($sql_statement);
1518     }
1520     $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay);
1524 sub watch_for_new_jobs {
1525         if($watch_for_new_jobs_in_progress == 0) {
1526                 $watch_for_new_jobs_in_progress = 1;
1527                 my ($kernel,$heap) = @_[KERNEL, HEAP];
1529                 # check gosa job quaeue for jobs with executable timestamp
1530                 my $timestamp = &get_time();
1531                 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE status='waiting' AND (CAST (timestamp AS INTEGER)) < $timestamp ORDER BY timestamp";
1532                 my $res = $job_db->exec_statement( $sql_statement );
1534                 # Merge all new jobs that would do the same actions
1535                 my @drops;
1536                 my $hits;
1537                 foreach my $hit (reverse @{$res} ) {
1538                         my $macaddress= lc @{$hit}[8];
1539                         my $headertag= @{$hit}[5];
1540                         if(
1541                                 defined($hits->{$macaddress}) &&
1542                                 defined($hits->{$macaddress}->{$headertag}) &&
1543                                 defined($hits->{$macaddress}->{$headertag}[0])
1544                         ) {
1545                                 push @drops, "DELETE FROM $job_queue_tn WHERE id = $hits->{$macaddress}->{$headertag}[0]";
1546                         }
1547                         $hits->{$macaddress}->{$headertag}= $hit;
1548                 }
1550                 # Delete new jobs with a matching job in state 'processing'
1551                 foreach my $macaddress (keys %{$hits}) {
1552                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1553                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1554                                 if(defined($jobdb_id)) {
1555                                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND headertag='$jobdb_headertag' AND status='processing'";
1556                                         my $res = $job_db->exec_statement( $sql_statement );
1557                                         foreach my $hit (@{$res}) {
1558                                                 push @drops, "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1559                                         }
1560                                 } else {
1561                                         daemon_log("J ERROR: Job without id exists for macaddress $macaddress!", 1);
1562                                 }
1563                         }
1564                 }
1566                 # Commit deletion
1567                 $job_db->exec_statementlist(\@drops);
1569                 # Look for new jobs that could be executed
1570                 foreach my $macaddress (keys %{$hits}) {
1572                         # Look if there is an executing job
1573                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='processing'";
1574                         my $res = $job_db->exec_statement( $sql_statement );
1576                         # Skip new jobs for host if there is a processing job
1577                         if(defined($res) and defined @{$res}[0]) {
1578                                 next;
1579                         }
1581                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1582                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1583                                 if(defined($jobdb_id)) {
1584                                         my $job_msg = @{$hits->{$macaddress}->{$jobdb_headertag}}[7];
1586                                         daemon_log("J DEBUG: its time to execute $job_msg", 7);
1587                                         my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1588                                         my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1590                                         # expect macaddress is unique!!!!!!
1591                                         my $target = $res_hash->{1}->{hostname};
1593                                         # change header
1594                                         $job_msg =~ s/<header>job_/<header>gosa_/;
1596                                         # add sqlite_id
1597                                         $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1599                                         $job_msg =~ /<header>(\S+)<\/header>/;
1600                                         my $header = $1 ;
1601                                         my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");
1603                                         # update status in job queue to 'processing'
1604                                         $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
1605                                         my $res = $job_db->update_dbentry($sql_statement);
1606 # TODO: abfangen ob alles in ordnung ist oder nicht, wenn nicht error schmeißen                                        
1608                                         # We don't want parallel processing
1609                                         last;
1610                                 }
1611                         }
1612                 }
1614                 $watch_for_new_jobs_in_progress = 0;
1615                 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1616         }
1621 sub watch_for_new_messages {
1622     my ($kernel,$heap) = @_[KERNEL, HEAP];
1623     my @coll_user_msg;   # collection list of outgoing messages
1624     
1625     # check messaging_db for new incoming messages with executable timestamp
1626     my $timestamp = &get_time();
1627     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS INTEGER))<$timestamp AND flag='n' AND direction='in' )";
1628     my $res = $messaging_db->exec_statement( $sql_statement );
1629         foreach my $hit (@{$res}) {
1631         # create outgoing messages
1632         my $message_to = @{$hit}[3];
1633         # translate message_to to plain login name
1634         my @message_to_l = split(/,/, $message_to);  
1635                 my %receiver_h; 
1636                 foreach my $receiver (@message_to_l) {
1637                         if ($receiver =~ /^u_([\s\S]*)$/) {
1638                                 $receiver_h{$1} = 0;
1639                         } elsif ($receiver =~ /^g_([\s\S]*)$/) {
1640                                 my $group_name = $1;
1641                                 # fetch all group members from ldap and add them to receiver hash
1642                                 my $ldap_handle = &get_ldap_handle();
1643                                 if (defined $ldap_handle) {
1644                                                 my $mesg = $ldap_handle->search(
1645                                                                                 base => $ldap_base,
1646                                                                                 scope => 'sub',
1647                                                                                 attrs => ['memberUid'],
1648                                                                                 filter => "cn=$group_name",
1649                                                                                 );
1650                                                 if ($mesg->count) {
1651                                                                 my @entries = $mesg->entries;
1652                                                                 foreach my $entry (@entries) {
1653                                                                                 my @receivers= $entry->get_value("memberUid");
1654                                                                                 foreach my $receiver (@receivers) { 
1655                                                                                                 $receiver_h{$1} = 0;
1656                                                                                 }
1657                                                                 }
1658                                                 } 
1659                                                 # translating errors ?
1660                                                 if ($mesg->code) {
1661                                                                 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: $mesg->error", 1);
1662                                                 }
1663                                 # ldap handle error ?           
1664                                 } else {
1665                                         daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: no ldap handle available", 1);
1666                                 }
1667                         } else {
1668                                 my $sbjct = &encode_base64(@{$hit}[1]);
1669                                 my $msg = &encode_base64(@{$hit}[7]);
1670                                 &daemon_log("M WARNING: unknown receiver '$receiver' for a user-message '$sbjct - $msg'", 3); 
1671                         }
1672                 }
1673                 my @receiver_l = keys(%receiver_h);
1675         my $message_id = @{$hit}[0];
1677         #add each outgoing msg to messaging_db
1678         my $receiver;
1679         foreach $receiver (@receiver_l) {
1680             my $sql_statement = "INSERT INTO $messaging_tn (id, subject, message_from, message_to, flag, direction, delivery_time, message, timestamp) ".
1681                 "VALUES ('".
1682                 $message_id."', '".    # id
1683                 @{$hit}[1]."', '".     # subject
1684                 @{$hit}[2]."', '".     # message_from
1685                 $receiver."', '".      # message_to
1686                 "none"."', '".         # flag
1687                 "out"."', '".          # direction
1688                 @{$hit}[6]."', '".     # delivery_time
1689                 @{$hit}[7]."', '".     # message
1690                 $timestamp."'".     # timestamp
1691                 ")";
1692             &daemon_log("M DEBUG: $sql_statement", 1);
1693             my $res = $messaging_db->exec_statement($sql_statement);
1694             &daemon_log("M INFO: message '".@{$hit}[0]."' is prepared for delivery to receiver '$receiver'", 5);
1695         }
1697         # set incoming message to flag d=deliverd
1698         $sql_statement = "UPDATE $messaging_tn SET flag='p' WHERE id='$message_id'"; 
1699         &daemon_log("M DEBUG: $sql_statement", 7);
1700         $res = $messaging_db->update_dbentry($sql_statement);
1701         &daemon_log("M INFO: message '$message_id' is set to flag 'p' (processed)", 5);
1702     }
1704     $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay); 
1705     return;
1708 sub watch_for_delivery_messages {
1709     my ($kernel, $heap) = @_[KERNEL, HEAP];
1711     # select outgoing messages
1712     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( flag='p' AND direction='out' )";
1713     #&daemon_log("0 DEBUG: $sql", 7);
1714     my $res = $messaging_db->exec_statement( $sql_statement );
1715     
1716     # build out msg for each    usr
1717     foreach my $hit (@{$res}) {
1718         my $receiver = @{$hit}[3];
1719         my $msg_id = @{$hit}[0];
1720         my $subject = @{$hit}[1];
1721         my $message = @{$hit}[7];
1723         # resolve usr -> host where usr is logged in
1724         my $sql = "SELECT * FROM $login_users_tn WHERE (user='$receiver')"; 
1725         #&daemon_log("0 DEBUG: $sql", 7);
1726         my $res = $login_users_db->exec_statement($sql);
1728         # reciver is logged in nowhere
1729         if (not ref(@$res[0]) eq "ARRAY") { next; }    
1731                 my $send_succeed = 0;
1732                 foreach my $hit (@$res) {
1733                                 my $receiver_host = @$hit[0];
1734                                 &daemon_log("M DEBUG: user '$receiver' is logged in at host '$receiver_host'", 7);
1736                                 # fetch key to encrypt msg propperly for usr/host
1737                                 my $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$receiver_host')";
1738                                 &daemon_log("0 DEBUG: $sql", 7);
1739                                 my $res = $known_clients_db->exec_statement($sql);
1741                                 # host is already down
1742                                 if (not ref(@$res[0]) eq "ARRAY") { next; }
1744                                 # host is on
1745                                 my $receiver_key = @{@{$res}[0]}[2];
1746                                 my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
1747                                 my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
1748                                 my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0); 
1749                                 if ($error == 0 ) {
1750                                         $send_succeed++ ;
1751                                 }
1752                 }
1754                 if ($send_succeed) {
1755                                 # set outgoing msg at db to deliverd
1756                                 my $sql = "UPDATE $messaging_tn SET flag='d' WHERE (id='$msg_id' AND direction='out' AND message_to='$receiver')"; 
1757                                 &daemon_log("0 DEBUG: $sql", 7);
1758                                 my $res = $messaging_db->exec_statement($sql); 
1759                 }
1760         }
1762     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay); 
1763     return;
1767 sub watch_for_done_messages {
1768     my ($kernel,$heap) = @_[KERNEL, HEAP];
1770     my $sql = "SELECT * FROM $messaging_tn WHERE (flag='p' AND direction='in')"; 
1771     #&daemon_log("0 DEBUG: $sql", 7);
1772     my $res = $messaging_db->exec_statement($sql); 
1774     foreach my $hit (@{$res}) {
1775         my $msg_id = @{$hit}[0];
1777         my $sql = "SELECT * FROM $messaging_tn WHERE (id='$msg_id' AND direction='out' AND (NOT flag='s'))"; 
1778         #&daemon_log("0 DEBUG: $sql", 7); 
1779         my $res = $messaging_db->exec_statement($sql);
1781         # not all usr msgs have been seen till now
1782         if ( ref(@$res[0]) eq "ARRAY") { next; }
1783         
1784         $sql = "DELETE FROM $messaging_tn WHERE (id='$msg_id')"; 
1785         #&daemon_log("0 DEBUG: $sql", 7);
1786         $res = $messaging_db->exec_statement($sql);
1787     
1788     }
1790     $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay); 
1791     return;
1795 sub watch_for_old_known_clients {
1796     my ($kernel,$heap) = @_[KERNEL, HEAP];
1798     my $sql_statement = "SELECT * FROM $known_clients_tn";
1799     my $res = $known_clients_db->select_dbentry( $sql_statement );
1801     my $act_time = int(&get_time());
1803     while ( my ($hit_num, $hit) = each %$res) {
1804         my $expired_timestamp = int($hit->{'timestamp'});
1805         $expired_timestamp =~ /(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/;
1806         my $dt = DateTime->new( year   => $1,
1807                 month  => $2,
1808                 day    => $3,
1809                 hour   => $4,
1810                 minute => $5,
1811                 second => $6,
1812                 );
1814         $dt->add( seconds => 2 * int($hit->{'keylifetime'}) );
1815         $expired_timestamp = $dt->ymd('').$dt->hms('')."\n";
1816         if ($act_time > $expired_timestamp) {
1817             my $hostname = $hit->{'hostname'};
1818             my $del_sql = "DELETE FROM $known_clients_tn WHERE hostname='$hostname'"; 
1819             my $del_res = $known_clients_db->exec_statement($del_sql);
1821             &main::daemon_log("0 INFO: timestamp '".$hit->{'timestamp'}."' of client '$hostname' is expired('$expired_timestamp'), client will be deleted from known_clients_db", 5);
1822         }
1824     }
1826     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1830 sub watch_for_next_tasks {
1831     my ($kernel,$heap) = @_[KERNEL, HEAP];
1833     my $sql = "SELECT * FROM $incoming_tn";
1834     my $res = $incoming_db->select_dbentry($sql);
1836     while ( my ($hit_num, $hit) = each %$res) {
1837         my $headertag = $hit->{'headertag'};
1838         if ($headertag =~ /^answer_(\d+)/) {
1839             # do not start processing, this message is for a still running POE::Wheel
1840             next;
1841         }
1842         my $message_id = $hit->{'id'};
1843         $kernel->yield('next_task', $hit);
1845         my $sql = "DELETE FROM $incoming_tn WHERE id=$message_id";
1846         my $res = $incoming_db->exec_statement($sql);
1847     }
1849     $kernel->delay_set('watch_for_next_tasks', 0.1); 
1853 sub get_ldap_handle {
1854         my ($session_id) = @_;
1855         my $heap;
1856         my $ldap_handle;
1858         if (not defined $session_id ) { $session_id = 0 };
1859         if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
1861         if ($session_id == 0) {
1862                 daemon_log("$session_id DEBUG: get_ldap_handle invoked without a session_id, create a new ldap_handle", 7); 
1863                 $ldap_handle = Net::LDAP->new( $ldap_uri );
1864                 $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!"); 
1866         } else {
1867                 my $session_reference = $global_kernel->ID_id_to_session($session_id);
1868                 if( defined $session_reference ) {
1869                         $heap = $session_reference->get_heap();
1870                 }
1872                 if (not defined $heap) {
1873                         daemon_log("$session_id DEBUG: cannot get heap for session_id '$session_id'", 7); 
1874                         return;
1875                 }
1877                 # TODO: This "if" is nonsense, because it doesn't prove that the
1878                 #       used handle is still valid - or if we've to reconnect...
1879                 #if (not exists $heap->{ldap_handle}) {
1880                         $ldap_handle = Net::LDAP->new( $ldap_uri );
1881                         $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!"); 
1882                         $heap->{ldap_handle} = $ldap_handle;
1883                 #}
1884         }
1885         return $ldap_handle;
1889 sub change_fai_state {
1890     my ($st, $targets, $session_id) = @_;
1891     $session_id = 0 if not defined $session_id;
1892     # Set FAI state to localboot
1893     my %mapActions= (
1894         reboot    => '',
1895         update    => 'softupdate',
1896         localboot => 'localboot',
1897         reinstall => 'install',
1898         rescan    => '',
1899         wake      => '',
1900         memcheck  => 'memcheck',
1901         sysinfo   => 'sysinfo',
1902         install   => 'install',
1903     );
1905     # Return if this is unknown
1906     if (!exists $mapActions{ $st }){
1907         daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1); 
1908       return;
1909     }
1911     my $state= $mapActions{ $st };
1913     my $ldap_handle = &get_ldap_handle($session_id);
1914     if( defined($ldap_handle) ) {
1916       # Build search filter for hosts
1917         my $search= "(&(objectClass=GOhard)";
1918         foreach (@{$targets}){
1919             $search.= "(macAddress=$_)";
1920         }
1921         $search.= ")";
1923       # If there's any host inside of the search string, procress them
1924         if (!($search =~ /macAddress/)){
1925             daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);    
1926             return;
1927         }
1929       # Perform search for Unit Tag
1930       my $mesg = $ldap_handle->search(
1931           base   => $ldap_base,
1932           scope  => 'sub',
1933           attrs  => ['dn', 'FAIstate', 'objectClass'],
1934           filter => "$search"
1935           );
1937           if ($mesg->count) {
1938                   my @entries = $mesg->entries;
1939                   if (0 == @entries) {
1940                                   daemon_log("$session_id ERROR: ldap search failed: ldap_base=$ldap_base, filter=$search", 1); 
1941                   }
1943                   foreach my $entry (@entries) {
1944                           # Only modify entry if it is not set to '$state'
1945                           if ($entry->get_value("FAIstate") ne "$state"){
1946                                   daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
1947                                   my $result;
1948                                   my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
1949                                   if (exists $tmp{'FAIobject'}){
1950                                           if ($state eq ''){
1951                                                   $result= $ldap_handle->modify($entry->dn, changes => [
1952                                                           delete => [ FAIstate => [] ] ]);
1953                                           } else {
1954                                                   $result= $ldap_handle->modify($entry->dn, changes => [
1955                                                           replace => [ FAIstate => $state ] ]);
1956                                           }
1957                                   } elsif ($state ne ''){
1958                                           $result= $ldap_handle->modify($entry->dn, changes => [
1959                                                   add     => [ objectClass => 'FAIobject' ],
1960                                                   add     => [ FAIstate => $state ] ]);
1961                                   }
1963                                   # Errors?
1964                                   if ($result->code){
1965                                           daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
1966                                   }
1967                           } else {
1968                                   daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 7); 
1969                           }  
1970                   }
1971           } else {
1972                 daemon_log("$session_id ERROR: LDAP search failed: ldap_base=$ldap_base, filter=$search", 1);
1973           }
1975     # if no ldap handle defined
1976     } else {
1977         daemon_log("$session_id ERROR: no LDAP handle defined for update FAIstate", 1); 
1978     }
1980         return;
1984 sub change_goto_state {
1985     my ($st, $targets, $session_id) = @_;
1986     $session_id = 0  if not defined $session_id;
1988     # Switch on or off?
1989     my $state= $st eq 'active' ? 'active': 'locked';
1991     my $ldap_handle = &get_ldap_handle($session_id);
1992     if( defined($ldap_handle) ) {
1994       # Build search filter for hosts
1995       my $search= "(&(objectClass=GOhard)";
1996       foreach (@{$targets}){
1997         $search.= "(macAddress=$_)";
1998       }
1999       $search.= ")";
2001       # If there's any host inside of the search string, procress them
2002       if (!($search =~ /macAddress/)){
2003         return;
2004       }
2006       # Perform search for Unit Tag
2007       my $mesg = $ldap_handle->search(
2008           base   => $ldap_base,
2009           scope  => 'sub',
2010           attrs  => ['dn', 'gotoMode'],
2011           filter => "$search"
2012           );
2014       if ($mesg->count) {
2015         my @entries = $mesg->entries;
2016         foreach my $entry (@entries) {
2018           # Only modify entry if it is not set to '$state'
2019           if ($entry->get_value("gotoMode") ne $state){
2021             daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
2022             my $result;
2023             $result= $ldap_handle->modify($entry->dn, changes => [
2024                                                 replace => [ gotoMode => $state ] ]);
2026             # Errors?
2027             if ($result->code){
2028               &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2029             }
2031           }
2032         }
2033       } else {
2034                 daemon_log("$session_id ERROR: LDAP search failed in function change_goto_state: ldap_base=$ldap_base, filter=$search", 1);
2035           }
2037     }
2041 sub run_recreate_packages_db {
2042     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2043     my $session_id = $session->ID;
2044         &main::daemon_log("$session_id INFO: Recreating FAI Packages DB ('$fai_release_tn', '$fai_server_tn', '$packages_list_tn')", 5);
2045         $kernel->yield('create_fai_release_db', $fai_release_tn);
2046         $kernel->yield('create_fai_server_db', $fai_server_tn);
2047         return;
2051 sub run_create_fai_server_db {
2052     my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2053     my $session_id = $session->ID;
2054     my $task = POE::Wheel::Run->new(
2055             Program => sub { &create_fai_server_db($table_name,$kernel, undef, $session_id) },
2056             StdoutEvent  => "session_run_result",
2057             StderrEvent  => "session_run_debug",
2058             CloseEvent   => "session_run_done",
2059             );
2061     $heap->{task}->{ $task->ID } = $task;
2062     return;
2066 sub create_fai_server_db {
2067     my ($table_name, $kernel, $dont_create_packages_list, $session_id) = @_;
2068         my $result;
2070         if (not defined $session_id) { $session_id = 0; }
2071     my $ldap_handle = &get_ldap_handle();
2072         if(defined($ldap_handle)) {
2073                 daemon_log("$session_id INFO: create_fai_server_db: start", 5);
2074                 my $mesg= $ldap_handle->search(
2075                         base   => $ldap_base,
2076                         scope  => 'sub',
2077                         attrs  => ['FAIrepository', 'gosaUnitTag'],
2078                         filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
2079                 );
2080                 if($mesg->{'resultCode'} == 0 &&
2081                    $mesg->count != 0) {
2082                    foreach my $entry (@{$mesg->{entries}}) {
2083                            if($entry->exists('FAIrepository')) {
2084                                    # Add an entry for each Repository configured for server
2085                                    foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
2086                                                    my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
2087                                                    my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
2088                                                    $result= $fai_server_db->add_dbentry( { 
2089                                                                    table => $table_name,
2090                                                                    primkey => ['server', 'release', 'tag'],
2091                                                                    server => $tmp_url,
2092                                                                    release => $tmp_release,
2093                                                                    sections => $tmp_sections,
2094                                                                    tag => (length($tmp_tag)>0)?$tmp_tag:"",
2095                                                            } );
2096                                            }
2097                                    }
2098                            }
2099                    }
2100                 daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
2102                 # TODO: Find a way to post the 'create_packages_list_db' event
2103                 if(not defined($dont_create_packages_list)) {
2104                         &create_packages_list_db(undef, undef, $session_id);
2105                 }
2106         }       
2107     
2108     $ldap_handle->disconnect;
2109         return $result;
2113 sub run_create_fai_release_db {
2114     my ($session, $heap, $table_name) = @_[SESSION, HEAP, ARG0];
2115         my $session_id = $session->ID;
2116     my $task = POE::Wheel::Run->new(
2117             Program => sub { &create_fai_release_db($table_name, $session_id) },
2118             StdoutEvent  => "session_run_result",
2119             StderrEvent  => "session_run_debug",
2120             CloseEvent   => "session_run_done",
2121             );
2123     $heap->{task}->{ $task->ID } = $task;
2124     return;
2128 sub create_fai_release_db {
2129         my ($table_name, $session_id) = @_;
2130         my $result;
2132     # used for logging
2133     if (not defined $session_id) { $session_id = 0; }
2135     my $ldap_handle = &get_ldap_handle();
2136         if(defined($ldap_handle)) {
2137                 daemon_log("$session_id INFO: create_fai_release_db: start",5);
2138                 my $mesg= $ldap_handle->search(
2139                         base   => $ldap_base,
2140                         scope  => 'sub',
2141                         attrs  => [],
2142                         filter => "(&(objectClass=organizationalUnit)(ou=fai))",
2143                 );
2144                 if($mesg->{'resultCode'} == 0 &&
2145                         $mesg->count != 0) {
2146                         # Walk through all possible FAI container ou's
2147                         my @sql_list;
2148                         my $timestamp= &get_time();
2149                         foreach my $ou (@{$mesg->{entries}}) {
2150                                 my $tmp_classes= resolve_fai_classes($ou->dn, $ldap_handle, $session_id);
2151                                 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
2152                                         my @tmp_array=get_fai_release_entries($tmp_classes);
2153                                         if(@tmp_array) {
2154                                                 foreach my $entry (@tmp_array) {
2155                                                         if(defined($entry) && ref($entry) eq 'HASH') {
2156                                                                 my $sql= 
2157                                                                 "INSERT INTO $table_name "
2158                                                                 ."(timestamp, release, class, type, state) VALUES ("
2159                                                                 .$timestamp.","
2160                                                                 ."'".$entry->{'release'}."',"
2161                                                                 ."'".$entry->{'class'}."',"
2162                                                                 ."'".$entry->{'type'}."',"
2163                                                                 ."'".$entry->{'state'}."')";
2164                                                                 push @sql_list, $sql;
2165                                                         }
2166                                                 }
2167                                         }
2168                                 }
2169                         }
2171                         daemon_log("$session_id DEBUG: Inserting ".scalar @sql_list." entries to DB",8);
2172                         if(@sql_list) {
2173                                 unshift @sql_list, "VACUUM";
2174                                 unshift @sql_list, "DELETE FROM $table_name";
2175                                 $fai_release_db->exec_statementlist(\@sql_list);
2176                         }
2177                         daemon_log("$session_id DEBUG: Done with inserting",7);
2178                 }
2179                 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
2180         }
2181     $ldap_handle->disconnect;
2182         return $result;
2185 sub get_fai_types {
2186         my $tmp_classes = shift || return undef;
2187         my @result;
2189         foreach my $type(keys %{$tmp_classes}) {
2190                 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
2191                         my $entry = {
2192                                 type => $type,
2193                                 state => $tmp_classes->{$type}[0],
2194                         };
2195                         push @result, $entry;
2196                 }
2197         }
2199         return @result;
2202 sub get_fai_state {
2203         my $result = "";
2204         my $tmp_classes = shift || return $result;
2206         foreach my $type(keys %{$tmp_classes}) {
2207                 if(defined($tmp_classes->{$type}[0])) {
2208                         $result = $tmp_classes->{$type}[0];
2209                         
2210                 # State is equal for all types in class
2211                         last;
2212                 }
2213         }
2215         return $result;
2218 sub resolve_fai_classes {
2219         my ($fai_base, $ldap_handle, $session_id) = @_;
2220         if (not defined $session_id) { $session_id = 0; }
2221         my $result;
2222         my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
2223         my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
2224         my $fai_classes;
2226         daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base",7);
2227         my $mesg= $ldap_handle->search(
2228                 base   => $fai_base,
2229                 scope  => 'sub',
2230                 attrs  => ['cn','objectClass','FAIstate'],
2231                 filter => $fai_filter,
2232         );
2233         daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries",7);
2235         if($mesg->{'resultCode'} == 0 &&
2236                 $mesg->count != 0) {
2237                 foreach my $entry (@{$mesg->{entries}}) {
2238                         if($entry->exists('cn')) {
2239                                 my $tmp_dn= $entry->dn();
2241                                 # Skip classname and ou dn parts for class
2242                                 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?),$fai_base$/;
2244                                 # Skip classes without releases
2245                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2246                                         next;
2247                                 }
2249                                 my $tmp_cn= $entry->get_value('cn');
2250                                 my $tmp_state= $entry->get_value('FAIstate');
2252                                 my $tmp_type;
2253                                 # Get FAI type
2254                                 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
2255                                         if(grep $_ eq $oclass, @possible_fai_classes) {
2256                                                 $tmp_type= $oclass;
2257                                                 last;
2258                                         }
2259                                 }
2261                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2262                                         # A Subrelease
2263                                         my @sub_releases = split(/,/, $tmp_release);
2265                                         # Walk through subreleases and build hash tree
2266                                         my $hash;
2267                                         while(my $tmp_sub_release = pop @sub_releases) {
2268                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2269                                         }
2270                                         eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
2271                                 } else {
2272                                         # A branch, no subrelease
2273                                         push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
2274                                 }
2275                         } elsif (!$entry->exists('cn')) {
2276                                 my $tmp_dn= $entry->dn();
2277                                 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?),$fai_base$/;
2279                                 # Skip classes without releases
2280                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2281                                         next;
2282                                 }
2284                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2285                                         # A Subrelease
2286                                         my @sub_releases= split(/,/, $tmp_release);
2288                                         # Walk through subreleases and build hash tree
2289                                         my $hash;
2290                                         while(my $tmp_sub_release = pop @sub_releases) {
2291                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2292                                         }
2293                                         # Remove the last two characters
2294                                         chop($hash);
2295                                         chop($hash);
2297                                         eval('$fai_classes->'.$hash.'= {}');
2298                                 } else {
2299                                         # A branch, no subrelease
2300                                         if(!exists($fai_classes->{$tmp_release})) {
2301                                                 $fai_classes->{$tmp_release} = {};
2302                                         }
2303                                 }
2304                         }
2305                 }
2307                 # The hash is complete, now we can honor the copy-on-write based missing entries
2308                 foreach my $release (keys %$fai_classes) {
2309                         $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
2310                 }
2311         }
2312         return $result;
2315 sub apply_fai_inheritance {
2316        my $fai_classes = shift || return {};
2317        my $tmp_classes;
2319        # Get the classes from the branch
2320        foreach my $class (keys %{$fai_classes}) {
2321                # Skip subreleases
2322                if($class =~ /^ou=.*$/) {
2323                        next;
2324                } else {
2325                        $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
2326                }
2327        }
2329        # Apply to each subrelease
2330        foreach my $subrelease (keys %{$fai_classes}) {
2331                if($subrelease =~ /ou=/) {
2332                        foreach my $tmp_class (keys %{$tmp_classes}) {
2333                                if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
2334                                        $fai_classes->{$subrelease}->{$tmp_class} =
2335                                        deep_copy($tmp_classes->{$tmp_class});
2336                                } else {
2337                                        foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
2338                                                if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
2339                                                        $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
2340                                                        deep_copy($tmp_classes->{$tmp_class}->{$type});
2341                                                }
2342                                        }
2343                                }
2344                        }
2345                }
2346        }
2348        # Find subreleases in deeper levels
2349        foreach my $subrelease (keys %{$fai_classes}) {
2350                if($subrelease =~ /ou=/) {
2351                        foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
2352                                if($subsubrelease =~ /ou=/) {
2353                                        apply_fai_inheritance($fai_classes->{$subrelease});
2354                                }
2355                        }
2356                }
2357        }
2359        return $fai_classes;
2362 sub get_fai_release_entries {
2363         my $tmp_classes = shift || return;
2364         my $parent = shift || "";
2365         my @result = shift || ();
2367         foreach my $entry (keys %{$tmp_classes}) {
2368                 if(defined($entry)) {
2369                         if($entry =~ /^ou=.*$/) {
2370                                 my $release_name = $entry;
2371                                 $release_name =~ s/ou=//g;
2372                                 if(length($parent)>0) {
2373                                         $release_name = $parent."/".$release_name;
2374                                 }
2375                                 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2376                                 foreach my $bufentry(@bufentries) {
2377                                         push @result, $bufentry;
2378                                 }
2379                         } else {
2380                                 my @types = get_fai_types($tmp_classes->{$entry});
2381                                 foreach my $type (@types) {
2382                                         push @result, 
2383                                         {
2384                                                 'class' => $entry,
2385                                                 'type' => $type->{'type'},
2386                                                 'release' => $parent,
2387                                                 'state' => $type->{'state'},
2388                                         };
2389                                 }
2390                         }
2391                 }
2392         }
2394         return @result;
2397 sub deep_copy {
2398         my $this = shift;
2399         if (not ref $this) {
2400                 $this;
2401         } elsif (ref $this eq "ARRAY") {
2402                 [map deep_copy($_), @$this];
2403         } elsif (ref $this eq "HASH") {
2404                 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2405         } else { die "what type is $_?" }
2409 sub session_run_result {
2410     my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];    
2411     $kernel->sig(CHLD => "child_reap");
2414 sub session_run_debug {
2415     my $result = $_[ARG0];
2416     print STDERR "$result\n";
2419 sub session_run_done {
2420     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2421     delete $heap->{task}->{$task_id};
2425 sub create_sources_list {
2426         my $session_id = shift;
2427         my $ldap_handle = &main::get_ldap_handle;
2428         my $result="/tmp/gosa_si_tmp_sources_list";
2430         # Remove old file
2431         if(stat($result)) {
2432                 unlink($result);
2433                 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7); 
2434         }
2436         my $fh;
2437         open($fh, ">$result");
2438         if (not defined $fh) {
2439                 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7); 
2440                 return undef;
2441         }
2442         if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2443                 my $mesg=$ldap_handle->search(
2444                         base    => $main::ldap_server_dn,
2445                         scope   => 'base',
2446                         attrs   => 'FAIrepository',
2447                         filter  => 'objectClass=FAIrepositoryServer'
2448                 );
2449                 if($mesg->count) {
2450                         foreach my $entry(@{$mesg->{'entries'}}) {
2451                                 foreach my $value(@{$entry->get_value('FAIrepository', asref => 1)}) {
2452                                         my ($server, $tag, $release, $sections)= split /\|/, $value;
2453                                         my $line = "deb $server $release";
2454                                         $sections =~ s/,/ /g;
2455                                         $line.= " $sections";
2456                                         print $fh $line."\n";
2457                                 }
2458                         }
2459                 }
2460         } else {
2461                 if (defined $main::ldap_server_dn){
2462                         &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1); 
2463                 } else {
2464                         &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2465                 }
2466         }
2467         close($fh);
2469         return $result;
2473 sub run_create_packages_list_db {
2474     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2475         my $session_id = $session->ID;
2477         my $task = POE::Wheel::Run->new(
2478                                         Priority => +20,
2479                                         Program => sub {&create_packages_list_db(undef, undef, $session_id)},
2480                                         StdoutEvent  => "session_run_result",
2481                                         StderrEvent  => "session_run_debug",
2482                                         CloseEvent   => "session_run_done",
2483                                         );
2484         $heap->{task}->{ $task->ID } = $task;
2488 sub create_packages_list_db {
2489         my ($ldap_handle, $sources_file, $session_id) = @_;
2490         
2491         # it should not be possible to trigger a recreation of packages_list_db
2492         # while packages_list_db is under construction, so set flag packages_list_under_construction
2493         # which is tested befor recreation can be started
2494         if (-r $packages_list_under_construction) {
2495                 daemon_log("$session_id WARNING: packages_list_db is right now under construction, please wait until this process is finished", 3);
2496                 return;
2497         } else {
2498                 daemon_log("$session_id INFO: create_packages_list_db: start", 5); 
2499                 # set packages_list_under_construction to true
2500                 system("touch $packages_list_under_construction");
2501                 @packages_list_statements=();
2502         }
2504         if (not defined $session_id) { $session_id = 0; }
2505         if (not defined $ldap_handle) { 
2506                 $ldap_handle= &get_ldap_handle();
2508                 if (not defined $ldap_handle) {
2509                         daemon_log("$session_id ERROR: no ldap_handle available to create_packages_list_db", 1);
2510                         unlink($packages_list_under_construction);
2511                         return;
2512                 }
2513         }
2514         if (not defined $sources_file) { 
2515                 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5); 
2516                 $sources_file = &create_sources_list($session_id);
2517         }
2519         if (not defined $sources_file) {
2520                 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1); 
2521                 unlink($packages_list_under_construction);
2522                 return;
2523         }
2525         my $line;
2527         open(CONFIG, "<$sources_file") or do {
2528                 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2529                 unlink($packages_list_under_construction);
2530                 return;
2531         };
2533         # Read lines
2534         while ($line = <CONFIG>){
2535                 # Unify
2536                 chop($line);
2537                 $line =~ s/^\s+//;
2538                 $line =~ s/^\s+/ /;
2540                 # Strip comments
2541                 $line =~ s/#.*$//g;
2543                 # Skip empty lines
2544                 if ($line =~ /^\s*$/){
2545                         next;
2546                 }
2548                 # Interpret deb line
2549                 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2550                         my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2551                         my $section;
2552                         foreach $section (split(' ', $sections)){
2553                                 &parse_package_info( $baseurl, $dist, $section, $session_id );
2554                         }
2555                 }
2556         }
2558         close (CONFIG);
2560         find(\&cleanup_and_extract, keys( %repo_dirs ));
2561         &main::strip_packages_list_statements();
2562         unshift @packages_list_statements, "VACUUM";
2563         $packages_list_db->exec_statementlist(\@packages_list_statements);
2564         unlink($packages_list_under_construction);
2565         daemon_log("$session_id INFO: create_packages_list_db: finished", 5); 
2566         return;
2569 # This function should do some intensive task to minimize the db-traffic
2570 sub strip_packages_list_statements {
2571     my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2572         my @new_statement_list=();
2573         my $hash;
2574         my $insert_hash;
2575         my $update_hash;
2576         my $delete_hash;
2577         my $local_timestamp=get_time();
2579         foreach my $existing_entry (@existing_entries) {
2580                 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
2581         }
2583         foreach my $statement (@packages_list_statements) {
2584                 if($statement =~ /^INSERT/i) {
2585                         # Assign the values from the insert statement
2586                         my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~ 
2587                         /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
2588                         if(exists($hash->{$distribution}->{$package}->{$version})) {
2589                                 # If section or description has changed, update the DB
2590                                 if( 
2591                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or 
2592                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
2593                                 ) {
2594                                         @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
2595                                 }
2596                         } else {
2597                                 # Insert a non-existing entry to db
2598                                 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2599                         }
2600                 } elsif ($statement =~ /^UPDATE/i) {
2601                         my ($template,$package,$version) = ($1,$2,$3) if $statement =~
2602                         /^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;
2603                         foreach my $distribution (keys %{$hash}) {
2604                                 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
2605                                         # update the insertion hash to execute only one query per package (insert instead insert+update)
2606                                         @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
2607                                 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
2608                                         if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
2609                                                 my $section;
2610                                                 my $description;
2611                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
2612                                                         length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
2613                                                         $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
2614                                                 }
2615                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2616                                                         $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
2617                                                 }
2618                                                 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2619                                         }
2620                                 }
2621                         }
2622                 }
2623         }
2625         # TODO: Check for orphaned entries
2627         # unroll the insert_hash
2628         foreach my $distribution (keys %{$insert_hash}) {
2629                 foreach my $package (keys %{$insert_hash->{$distribution}}) {
2630                         foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
2631                                 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
2632                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
2633                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
2634                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
2635                                 ."'$local_timestamp')";
2636                         }
2637                 }
2638         }
2640         # unroll the update hash
2641         foreach my $distribution (keys %{$update_hash}) {
2642                 foreach my $package (keys %{$update_hash->{$distribution}}) {
2643                         foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
2644                                 my $set = "";
2645                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
2646                                         $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
2647                                 }
2648                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2649                                         $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
2650                                 }
2651                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
2652                                         $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
2653                                 }
2654                                 if(defined($set) and length($set) > 0) {
2655                                         $set .= "timestamp = '$local_timestamp'";
2656                                 } else {
2657                                         next;
2658                                 }
2659                                 push @new_statement_list, 
2660                                         "UPDATE $main::packages_list_tn SET $set WHERE"
2661                                         ." distribution = '$distribution'"
2662                                         ." AND package = '$package'"
2663                                         ." AND version = '$version'";
2664                         }
2665                 }
2666         }
2668         @packages_list_statements = @new_statement_list;
2672 sub parse_package_info {
2673     my ($baseurl, $dist, $section, $session_id)= @_;
2674     my ($package);
2675     if (not defined $session_id) { $session_id = 0; }
2676     my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
2677     $repo_dirs{ "${repo_path}/pool" } = 1;
2679     foreach $package ("Packages.gz"){
2680         daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
2681         get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
2682         parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
2683     }
2684     
2688 sub get_package {
2689     my ($url, $dest, $session_id)= @_;
2690     if (not defined $session_id) { $session_id = 0; }
2692     my $tpath = dirname($dest);
2693     -d "$tpath" || mkpath "$tpath";
2695     # This is ugly, but I've no time to take a look at "how it works in perl"
2696     if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
2697         system("gunzip -cd '$dest' > '$dest.in'");
2698         daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 5);
2699         unlink($dest);
2700         daemon_log("$session_id DEBUG: delete file '$dest'", 5); 
2701     } else {
2702         daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' failed!", 1);
2703     }
2704     return 0;
2708 sub parse_package {
2709     my ($path, $dist, $srv_path, $session_id)= @_;
2710     if (not defined $session_id) { $session_id = 0;}
2711     my ($package, $version, $section, $description);
2712     my $PACKAGES;
2713     my $timestamp = &get_time();
2715     if(not stat("$path.in")) {
2716         daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
2717         return;
2718     }
2720     open($PACKAGES, "<$path.in");
2721     if(not defined($PACKAGES)) {
2722         daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1); 
2723         return;
2724     }
2726     # Read lines
2727     while (<$PACKAGES>){
2728         my $line = $_;
2729         # Unify
2730         chop($line);
2732         # Use empty lines as a trigger
2733         if ($line =~ /^\s*$/){
2734             my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
2735             push(@packages_list_statements, $sql);
2736             $package = "none";
2737             $version = "none";
2738             $section = "none";
2739             $description = "none"; 
2740             next;
2741         }
2743         # Trigger for package name
2744         if ($line =~ /^Package:\s/){
2745             ($package)= ($line =~ /^Package: (.*)$/);
2746             next;
2747         }
2749         # Trigger for version
2750         if ($line =~ /^Version:\s/){
2751             ($version)= ($line =~ /^Version: (.*)$/);
2752             next;
2753         }
2755         # Trigger for description
2756         if ($line =~ /^Description:\s/){
2757             ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
2758             next;
2759         }
2761         # Trigger for section
2762         if ($line =~ /^Section:\s/){
2763             ($section)= ($line =~ /^Section: (.*)$/);
2764             next;
2765         }
2767         # Trigger for filename
2768         if ($line =~ /^Filename:\s/){
2769             my ($filename) = ($line =~ /^Filename: (.*)$/);
2770             store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
2771             next;
2772         }
2773     }
2775     close( $PACKAGES );
2776     unlink( "$path.in" );
2780 sub store_fileinfo {
2781     my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
2783     my %fileinfo = (
2784         'package' => $package,
2785         'dist' => $dist,
2786         'version' => $vers,
2787     );
2789     $repo_files{ "${srvdir}/$file" } = \%fileinfo;
2793 sub cleanup_and_extract {
2794     my $fileinfo = $repo_files{ $File::Find::name };
2796     if( defined $fileinfo ) {
2798         my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
2799         my $sql;
2800         my $package = $fileinfo->{ 'package' };
2801         my $newver = $fileinfo->{ 'version' };
2803         mkpath($dir);
2804         system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
2806                 if( -f "$dir/DEBIAN/templates" ) {
2808                         daemon_log("DEBUG: Found debconf templates in '$package' - $newver", 5);
2810                         my $tmpl= "";
2811                         {
2812                                 local $/=undef;
2813                                 open FILE, "$dir/DEBIAN/templates";
2814                                 $tmpl = &encode_base64(<FILE>);
2815                                 close FILE;
2816                         }
2817                         rmtree("$dir/DEBIAN/templates");
2819                         $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
2820                 push @packages_list_statements, $sql;
2821                 }
2822     }
2824     return;
2828 sub register_at_foreign_servers {   
2829     my ($kernel) = $_[KERNEL];
2831     # hole alle bekannten server aus known_server_db
2832     my $server_sql = "SELECT * FROM $known_server_tn";
2833     my $server_res = $known_server_db->exec_statement($server_sql);
2835     # no entries in known_server_db
2836     if (not ref(@$server_res[0]) eq "ARRAY") { 
2837         # TODO
2838     }
2840     # detect already connected clients
2841     my $client_sql = "SELECT * FROM $known_clients_tn"; 
2842     my $client_res = $known_clients_db->exec_statement($client_sql);
2844     # send my server details to all other gosa-si-server within the network
2845     foreach my $hit (@$server_res) {
2846         my $hostname = @$hit[0];
2847         my $hostkey = &create_passwd;
2849         # add already connected clients to registration message 
2850         my $myhash = &create_xml_hash('new_server', $server_address, $hostname);
2851         &add_content2xml_hash($myhash, 'key', $hostkey);
2852         map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res);
2853         
2854         # build registration message and send it
2855         my $foreign_server_msg = &create_xml_string($myhash);
2856         my $error = &send_msg_to_target($foreign_server_msg, $hostname, $ServerPackages_key, "new_server", 0); 
2857     }
2858     
2859     $kernel->delay_set("register_at_foreign_servers", $foreign_servers_register_delay); 
2860     return;
2864 #==== MAIN = main ==============================================================
2865 #  parse commandline options
2866 Getopt::Long::Configure( "bundling" );
2867 GetOptions("h|help" => \&usage,
2868         "c|config=s" => \$cfg_file,
2869         "f|foreground" => \$foreground,
2870         "v|verbose+" => \$verbose,
2871         "no-arp+" => \$no_arp,
2872            );
2874 #  read and set config parameters
2875 &check_cmdline_param ;
2876 &read_configfile;
2877 &check_pid;
2879 $SIG{CHLD} = 'IGNORE';
2881 # forward error messages to logfile
2882 if( ! $foreground ) {
2883   open( STDIN,  '+>/dev/null' );
2884   open( STDOUT, '+>&STDIN'    );
2885   open( STDERR, '+>&STDIN'    );
2888 # Just fork, if we are not in foreground mode
2889 if( ! $foreground ) { 
2890     chdir '/'                 or die "Can't chdir to /: $!";
2891     $pid = fork;
2892     setsid                    or die "Can't start a new session: $!";
2893     umask 0;
2894 } else { 
2895     $pid = $$; 
2898 # Do something useful - put our PID into the pid_file
2899 if( 0 != $pid ) {
2900     open( LOCK_FILE, ">$pid_file" );
2901     print LOCK_FILE "$pid\n";
2902     close( LOCK_FILE );
2903     if( !$foreground ) { 
2904         exit( 0 ) 
2905     };
2908 # parse head url and revision from svn
2909 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
2910 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
2911 $server_headURL = defined $1 ? $1 : 'unknown' ;
2912 $server_revision = defined $2 ? $2 : 'unknown' ;
2913 if ($server_headURL =~ /\/tag\// || 
2914         $server_headURL =~ /\/branches\// ) {
2915     $server_status = "stable"; 
2916 } else {
2917     $server_status = "developmental" ;
2921 daemon_log(" ", 1);
2922 daemon_log("$0 started!", 1);
2923 daemon_log("status: $server_status", 1);
2924 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1); 
2926 # connect to incoming_db
2927 unlink($incoming_file_name);
2928 $incoming_db = GOSA::DBsqlite->new($incoming_file_name);
2929 $incoming_db->create_table($incoming_tn, \@incoming_col_names);
2931 # connect to gosa-si job queue
2932 $job_db = GOSA::DBsqlite->new($job_queue_file_name);
2933 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
2935 # connect to known_clients_db
2936 $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
2937 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
2939 # connect to foreign_clients_db
2940 $foreign_clients_db = GOSA::DBsqlite->new($foreign_clients_file_name);
2941 $foreign_clients_db->create_table($foreign_clients_tn, \@foreign_clients_col_names);
2943 # connect to known_server_db
2944 unlink($known_server_file_name);
2945 $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
2946 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
2948 # connect to login_usr_db
2949 $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
2950 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
2952 # connect to fai_server_db and fai_release_db
2953 unlink($fai_server_file_name);
2954 $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
2955 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
2957 unlink($fai_release_file_name);
2958 $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
2959 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
2961 # connect to packages_list_db
2962 #unlink($packages_list_file_name);
2963 unlink($packages_list_under_construction);
2964 $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
2965 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
2967 # connect to messaging_db
2968 $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
2969 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
2972 # create xml object used for en/decrypting
2973 $xml = new XML::Simple();
2976 # foreign servers 
2977 my @foreign_server_list;
2979 # add foreign server from cfg file
2980 if ($foreign_server_string ne "") {
2981     my @cfg_foreign_server_list = split(",", $foreign_server_string);
2982     foreach my $foreign_server (@cfg_foreign_server_list) {
2983         push(@foreign_server_list, $foreign_server);
2984     }
2987 # add foreign server from dns
2988 my @tmp_servers;
2989 if ( !$server_domain) {
2990     # Try our DNS Searchlist
2991     for my $domain(get_dns_domains()) {
2992         chomp($domain);
2993         my @tmp_domains= &get_server_addresses($domain);
2994         if(@tmp_domains) {
2995             for my $tmp_server(@tmp_domains) {
2996                 push @tmp_servers, $tmp_server;
2997             }
2998         }
2999     }
3000     if(@tmp_servers && length(@tmp_servers)==0) {
3001         daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3002     }
3003 } else {
3004     @tmp_servers = &get_server_addresses($server_domain);
3005     if( 0 == @tmp_servers ) {
3006         daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3007     }
3009 foreach my $server (@tmp_servers) { 
3010     unshift(@foreign_server_list, $server); 
3012 # eliminate duplicate entries
3013 @foreign_server_list = &del_doubles(@foreign_server_list);
3014 my $all_foreign_server = join(", ", @foreign_server_list);
3015 daemon_log("0 INFO: found foreign server in config file and DNS: $all_foreign_server", 5);
3017 # add all found foreign servers to known_server
3018 my $act_timestamp = &get_time();
3019 foreach my $foreign_server (@foreign_server_list) {
3021         # do not add myself to known_server_db
3022         if (&is_local($foreign_server)) { next; }
3023         ######################################
3025     my $res = $known_server_db->add_dbentry( {table=>$known_server_tn, 
3026             primkey=>['hostname'],
3027             hostname=>$foreign_server,
3028             status=>'not_jet_registered',
3029             hostkey=>"none",
3030             timestamp=>$act_timestamp,
3031             } );
3035 # import all modules
3036 &import_modules;
3037 # check wether all modules are gosa-si valid passwd check
3038 &password_check;
3041 POE::Component::Server::TCP->new(
3042     Alias => "TCP_SERVER",
3043         Port => $server_port,
3044         ClientInput => sub {
3045         my ($kernel, $input) = @_[KERNEL, ARG0];
3046         push(@tasks, $input);
3047         push(@msgs_to_decrypt, $input);
3048         $kernel->yield("msg_to_decrypt");
3049         },
3050     InlineStates => {
3051         msg_to_decrypt => \&msg_to_decrypt,
3052         next_task => \&next_task,
3053         task_result => \&handle_task_result,
3054         task_done   => \&handle_task_done,
3055         task_debug  => \&handle_task_debug,
3056         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3057     }
3058 );
3060 daemon_log("start socket for incoming xml messages at port '$server_port' ", 1);
3062 # create session for repeatedly checking the job queue for jobs
3063 POE::Session->create(
3064         inline_states => {
3065                 _start => \&session_start,
3066         register_at_foreign_servers => \&register_at_foreign_servers,
3067         sig_handler => \&sig_handler,
3068         next_task => \&next_task,
3069         task_result => \&handle_task_result,
3070         task_done   => \&handle_task_done,
3071         task_debug  => \&handle_task_debug,
3072         watch_for_next_tasks => \&watch_for_next_tasks,
3073         watch_for_new_messages => \&watch_for_new_messages,
3074         watch_for_delivery_messages => \&watch_for_delivery_messages,
3075         watch_for_done_messages => \&watch_for_done_messages,
3076                 watch_for_new_jobs => \&watch_for_new_jobs,
3077         watch_for_modified_jobs => \&watch_for_modified_jobs,
3078         watch_for_done_jobs => \&watch_for_done_jobs,
3079         watch_for_old_known_clients => \&watch_for_old_known_clients,
3080         create_packages_list_db => \&run_create_packages_list_db,
3081         create_fai_server_db => \&run_create_fai_server_db,
3082         create_fai_release_db => \&run_create_fai_release_db,
3083                 recreate_packages_db => \&run_recreate_packages_db,
3084         session_run_result => \&session_run_result,
3085         session_run_debug => \&session_run_debug,
3086         session_run_done => \&session_run_done,
3087         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3088         }
3089 );
3092 POE::Kernel->run();
3093 exit;