Code

8a897ebee5c4dc0bca6d0188df1c97468201e70a
[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 use strict;
25 use warnings;
26 use Getopt::Long;
27 use Config::IniFiles;
28 use POSIX;
30 use Fcntl;
31 use IO::Socket::INET;
32 use IO::Handle;
33 use IO::Select;
34 use Symbol qw(qualify_to_ref);
35 use Crypt::Rijndael;
36 use MIME::Base64;
37 use Digest::MD5  qw(md5 md5_hex md5_base64);
38 use XML::Simple;
39 use Data::Dumper;
40 use Sys::Syslog qw( :DEFAULT setlogsock);
41 use Cwd;
42 use File::Spec;
43 use File::Basename;
44 use File::Find;
45 use File::Copy;
46 use File::Path;
47 use GOSA::DBsqlite;
48 use GOSA::GosaSupportDaemon;
49 use POE qw(Component::Server::TCP Wheel::Run Filter::Reference);
50 use Net::LDAP;
51 use Net::LDAP::Util qw(:escape);
53 my $modules_path = "/usr/lib/gosa-si/modules";
54 use lib "/usr/lib/gosa-si/modules";
55 my $server_version = "$HeadURL$:$Rev$";
57 # TODO es gibt eine globale funktion get_ldap_handle
58 # - ist in einer session dieses ldap handle schon vorhanden, wird es zurückgegeben
59 # - ist es nicht vorhanden, wird es erzeugt, im heap für spätere ldap anfragen gespeichert und zurückgegeben
60 # - sessions die kein ldap handle brauchen, sollen auch keins haben
61 # - wird eine session geschlossen, muss das ldap verbindung vorher beendet werden
62 our $global_kernel;
64 my (%cfg_defaults, $foreground, $verbose, $ping_timeout);
65 my ($bus_activ, $bus, $msg_to_bus, $bus_cipher);
66 my ($server);
67 my ($gosa_server, $job_queue_timeout, $job_queue_loop_delay);
68 my ($messaging_db_loop_delay);
69 my ($known_modules);
70 my ($pid_file, $procid, $pid, $log_file);
71 my ($arp_activ, $arp_fifo);
72 my ($xml);
73 my $sources_list;
74 my $max_clients;
75 my %repo_files=();
76 my $repo_path;
77 my %repo_dirs=();
78 # variables declared in config file are always set to 'our'
79 our (%cfg_defaults, $log_file, $pid_file, 
80     $server_ip, $server_port, $SIPackages_key, 
81     $arp_activ, $gosa_unit_tag,
82     $GosaPackages_key, $gosa_ip, $gosa_port, $gosa_timeout,
83 );
85 # additional variable which should be globaly accessable
86 our $server_address;
87 our $server_mac_address;
88 our $bus_address;
89 our $gosa_address;
90 our $no_bus;
91 our $no_arp;
92 our $verbose;
93 our $forground;
94 our $cfg_file;
95 #our ($ldap_handle, $ldap_uri, $ldap_base, $ldap_admin_dn, $ldap_admin_password, $ldap_server_dn);
96 our ($ldap_uri, $ldap_base, $ldap_admin_dn, $ldap_admin_password, $ldap_server_dn);
99 # specifies the verbosity of the daemon_log
100 $verbose = 0 ;
102 # if foreground is not null, script will be not forked to background
103 $foreground = 0 ;
105 # specifies the timeout seconds while checking the online status of a registrating client
106 $ping_timeout = 5;
108 $no_bus = 0;
109 $bus_activ = "true";
110 $no_arp = 0;
111 my $packages_list_under_construction = "/tmp/packages_list_creation_in_progress";
112 my @packages_list_statements;
113 my $watch_for_new_jobs_in_progress = 0;
115 our $prg= basename($0);
117 # holds all gosa jobs
118 our $job_db;
119 our $job_queue_tn = 'jobs';
120 my $job_queue_file_name;
121 my @job_queue_col_names = ("id INTEGER PRIMARY KEY", 
122                 "timestamp DEFAULT 'none'", 
123                 "status DEFAULT 'none'", 
124                 "result DEFAULT 'none'", 
125                 "progress DEFAULT 'none'", 
126         "headertag DEFAULT 'none'", 
127                 "targettag DEFAULT 'none'", 
128                 "xmlmessage DEFAULT 'none'", 
129                 "macaddress DEFAULT 'none'",
130                 "plainname DEFAULT 'none'",
131                 );
133 # holds all other gosa-sd as well as the gosa-sd-bus
134 our $known_server_db;
135 our $known_server_tn = "known_server";
136 my $known_server_file_name;
137 my @known_server_col_names = ("hostname", "status", "hostkey", "timestamp");
139 # holds all registrated clients
140 our $known_clients_db;
141 our $known_clients_tn = "known_clients";
142 my $known_clients_file_name;
143 my @known_clients_col_names = ("hostname", "status", "hostkey", "timestamp", "macaddress", "events");
145 # holds all logged in user at each client 
146 our $login_users_db;
147 our $login_users_tn = "login_users";
148 my $login_users_file_name;
149 my @login_users_col_names = ("client", "user", "timestamp");
151 # holds all fai server, the debian release and tag
152 our $fai_server_db;
153 our $fai_server_tn = "fai_server"; 
154 my $fai_server_file_name;
155 our @fai_server_col_names = ("timestamp", "server", "release", "sections", "tag"); 
157 our $fai_release_db;
158 our $fai_release_tn = "fai_release"; 
159 my $fai_release_file_name;
160 our @fai_release_col_names = ("timestamp", "release", "class", "type", "state"); 
162 # holds all packages available from different repositories
163 our $packages_list_db;
164 our $packages_list_tn = "packages_list";
165 my $packages_list_file_name;
166 our @packages_list_col_names = ("distribution", "package", "version", "section", "description", "template", "timestamp");
167 my $outdir = "/tmp/packages_list_db";
168 my $arch = "i386"; 
170 # holds all messages which should be delivered to a user
171 our $messaging_db;
172 our $messaging_tn = "messaging"; 
173 our @messaging_col_names = ("id INTEGER", "subject", "message_from", "message_to", 
174         "flag", "direction", "delivery_time", "message", "timestamp" );
175 my $messaging_file_name;
177 # path to directory to store client install log files
178 our $client_fai_log_dir = "/var/log/fai"; 
180 # queue which stores taskes until one of the $max_children children are ready to process the task
181 my @tasks = qw();
182 my $max_children = 2;
185 %cfg_defaults = (
186 "general" => {
187     "log-file" => [\$log_file, "/var/run/".$prg.".log"],
188     "pid-file" => [\$pid_file, "/var/run/".$prg.".pid"],
189     },
190 "bus" => {
191     "activ" => [\$bus_activ, "true"],
192     },
193 "server" => {
194     "port" => [\$server_port, "20081"],
195     "known-clients" => [\$known_clients_file_name, '/var/lib/gosa-si/clients.db' ],
196     "known-servers" => [\$known_server_file_name, '/var/lib/gosa-si/servers.db'],
197     "login-users" => [\$login_users_file_name, '/var/lib/gosa-si/users.db'],
198     "fai-server" => [\$fai_server_file_name, '/var/lib/gosa-si/fai_server.db'],
199     "fai-release" => [\$fai_release_file_name, '/var/lib/gosa-si/fai_release.db'],
200     "packages-list" => [\$packages_list_file_name, '/var/lib/gosa-si/packages.db'],
201     "messaging" => [\$messaging_file_name, '/var/lib/gosa-si/messaging.db'],
202     "source-list" => [\$sources_list, '/etc/apt/sources.list'],
203     "repo-path" => [\$repo_path, '/srv/www/repository'],
204     "ldap-uri" => [\$ldap_uri, ""],
205     "ldap-base" => [\$ldap_base, ""],
206     "ldap-admin-dn" => [\$ldap_admin_dn, ""],
207     "ldap-admin-password" => [\$ldap_admin_password, ""],
208     "gosa-unit-tag" => [\$gosa_unit_tag, ""],
209     "max-clients" => [\$max_clients, 10],
210     },
211 "GOsaPackages" => {
212     "ip" => [\$gosa_ip, "0.0.0.0"],
213     "port" => [\$gosa_port, "20082"],
214     "job-queue" => [\$job_queue_file_name, '/var/lib/gosa-si/jobs.db'],
215     "job-queue-loop-delay" => [\$job_queue_loop_delay, 3],
216     "messaging-db-loop-delay" => [\$messaging_db_loop_delay, 3],
217     "key" => [\$GosaPackages_key, "none"],
218     },
219 "SIPackages" => {
220     "key" => [\$SIPackages_key, "none"],
221     },
222 );
225 #===  FUNCTION  ================================================================
226 #         NAME:  usage
227 #   PARAMETERS:  nothing
228 #      RETURNS:  nothing
229 #  DESCRIPTION:  print out usage text to STDERR
230 #===============================================================================
231 sub usage {
232     print STDERR << "EOF" ;
233 usage: $prg [-hvf] [-c config]
235            -h        : this (help) message
236            -c <file> : config file
237            -f        : foreground, process will not be forked to background
238            -v        : be verbose (multiple to increase verbosity)
239            -no-bus   : starts $prg without connection to bus
240            -no-arp   : starts $prg without connection to arp module
241  
242 EOF
243     print "\n" ;
247 #===  FUNCTION  ================================================================
248 #         NAME:  read_configfile
249 #   PARAMETERS:  cfg_file - string -
250 #      RETURNS:  nothing
251 #  DESCRIPTION:  read cfg_file and set variables
252 #===============================================================================
253 sub read_configfile {
254     my $cfg;
255     if( defined( $cfg_file) && ( (-s $cfg_file) > 0 )) {
256         if( -r $cfg_file ) {
257             $cfg = Config::IniFiles->new( -file => $cfg_file );
258         } else {
259             print STDERR "Couldn't read config file!\n";
260         }
261     } else {
262         $cfg = Config::IniFiles->new() ;
263     }
264     foreach my $section (keys %cfg_defaults) {
265         foreach my $param (keys %{$cfg_defaults{ $section }}) {
266             my $pinfo = $cfg_defaults{ $section }{ $param };
267             ${@$pinfo[ 0 ]} = $cfg->val( $section, $param, @$pinfo[ 1 ] );
268         }
269     }
273 #===  FUNCTION  ================================================================
274 #         NAME:  logging
275 #   PARAMETERS:  level - string - default 'info'
276 #                msg - string -
277 #                facility - string - default 'LOG_DAEMON'
278 #      RETURNS:  nothing
279 #  DESCRIPTION:  function for logging
280 #===============================================================================
281 sub daemon_log {
282     # log into log_file
283     my( $msg, $level ) = @_;
284     if(not defined $msg) { return }
285     if(not defined $level) { $level = 1 }
286     if(defined $log_file){
287         open(LOG_HANDLE, ">>$log_file");
288         if(not defined open( LOG_HANDLE, ">>$log_file" )) {
289             print STDERR "cannot open $log_file: $!";
290             return }
291             chomp($msg);
292                         $msg =~s/\n//g;   # no newlines are allowed in log messages, this is important for later log parsing
293             if($level <= $verbose){
294                 my ($seconds, $minutes, $hours, $monthday, $month,
295                         $year, $weekday, $yearday, $sommertime) = localtime(time);
296                 $hours = $hours < 10 ? $hours = "0".$hours : $hours;
297                 $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
298                 $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
299                 my @monthnames = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
300                 $month = $monthnames[$month];
301                 $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
302                 $year+=1900;
303                 my $name = $prg;
305                 my $log_msg = "$month $monthday $hours:$minutes:$seconds $name $msg\n";
306                 print LOG_HANDLE $log_msg;
307                 if( $foreground ) { 
308                     print STDERR $log_msg;
309                 }
310             }
311         close( LOG_HANDLE );
312     }
316 #===  FUNCTION  ================================================================
317 #         NAME:  check_cmdline_param
318 #   PARAMETERS:  nothing
319 #      RETURNS:  nothing
320 #  DESCRIPTION:  validates commandline parameter
321 #===============================================================================
322 sub check_cmdline_param () {
323     my $err_config;
324     my $err_counter = 0;
325         if(not defined($cfg_file)) {
326                 $cfg_file = "/etc/gosa-si/server.conf";
327                 if(! -r $cfg_file) {
328                         $err_config = "please specify a config file";
329                         $err_counter += 1;
330                 }
331     }
332     if( $err_counter > 0 ) {
333         &usage( "", 1 );
334         if( defined( $err_config)) { print STDERR "$err_config\n"}
335         print STDERR "\n";
336         exit( -1 );
337     }
341 #===  FUNCTION  ================================================================
342 #         NAME:  check_pid
343 #   PARAMETERS:  nothing
344 #      RETURNS:  nothing
345 #  DESCRIPTION:  handels pid processing
346 #===============================================================================
347 sub check_pid {
348     $pid = -1;
349     # Check, if we are already running
350     if( open(LOCK_FILE, "<$pid_file") ) {
351         $pid = <LOCK_FILE>;
352         if( defined $pid ) {
353             chomp( $pid );
354             if( -f "/proc/$pid/stat" ) {
355                 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
356                 if( $stat ) {
357                                         daemon_log("ERROR: Already running",1);
358                     close( LOCK_FILE );
359                     exit -1;
360                 }
361             }
362         }
363         close( LOCK_FILE );
364         unlink( $pid_file );
365     }
367     # create a syslog msg if it is not to possible to open PID file
368     if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
369         my($msg) = "Couldn't obtain lockfile '$pid_file' ";
370         if (open(LOCK_FILE, '<', $pid_file)
371                 && ($pid = <LOCK_FILE>))
372         {
373             chomp($pid);
374             $msg .= "(PID $pid)\n";
375         } else {
376             $msg .= "(unable to read PID)\n";
377         }
378         if( ! ($foreground) ) {
379             openlog( $0, "cons,pid", "daemon" );
380             syslog( "warning", $msg );
381             closelog();
382         }
383         else {
384             print( STDERR " $msg " );
385         }
386         exit( -1 );
387     }
390 #===  FUNCTION  ================================================================
391 #         NAME:  import_modules
392 #   PARAMETERS:  module_path - string - abs. path to the directory the modules 
393 #                are stored
394 #      RETURNS:  nothing
395 #  DESCRIPTION:  each file in module_path which ends with '.pm' and activation 
396 #                state is on is imported by "require 'file';"
397 #===============================================================================
398 sub import_modules {
399     daemon_log(" ", 1);
401     if (not -e $modules_path) {
402         daemon_log("ERROR: cannot find directory or directory is not readable: $modules_path", 1);   
403     }
405     opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
406     while (defined (my $file = readdir (DIR))) {
407         if (not $file =~ /(\S*?).pm$/) {
408             next;
409         }
410                 my $mod_name = $1;
412         if( $file =~ /ArpHandler.pm/ ) {
413             if( $no_arp > 0 ) {
414                 next;
415             }
416         }
417         
418         eval { require $file; };
419         if ($@) {
420             daemon_log("ERROR: gosa-si-server could not load module $file", 1);
421             daemon_log("$@", 5);
422                 } else {
423                         my $info = eval($mod_name.'::get_module_info()');
424                         # Only load module if get_module_info() returns a non-null object
425                         if( $info ) {
426                                 my ($input_address, $input_key, $input, $input_active, $input_type) = @{$info};
427                                 $known_modules->{$mod_name} = $info;
428                                 daemon_log("INFO: module $mod_name loaded", 5);
429                         }
430                 }
431     }   
432     close (DIR);
436 #===  FUNCTION  ================================================================
437 #         NAME:  sig_int_handler
438 #   PARAMETERS:  signal - string - signal arose from system
439 #      RETURNS:  noting
440 #  DESCRIPTION:  handels tasks to be done befor signal becomes active
441 #===============================================================================
442 sub sig_int_handler {
443     my ($signal) = @_;
445 #       if (defined($ldap_handle)) {
446 #               $ldap_handle->disconnect;
447 #       }
448     # TODO alle verbliebenden ldap verbindungen aus allen heaps beenden
449     
451     daemon_log("shutting down gosa-si-server", 1);
452     system("kill `ps -C gosa-si-server -o pid=`");
454 $SIG{INT} = \&sig_int_handler;
457 sub check_key_and_xml_validity {
458     my ($crypted_msg, $module_key, $session_id) = @_;
459     my $msg;
460     my $msg_hash;
461     my $error_string;
462     eval{
463         $msg = &decrypt_msg($crypted_msg, $module_key);
465         if ($msg =~ /<xml>/i){
466             $msg =~ s/\s+/ /g;  # just for better daemon_log
467             daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 8);
468             $msg_hash = $xml->XMLin($msg, ForceArray=>1);
470             ##############
471             # check header
472             if( not exists $msg_hash->{'header'} ) { die "no header specified"; }
473             my $header_l = $msg_hash->{'header'};
474             if( 1 > @{$header_l} ) { die 'empty header tag'; }
475             if( 1 < @{$header_l} ) { die 'more than one header specified'; }
476             my $header = @{$header_l}[0];
477             if( 0 == length $header) { die 'empty string in header tag'; }
479             ##############
480             # check source
481             if( not exists $msg_hash->{'source'} ) { die "no source specified"; }
482             my $source_l = $msg_hash->{'source'};
483             if( 1 > @{$source_l} ) { die 'empty source tag'; }
484             if( 1 < @{$source_l} ) { die 'more than one source specified'; }
485             my $source = @{$source_l}[0];
486             if( 0 == length $source) { die 'source error'; }
488             ##############
489             # check target
490             if( not exists $msg_hash->{'target'} ) { die "no target specified"; }
491             my $target_l = $msg_hash->{'target'};
492             if( 1 > @{$target_l} ) { die 'empty target tag'; }
493         }
494     };
495     if($@) {
496         daemon_log("$session_id DEBUG: do not understand the message: $@", 7);
497         $msg = undef;
498         $msg_hash = undef;
499     }
501     return ($msg, $msg_hash);
505 sub check_outgoing_xml_validity {
506     my ($msg) = @_;
508     my $msg_hash;
509     eval{
510         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
512         ##############
513         # check header
514         my $header_l = $msg_hash->{'header'};
515         if( 1 != @{$header_l} ) {
516             die 'no or more than one headers specified';
517         }
518         my $header = @{$header_l}[0];
519         if( 0 == length $header) {
520             die 'header has length 0';
521         }
523         ##############
524         # check source
525         my $source_l = $msg_hash->{'source'};
526         if( 1 != @{$source_l} ) {
527             die 'no or more than 1 sources specified';
528         }
529         my $source = @{$source_l}[0];
530         if( 0 == length $source) {
531             die 'source has length 0';
532         }
533         unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
534                 $source =~ /^GOSA$/i ) {
535             die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
536         }
537         
538         ##############
539         # check target  
540         my $target_l = $msg_hash->{'target'};
541         if( 0 == @{$target_l} ) {
542             die "no targets specified";
543         }
544         foreach my $target (@$target_l) {
545             if( 0 == length $target) {
546                 die "target has length 0";
547             }
548             unless( $target =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
549                     $target =~ /^GOSA$/i ||
550                     $target =~ /^\*$/ ||
551                     $target =~ /KNOWN_SERVER/i ||
552                     $target =~ /JOBDB/i ||
553                     $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 ){
554                 die "target '$target' is not a complete ip-address with port or a valid target name or a mac-address";
555             }
556         }
557     };
558     if($@) {
559         daemon_log("WARNING: outgoing msg is not gosa-si envelope conform", 5);
560         daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 8);
561         $msg_hash = undef;
562     }
564     return ($msg_hash);
568 sub input_from_known_server {
569     my ($input, $remote_ip, $session_id) = @_ ;  
570     my ($msg, $msg_hash, $module);
572     my $sql_statement= "SELECT * FROM known_server";
573     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
575     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
576         my $host_name = $hit->{hostname};
577         if( not $host_name =~ "^$remote_ip") {
578             next;
579         }
580         my $host_key = $hit->{hostkey};
581         daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 7);
582         daemon_log("DEBUG: input_from_known_server: host_key: $host_key", 7);
584         # check if module can open msg envelope with module key
585         my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
586         if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
587             daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 7);
588             daemon_log("$@", 8);
589             next;
590         }
591         else {
592             $msg = $tmp_msg;
593             $msg_hash = $tmp_msg_hash;
594             $module = "SIPackages";
595             last;
596         }
597     }
599     if( (!$msg) || (!$msg_hash) || (!$module) ) {
600         daemon_log("$session_id DEBUG: Incoming message is not from a known server", 7);
601     }
602   
603     return ($msg, $msg_hash, $module);
607 sub input_from_known_client {
608     my ($input, $remote_ip, $session_id) = @_ ;  
609     my ($msg, $msg_hash, $module);
611     my $sql_statement= "SELECT * FROM known_clients";
612     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
613     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
614         my $host_name = $hit->{hostname};
615         if( not $host_name =~ /^$remote_ip:\d*$/) {
616                 next;
617                 }
618         my $host_key = $hit->{hostkey};
619         &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 7);
620         &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 7);
622         # check if module can open msg envelope with module key
623         ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
625         if( (!$msg) || (!$msg_hash) ) {
626             &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 7);
627             &daemon_log("$@", 8);
628             next;
629         }
630         else {
631             $module = "SIPackages";
632             last;
633         }
634     }
636     if( (!$msg) || (!$msg_hash) || (!$module) ) {
637         &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 7);
638     }
640     return ($msg, $msg_hash, $module);
644 sub input_from_unknown_host {
645     no strict "refs";
646     my ($input, $session_id) = @_ ;
647     my ($msg, $msg_hash, $module);
648     my $error_string;
649     
650         my %act_modules = %$known_modules;
652         while( my ($mod, $info) = each(%act_modules)) {
654         # check a key exists for this module
655         my $module_key = ${$mod."_key"};
656         if( not defined $module_key ) {
657             if( $mod eq 'ArpHandler' ) {
658                 next;
659             }
660             daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
661             next;
662         }
663         daemon_log("$session_id DEBUG: $mod: $module_key", 7);
665         # check if module can open msg envelope with module key
666         ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
667         if( (not defined $msg) || (not defined $msg_hash) ) {
668             next;
669         }
670         else {
671             $module = $mod;
672             last;
673         }
674     }
676     if( (!$msg) || (!$msg_hash) || (!$module)) {
677         daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 7);
678     }
680     return ($msg, $msg_hash, $module);
684 sub create_ciphering {
685     my ($passwd) = @_;
686         if((!defined($passwd)) || length($passwd)==0) {
687                 $passwd = "";
688         }
689     $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
690     my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
691     my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
692     $my_cipher->set_iv($iv);
693     return $my_cipher;
697 sub encrypt_msg {
698     my ($msg, $key) = @_;
699     my $my_cipher = &create_ciphering($key);
700     my $len;
701     {
702             use bytes;
703             $len= 16-length($msg)%16;
704     }
705     $msg = "\0"x($len).$msg;
706     $msg = $my_cipher->encrypt($msg);
707     chomp($msg = &encode_base64($msg));
708     # there are no newlines allowed inside msg
709     $msg=~ s/\n//g;
710     return $msg;
714 sub decrypt_msg {
716     my ($msg, $key) = @_ ;
717     $msg = &decode_base64($msg);
718     my $my_cipher = &create_ciphering($key);
719     $msg = $my_cipher->decrypt($msg); 
720     $msg =~ s/\0*//g;
721     return $msg;
725 sub get_encrypt_key {
726     my ($target) = @_ ;
727     my $encrypt_key;
728     my $error = 0;
730     # target can be in known_server
731     if( not defined $encrypt_key ) {
732         my $sql_statement= "SELECT * FROM known_server WHERE hostname='$target'";
733         my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
734         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
735             my $host_name = $hit->{hostname};
736             if( $host_name ne $target ) {
737                 next;
738             }
739             $encrypt_key = $hit->{hostkey};
740             last;
741         }
742     }
744     # target can be in known_client
745     if( not defined $encrypt_key ) {
746         my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$target'";
747         my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
748         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
749             my $host_name = $hit->{hostname};
750             if( $host_name ne $target ) {
751                 next;
752             }
753             $encrypt_key = $hit->{hostkey};
754             last;
755         }
756     }
758     return $encrypt_key;
762 #===  FUNCTION  ================================================================
763 #         NAME:  open_socket
764 #   PARAMETERS:  PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
765 #                [PeerPort] string necessary if port not appended by PeerAddr
766 #      RETURNS:  socket IO::Socket::INET
767 #  DESCRIPTION:  open a socket to PeerAddr
768 #===============================================================================
769 sub open_socket {
770     my ($PeerAddr, $PeerPort) = @_ ;
771     if(defined($PeerPort)){
772         $PeerAddr = $PeerAddr.":".$PeerPort;
773     }
774     my $socket;
775     $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
776             Porto => "tcp",
777             Type => SOCK_STREAM,
778             Timeout => 5,
779             );
780     if(not defined $socket) {
781         return;
782     }
783 #    &daemon_log("DEBUG: open_socket: $PeerAddr", 7);
784     return $socket;
788 #===  FUNCTION  ================================================================
789 #         NAME:  get_ip 
790 #   PARAMETERS:  interface name (i.e. eth0)
791 #      RETURNS:  (ip address) 
792 #  DESCRIPTION:  Uses ioctl to get ip address directly from system.
793 #===============================================================================
794 sub get_ip {
795         my $ifreq= shift;
796         my $result= "";
797         my $SIOCGIFADDR= 0x8915;       # man 2 ioctl_list
798         my $proto= getprotobyname('ip');
800         socket SOCKET, PF_INET, SOCK_DGRAM, $proto
801                 or die "socket: $!";
803         if(ioctl SOCKET, $SIOCGIFADDR, $ifreq) {
804                 my ($if, $sin)    = unpack 'a16 a16', $ifreq;
805                 my ($port, $addr) = sockaddr_in $sin;
806                 my $ip            = inet_ntoa $addr;
808                 if ($ip && length($ip) > 0) {
809                         $result = $ip;
810                 }
811         }
813         return $result;
817 sub get_local_ip_for_remote_ip {
818         my $remote_ip= shift;
819         my $result="0.0.0.0";
821         if($remote_ip =~ /^(\d\d?\d?\.){3}\d\d?\d?$/) {
822                 if($remote_ip eq "127.0.0.1") {
823                         $result = "127.0.0.1";
824                 } else {
825                         my $PROC_NET_ROUTE= ('/proc/net/route');
827                         open(PROC_NET_ROUTE, "<$PROC_NET_ROUTE")
828                                 or die "Could not open $PROC_NET_ROUTE";
830                         my @ifs = <PROC_NET_ROUTE>;
832                         close(PROC_NET_ROUTE);
834                         # Eat header line
835                         shift @ifs;
836                         chomp @ifs;
837                         foreach my $line(@ifs) {
838                                 my ($Iface,$Destination,$Gateway,$Flags,$RefCnt,$Use,$Metric,$Mask,$MTU,$Window,$IRTT)=split(/\s/, $line);
839                                 my $destination;
840                                 my $mask;
841                                 my ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Destination);
842                                 $destination= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
843                                 ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Mask);
844                                 $mask= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
845                                 if(new NetAddr::IP($remote_ip)->within(new NetAddr::IP($destination, $mask))) {
846                                         # destination matches route, save mac and exit
847                                         $result= &get_ip($Iface);
848                                         last;
849                                 }
850                         }
851                 }
852         } else {
853                 daemon_log("get_local_ip_for_remote_ip was called with a non-ip parameter: $remote_ip", 1);
854         }
855         return $result;
859 sub send_msg_to_target {
860     my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
861     my $error = 0;
862     my $header;
863     my $new_status;
864     my $act_status;
865     my ($sql_statement, $res);
866   
867     if( $msg_header ) {
868         $header = "'$msg_header'-";
869     } else {
870         $header = "";
871     }
873         # Patch the source ip
874         if($msg =~ /<source>0\.0\.0\.0:\d*?<\/source>/) {
875                 my $remote_ip = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
876                 $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$remote_ip:$2<\/source>/s;
877         }
879     # encrypt xml msg
880     my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
882     # opensocket
883     my $socket = &open_socket($address);
884     if( !$socket ) {
885         daemon_log("$session_id ERROR: cannot send ".$header."msg to $address , host not reachable", 1);
886         $error++;
887     }
888     
889     if( $error == 0 ) {
890         # send xml msg
891         print $socket $crypted_msg."\n";
893         daemon_log("$session_id INFO: send ".$header."msg to $address", 5);
894         #daemon_log("DEBUG: message:\n$msg", 9);
895         
896     }
898     # close socket in any case
899     if( $socket ) {
900         close $socket;
901     }
903     if( $error > 0 ) { $new_status = "down"; }
904     else { $new_status = $msg_header; }
907     # known_clients
908     $sql_statement = "SELECT * FROM known_clients WHERE hostname='$address'";
909     $res = $known_clients_db->select_dbentry($sql_statement);
910     if( keys(%$res) > 0) {
911         $act_status = $res->{1}->{'status'};
912         if( $act_status eq "down" ) {
913             $sql_statement = "DELETE FROM known_clients WHERE hostname='$address'";
914             $res = $known_clients_db->del_dbentry($sql_statement);
915             daemon_log("$session_id WARNING: failed 2x to send msg to host '$address', delete host from known_clients", 3);
916         } else { 
917             $sql_statement = "UPDATE known_clients SET status='$new_status' WHERE hostname='$address'";
918             $res = $known_clients_db->update_dbentry($sql_statement);
919             if($new_status eq "down"){
920                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
921             } else {
922                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
923             }
924         }
925     }
927     # known_server
928     $sql_statement = "SELECT * FROM known_server WHERE hostname='$address'";
929     $res = $known_server_db->select_dbentry($sql_statement);
930     if( keys(%$res) > 0 ) {
931         $act_status = $res->{1}->{'status'};
932         if( $act_status eq "down" ) {
933             $sql_statement = "DELETE FROM known_server WHERE hostname='$address'";
934             $res = $known_server_db->del_dbentry($sql_statement);
935             daemon_log("$session_id WARNING: failed 2x to a send msg to host '$address', delete host from known_server", 3);
936         } 
937         else { 
938             $sql_statement = "UPDATE known_server SET status='$new_status' WHERE hostname='$address'";
939             $res = $known_server_db->update_dbentry($sql_statement);
940             if($new_status eq "down"){
941                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
942             }
943             else {
944                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
945             }
946         }
947     }
948     return $error; 
952 sub update_jobdb_status_for_send_msgs {
953     my ($answer, $error) = @_;
954     if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
955         my $jobdb_id = $1;
956             
957         # sending msg faild
958         if( $error ) {
959             if (not $answer =~ /<header>trigger_action_reinstall<\/header>/) {
960                 my $sql_statement = "UPDATE $job_queue_tn ".
961                     "SET status='error', result='can not deliver msg, please consult log file' ".
962                     "WHERE id=$jobdb_id";
963                 my $res = $job_db->update_dbentry($sql_statement);
964             }
966         # sending msg was successful
967         } else {
968             my $sql_statement = "UPDATE $job_queue_tn ".
969                 "SET status='done' ".
970                 "WHERE id=$jobdb_id AND status='processed'";
971             my $res = $job_db->update_dbentry($sql_statement);
972         }
973     }
976 sub _start {
977     my ($kernel) = $_[KERNEL];
978     &trigger_db_loop($kernel);
979     $global_kernel = $kernel;
980         $kernel->yield('create_fai_server_db', $fai_server_tn );
981         $kernel->yield('create_fai_release_db', $fai_release_tn );
982         $kernel->sig(USR1 => "sig_handler");
983         $kernel->sig(USR2 => "create_packages_list_db");
986 sub sig_handler {
987         my ($kernel, $signal) = @_[KERNEL, ARG0] ;
988         daemon_log("0 INFO got signal '$signal'", 1); 
989         $kernel->sig_handled();
990         return;
993 sub next_task {
994     my ($session, $heap) = @_[SESSION, HEAP];
996     while ( keys( %{ $heap->{task} } ) < $max_children ) {
997         my $next_task = shift @tasks;
998         last unless defined $next_task;
1000         my $task = POE::Wheel::Run->new(
1001                 Program => sub { process_task($session, $heap, $next_task) },
1002                 StdioFilter => POE::Filter::Reference->new(),
1003                 StdoutEvent  => "task_result",
1004                 StderrEvent  => "task_debug",
1005                 CloseEvent   => "task_done",
1006                );
1008         $heap->{task}->{ $task->ID } = $task;
1009     }
1012 sub handle_task_result {
1013     my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
1014     my $client_answer = $result->{'answer'};
1015     if( $client_answer =~ s/session_id=(\d+)$// ) {
1016         my $session_id = $1;
1017         if( defined $session_id ) {
1018             my $session_reference = $kernel->ID_id_to_session($session_id);
1019             if( defined $session_reference ) {
1020                 $heap = $session_reference->get_heap();
1021             }
1022         }
1024         if(exists $heap->{'client'}) {
1025             $heap->{'client'}->put($client_answer);
1026         }
1027     }
1028     $kernel->sig(CHLD => "child_reap");
1031 sub handle_task_debug {
1032     my $result = $_[ARG0];
1033     print STDERR "$result\n";
1036 sub handle_task_done {
1037     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1038     delete $heap->{task}->{$task_id};
1039     $kernel->yield("next_task");
1042 sub process_task {
1043     no strict "refs";
1044     my ($session, $heap, $input) = @_;
1045     my $session_id = $session->ID;
1046     my ($msg, $msg_hash, $module);
1047     my $error = 0;
1048     my $answer_l;
1049     my ($answer_header, @answer_target_l, $answer_source);
1050     my $client_answer = "";
1052     daemon_log("", 5); 
1053     daemon_log("$session_id INFO: Incoming msg with session ID $session_id from '".$heap->{'remote_ip'}."'", 5);
1054     #daemon_log("$session_id DEBUG: Incoming msg:\n$input", 9);
1056     ####################
1057     # check incoming msg
1058     # msg is from a new client or gosa
1059     ($msg, $msg_hash, $module) = &input_from_unknown_host($input, $session_id);
1060     # msg is from a gosa-si-server or gosa-si-bus
1061     if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1062         ($msg, $msg_hash, $module) = &input_from_known_server($input, $heap->{'remote_ip'}, $session_id);
1063     }
1064     # msg is from a gosa-si-client
1065     if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1066         ($msg, $msg_hash, $module) = &input_from_known_client($input, $heap->{'remote_ip'}, $session_id);
1067     }
1068     # an error occurred
1069     if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1070         # if an incoming msg could not be decrypted (maybe a wrong key), send client a ping. If the client
1071         # could not understand a msg from its server the client cause a re-registering process
1072         daemon_log("$session_id INFO cannot understand incoming msg, send 'ping'-msg to all host with ip '".$heap->{remote_ip}."' to cause a re-registering of the client if necessary", 5);
1073         my $sql_statement = "SELECT * FROM $main::known_clients_tn WHERE (hostname LIKE '".$heap->{'remote_ip'}."%')";
1074         my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
1075         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1076             my $host_name = $hit->{'hostname'};
1077             my $host_key = $hit->{'hostkey'};
1078             my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source> <target>$host_name</target></xml>";
1079             my $error = &send_msg_to_target($ping_msg, $host_name, $host_key, "gosa_ping", $session_id);
1080             &update_jobdb_status_for_send_msgs($ping_msg, $error);
1081         }
1082         $error++;
1083     }
1085     ######################
1086     # process incoming msg
1087     if( $error == 0) {
1088         daemon_log("$session_id INFO: Incoming msg with header '".@{$msg_hash->{'header'}}[0].
1089                                 "' from '".$heap->{'remote_ip'}."'", 5); 
1090         daemon_log("$session_id DEBUG: Processing module ".$module, 7);
1091         $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1093         if ( 0 < @{$answer_l} ) {
1094             my $answer_str = join("\n", @{$answer_l});
1095             daemon_log("$session_id DEBUG: $module: Got answer from module: \n".$answer_str,8);
1096         } else {
1097             daemon_log("$session_id DEBUG: $module: Got no answer from module!" ,8);
1098         }
1100     }
1101     if( !$answer_l ) { $error++ };
1103     ########
1104     # answer
1105     if( $error == 0 ) {
1107         foreach my $answer ( @{$answer_l} ) {
1108             # for each answer in answer list
1109             
1110             # check outgoing msg to xml validity
1111             my $answer_hash = &check_outgoing_xml_validity($answer);
1112             if( not defined $answer_hash ) {
1113                 next;
1114             }
1115             
1116             $answer_header = @{$answer_hash->{'header'}}[0];
1117             @answer_target_l = @{$answer_hash->{'target'}};
1118             $answer_source = @{$answer_hash->{'source'}}[0];
1120             # deliver msg to all targets 
1121             foreach my $answer_target ( @answer_target_l ) {
1123                 # targets of msg are all gosa-si-clients in known_clients_db
1124                 if( $answer_target eq "*" ) {
1125                     # answer is for all clients
1126                     my $sql_statement= "SELECT * FROM known_clients";
1127                     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
1128                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1129                         my $host_name = $hit->{hostname};
1130                         my $host_key = $hit->{hostkey};
1131                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1132                         &update_jobdb_status_for_send_msgs($answer, $error);
1133                     }
1134                 }
1136                 # targets of msg are all gosa-si-server in known_server_db
1137                 elsif( $answer_target eq "KNOWN_SERVER" ) {
1138                     # answer is for all server in known_server
1139                     my $sql_statement= "SELECT * FROM known_server";
1140                     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
1141                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1142                         my $host_name = $hit->{hostname};
1143                         my $host_key = $hit->{hostkey};
1144                         $answer =~ s/KNOWN_SERVER/$host_name/g;
1145                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1146                         &update_jobdb_status_for_send_msgs($answer, $error);
1147                     }
1148                 }
1150                 # target of msg is GOsa
1151                                 elsif( $answer_target eq "GOSA" ) {
1152                                         my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1153                                         my $add_on = "";
1154                     if( defined $session_id ) {
1155                         $add_on = ".session_id=$session_id";
1156                     }
1157                     # answer is for GOSA and has to returned to connected client
1158                     my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1159                     $client_answer = $gosa_answer.$add_on;
1160                 }
1162                 # target of msg is job queue at this host
1163                 elsif( $answer_target eq "JOBDB") {
1164                     $answer =~ /<header>(\S+)<\/header>/;   
1165                     my $header;
1166                     if( defined $1 ) { $header = $1; }
1167                     my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1168                     &update_jobdb_status_for_send_msgs($answer, $error);
1169                 }
1171                 # target of msg is a mac address
1172                 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 ) {
1173                     daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients", 5);
1174                     my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1175                     my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1176                     my $found_ip_flag = 0;
1177                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1178                         my $host_name = $hit->{hostname};
1179                         my $host_key = $hit->{hostkey};
1180                         $answer =~ s/$answer_target/$host_name/g;
1181                         daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1182                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1183                         &update_jobdb_status_for_send_msgs($answer, $error);
1184                         $found_ip_flag++ ;
1185                     }   
1186                     if( $found_ip_flag == 0) {
1187                         daemon_log("$session_id WARNING: no host found in known_clients with mac address '$answer_target'", 3);
1188                         if( $bus_activ eq "true" ) { 
1189                             daemon_log("$session_id INFO: try to forward msg '$answer_header' to bus '$bus_address'", 5);
1190                             my $sql_statement = "SELECT * FROM known_server WHERE hostname='$bus_address'";
1191                             my $query_res = $known_server_db->select_dbentry( $sql_statement );
1192                             while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1193                                 my $bus_address = $hit->{hostname};
1194                                 my $bus_key = $hit->{hostkey};
1195                                 my $error = &send_msg_to_target($answer, $bus_address, $bus_key, $answer_header, $session_id);
1196                                 &update_jobdb_status_for_send_msgs($answer, $error);
1197                                 last;
1198                             }
1199                         }
1201                     }
1203                 #  answer is for one specific host   
1204                 } else {
1205                     # get encrypt_key
1206                     my $encrypt_key = &get_encrypt_key($answer_target);
1207                     if( not defined $encrypt_key ) {
1208                         # unknown target, forward msg to bus
1209                         daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1210                         if( $bus_activ eq "true" ) { 
1211                             daemon_log("$session_id INFO: try to forward msg '$answer_header' to bus '$bus_address'", 5);
1212                             my $sql_statement = "SELECT * FROM known_server WHERE hostname='$bus_address'";
1213                             my $query_res = $known_server_db->select_dbentry( $sql_statement );
1214                             my $res_length = keys( %{$query_res} );
1215                             if( $res_length == 0 ){
1216                                 daemon_log("$session_id WARNING: send '$answer_header' to '$bus_address' failed, ".
1217                                         "no bus found in known_server", 3);
1218                             }
1219                             else {
1220                                 while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1221                                     my $bus_key = $hit->{hostkey};
1222                                     my $error = &send_msg_to_target($answer, $bus_address, $bus_key, $answer_header,$session_id );
1223                                     &update_jobdb_status_for_send_msgs($answer, $error);
1224                                 }
1225                             }
1226                         }
1227                         next;
1228                     }
1229                     my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1230                     &update_jobdb_status_for_send_msgs($answer, $error);
1231                 }
1232             }
1233         }
1234     }
1236     my $filter = POE::Filter::Reference->new();
1237     my %result = ( 
1238             status => "seems ok to me",
1239             answer => $client_answer,
1240             );
1242     my $output = $filter->put( [ \%result ] );
1243     print @$output;
1249 sub trigger_db_loop {
1250         my ($kernel) = @_ ;
1251         $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1252         $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay); 
1253         $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1254     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1255         $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1259 sub watch_for_done_jobs {
1260     my ($kernel,$heap) = @_[KERNEL, HEAP];
1262     my $sql_statement = "SELECT * FROM ".$job_queue_tn.
1263         " WHERE status='done'";
1264         my $res = $job_db->select_dbentry( $sql_statement );
1266     while( my ($id, $hit) = each %{$res} ) {
1267         my $jobdb_id = $hit->{id};
1268         my $sql_statement = "DELETE FROM $job_queue_tn WHERE id=$jobdb_id"; 
1269         my $res = $job_db->del_dbentry($sql_statement); 
1270     }
1272     $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1276 sub watch_for_new_jobs {
1277         if($watch_for_new_jobs_in_progress == 0) {
1278                 $watch_for_new_jobs_in_progress = 1;
1279                 my ($kernel,$heap) = @_[KERNEL, HEAP];
1281                 # check gosa job queue for jobs with executable timestamp
1282                 my $timestamp = &get_time();
1283                 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE status='waiting' AND (CAST (timestamp AS INTEGER)) < $timestamp ORDER BY timestamp";
1284                 my $res = $job_db->exec_statement( $sql_statement );
1286                 # Merge all new jobs that would do the same actions
1287                 my @drops;
1288                 my $hits;
1289                 foreach my $hit (reverse @{$res} ) {
1290                         my $macaddress= lc @{$hit}[8];
1291                         my $headertag= @{$hit}[5];
1292                         if(
1293                                 defined($hits->{$macaddress}) &&
1294                                 defined($hits->{$macaddress}->{$headertag}) &&
1295                                 defined($hits->{$macaddress}->{$headertag}[0])
1296                         ) {
1297                                 push @drops, "DELETE FROM $job_queue_tn WHERE id = $hits->{$macaddress}->{$headertag}[0]";
1298                         }
1299                         $hits->{$macaddress}->{$headertag}= $hit;
1300                 }
1302                 # Delete new jobs with a matching job in state 'processing'
1303                 foreach my $macaddress (keys %{$hits}) {
1304                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1305                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1306                                 if(defined($jobdb_id)) {
1307                                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND headertag='$jobdb_headertag' AND status='processing'";
1308                                         my $res = $job_db->exec_statement( $sql_statement );
1309                                         foreach my $hit (@{$res}) {
1310                                                 push @drops, "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1311                                         }
1312                                 } else {
1313                                         daemon_log("J ERROR: Job without id exists for macaddress $macaddress!", 1);
1314                                 }
1315                         }
1316                 }
1318                 # Commit deletion
1319                 $job_db->exec_statementlist(\@drops);
1321                 # Look for new jobs that could be executed
1322                 foreach my $macaddress (keys %{$hits}) {
1324                         # Look if there is an executing job
1325                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='processing'";
1326                         my $res = $job_db->exec_statement( $sql_statement );
1328                         # Skip new jobs for host if there is a processing job
1329                         if(defined($res) and defined @{$res}[0]) {
1330                                 next;
1331                         }
1333                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1334                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1335                                 if(defined($jobdb_id)) {
1336                                         my $job_msg = @{$hits->{$macaddress}->{$jobdb_headertag}}[7];
1338                                         daemon_log("J DEBUG: its time to execute $job_msg", 7);
1339                                         my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1340                                         my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1342                                         # expect macaddress is unique!!!!!!
1343                                         my $target = $res_hash->{1}->{hostname};
1345                                         # change header
1346                                         $job_msg =~ s/<header>job_/<header>gosa_/;
1348                                         # add sqlite_id
1349                                         $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1351                                         $job_msg =~ /<header>(\S+)<\/header>/;
1352                                         my $header = $1 ;
1353                                         my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");
1355                                         # update status in job queue to 'processing'
1356                                         $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
1357                                         my $res = $job_db->update_dbentry($sql_statement);
1358 # TODO: abfangen ob alles in ordnung ist oder nicht, wenn nicht error schmeißen                                        
1360                                         # We don't want parallel processing
1361                                         last;
1362                                 }
1363                         }
1364                 }
1366                 $watch_for_new_jobs_in_progress = 0;
1367                 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1368         }
1372 sub watch_for_new_messages {
1373     my ($kernel,$heap) = @_[KERNEL, HEAP];
1374     my @coll_user_msg;   # collection list of outgoing messages
1375     
1376     # check messaging_db for new incoming messages with executable timestamp
1377     my $timestamp = &get_time();
1378     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS INTEGER))<$timestamp AND flag='n' AND direction='in' )";
1379     my $res = $messaging_db->exec_statement( $sql_statement );
1380         foreach my $hit (@{$res}) {
1382         # create outgoing messages
1383         my $message_to = @{$hit}[3];
1384         # translate message_to to plain login name
1385         my @message_to_l = split(/,/, $message_to);  
1386                 my %receiver_h; 
1387                 foreach my $receiver (@message_to_l) {
1388                         if ($receiver =~ /^u_([\s\S]*)$/) {
1389                                 $receiver_h{$1} = 0;
1390                         } elsif ($receiver =~ /^g_([\s\S]*)$/) {
1391 # TODO implement receiver translation
1392                         } else {
1393                                 my $sbjct = &encode_base64(@{$hit}[1]);
1394                                 my $msg = &encode_base64(@{$hit}[7]);
1395                                 &daemon_log("M WARNING: unknown receiver '$receiver' for a user-message 'sbjct - msg'", 3); 
1396                         }
1397                 }
1398                 my @receiver_l = keys(%receiver_h);
1400         my $message_id = @{$hit}[0];
1402         #add each outgoing msg to messaging_db
1403         my $receiver;
1404         foreach $receiver (@receiver_l) {
1405             my $sql_statement = "INSERT INTO $messaging_tn (id, subject, message_from, message_to, flag, direction, delivery_time, message, timestamp) ".
1406                 "VALUES ('".
1407                 $message_id."', '".    # id
1408                 @{$hit}[1]."', '".     # subject
1409                 @{$hit}[2]."', '".     # message_from
1410                 $receiver."', '".      # message_to
1411                 "none"."', '".         # flag
1412                 "out"."', '".          # direction
1413                 @{$hit}[6]."', '".     # delivery_time
1414                 @{$hit}[7]."', '".     # message
1415                 $timestamp."'".     # timestamp
1416                 ")";
1417             &daemon_log("M DEBUG: $sql_statement", 1);
1418             my $res = $messaging_db->exec_statement($sql_statement);
1419             &daemon_log("M INFO: message '".@{$hit}[0]."' is prepared for delivery to receiver '$receiver'", 5);
1420         }
1422         # set incoming message to flag d=deliverd
1423         $sql_statement = "UPDATE $messaging_tn SET flag='p' WHERE id='$message_id'"; 
1424         &daemon_log("M DEBUG: $sql_statement", 7);
1425         $res = $messaging_db->update_dbentry($sql_statement);
1426         &daemon_log("M INFO: message '$message_id' is set to flag 'p' (processed)", 5);
1427     }
1429     $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay); 
1430     return;
1433 sub watch_for_delivery_messages {
1434     my ($kernel, $heap) = @_[KERNEL, HEAP];
1436     # select outgoing messages
1437     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( flag='p' AND direction='out' )";
1438     #&daemon_log("0 DEBUG: $sql", 7);
1439     my $res = $messaging_db->exec_statement( $sql_statement );
1440     
1441     # build out msg for each    usr
1442     foreach my $hit (@{$res}) {
1443         my $receiver = @{$hit}[3];
1444         my $msg_id = @{$hit}[0];
1445         my $subject = @{$hit}[1];
1446         my $message = @{$hit}[7];
1448         # resolve usr -> host where usr is logged in
1449         my $sql = "SELECT * FROM $login_users_tn WHERE (user='$receiver')"; 
1450         #&daemon_log("0 DEBUG: $sql", 7);
1451         my $res = $login_users_db->exec_statement($sql);
1453         # reciver is logged in nowhere
1454         if (not ref(@$res[0]) eq "ARRAY") { next; }    
1456                 my $send_succeed = 0;
1457                 foreach my $hit (@$res) {
1458                                 my $receiver_host = @$hit[0];
1459                                 &daemon_log("M DEBUG: user '$receiver' is logged in at host '$receiver_host'", 7);
1461                                 # fetch key to encrypt msg propperly for usr/host
1462                                 my $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$receiver_host')";
1463                                 &daemon_log("0 DEBUG: $sql", 7);
1464                                 my $res = $known_clients_db->exec_statement($sql);
1466                                 # host is already down
1467                                 if (not ref(@$res[0]) eq "ARRAY") { next; }
1469                                 # host is on
1470                                 my $receiver_key = @{@{$res}[0]}[2];
1471                                 my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
1472                                 my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
1473                                 my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0); 
1474                                 if ($error == 0 ) {
1475                                         $send_succeed++ ;
1476                                 }
1477                 }
1479                 if ($send_succeed) {
1480                                 # set outgoing msg at db to deliverd
1481                                 my $sql = "UPDATE $messaging_tn SET flag='d' WHERE (id='$msg_id' AND direction='out' AND message_to='$receiver')"; 
1482                                 &daemon_log("0 DEBUG: $sql", 7);
1483                                 my $res = $messaging_db->exec_statement($sql); 
1484                 }
1485         }
1487     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay); 
1488     return;
1492 sub watch_for_done_messages {
1493     my ($kernel,$heap) = @_[KERNEL, HEAP];
1495     my $sql = "SELECT * FROM $messaging_tn WHERE (flag='p' AND direction='in')"; 
1496     #&daemon_log("0 DEBUG: $sql", 7);
1497     my $res = $messaging_db->exec_statement($sql); 
1499     foreach my $hit (@{$res}) {
1500         my $msg_id = @{$hit}[0];
1502         my $sql = "SELECT * FROM $messaging_tn WHERE (id='$msg_id' AND direction='out' AND (NOT flag='s'))"; 
1503         #&daemon_log("0 DEBUG: $sql", 7); 
1504         my $res = $messaging_db->exec_statement($sql);
1506         # not all usr msgs have been seen till now
1507         if ( ref(@$res[0]) eq "ARRAY") { next; }
1508         
1509         $sql = "DELETE FROM $messaging_tn WHERE (id='$msg_id')"; 
1510         #&daemon_log("0 DEBUG: $sql", 7);
1511         $res = $messaging_db->exec_statement($sql);
1512     
1513     }
1515     $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay); 
1516     return;
1520 sub get_ldap_handle {
1521         my ($session_id) = @_;
1522         my $heap;
1523         my $ldap_handle;
1525         if (not defined $session_id ) { $session_id = 0 };
1526         if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
1528         if ($session_id == 0) {
1529                 daemon_log("$session_id DEBUG: get_ldap_handle invoked without a session_id, create a new ldap_handle", 7); 
1530                 $ldap_handle = Net::LDAP->new( $ldap_uri );
1531                 $ldap_handle->bind($ldap_admin_dn, password => $ldap_admin_password); 
1533         } else {
1534                 my $session_reference = $global_kernel->ID_id_to_session($session_id);
1535                 if( defined $session_reference ) {
1536                         $heap = $session_reference->get_heap();
1537                 }
1539                 if (not defined $heap) {
1540                         daemon_log("$session_id DEBUG: cannot get heap for session_id '$session_id'", 7); 
1541                         return;
1542                 }
1544                 # TODO: This "if" is nonsense, because it doesn't prove that the
1545                 #       used handle is still valid - or if we've to reconnect...
1546                 #if (not exists $heap->{ldap_handle}) {
1547                         $ldap_handle = Net::LDAP->new( $ldap_uri );
1548                         $ldap_handle->bind($ldap_admin_dn, password => $ldap_admin_password); 
1549                         $heap->{ldap_handle} = $ldap_handle;
1550                 #}
1551         }
1552         return $ldap_handle;
1556 sub change_fai_state {
1557     my ($st, $targets, $session_id) = @_;
1558     $session_id = 0 if not defined $session_id;
1559     # Set FAI state to localboot
1560     my %mapActions= (
1561         reboot    => '',
1562         update    => 'softupdate',
1563         localboot => 'localboot',
1564         reinstall => 'install',
1565         rescan    => '',
1566         wake      => '',
1567         memcheck  => 'memcheck',
1568         sysinfo   => 'sysinfo',
1569         install   => 'install',
1570     );
1572     # Return if this is unknown
1573     if (!exists $mapActions{ $st }){
1574         daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1); 
1575       return;
1576     }
1578     my $state= $mapActions{ $st };
1580     my $ldap_handle = &get_ldap_handle($session_id);
1581     if( defined($ldap_handle) ) {
1583       # Build search filter for hosts
1584         my $search= "(&(objectClass=GOhard)";
1585         foreach (@{$targets}){
1586             $search.= "(macAddress=$_)";
1587         }
1588         $search.= ")";
1590       # If there's any host inside of the search string, procress them
1591         if (!($search =~ /macAddress/)){
1592             daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);    
1593             return;
1594         }
1596       # Perform search for Unit Tag
1597       my $mesg = $ldap_handle->search(
1598           base   => $ldap_base,
1599           scope  => 'sub',
1600           attrs  => ['dn', 'FAIstate', 'objectClass'],
1601           filter => "$search"
1602           );
1604           if ($mesg->count) {
1605                   my @entries = $mesg->entries;
1606                   foreach my $entry (@entries) {
1607                           # Only modify entry if it is not set to '$state'
1608                           if ($entry->get_value("FAIstate") ne "$state"){
1609                                   daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
1610                                   my $result;
1611                                   my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
1612                                   if (exists $tmp{'FAIobject'}){
1613                                           if ($state eq ''){
1614                                                   $result= $ldap_handle->modify($entry->dn, changes => [
1615                                                           delete => [ FAIstate => [] ] ]);
1616                                           } else {
1617                                                   $result= $ldap_handle->modify($entry->dn, changes => [
1618                                                           replace => [ FAIstate => $state ] ]);
1619                                           }
1620                                   } elsif ($state ne ''){
1621                                           $result= $ldap_handle->modify($entry->dn, changes => [
1622                                                   add     => [ objectClass => 'FAIobject' ],
1623                                                   add     => [ FAIstate => $state ] ]);
1624                                   }
1626                                   # Errors?
1627                                   if ($result->code){
1628                                           daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
1629                                   }
1630                           } else {
1631                                   daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 7); 
1632                           }  
1633                   }
1634           }
1635     # if no ldap handle defined
1636     } else {
1637         daemon_log("$session_id ERROR: no LDAP handle defined for update FAIstate", 1); 
1638     }
1643 sub change_goto_state {
1644     my ($st, $targets, $session_id) = @_;
1645     $session_id = 0  if not defined $session_id;
1647     # Switch on or off?
1648     my $state= $st eq 'active' ? 'active': 'locked';
1650     my $ldap_handle = &get_ldap_handle($session_id);
1651     if( defined($ldap_handle) ) {
1653       # Build search filter for hosts
1654       my $search= "(&(objectClass=GOhard)";
1655       foreach (@{$targets}){
1656         $search.= "(macAddress=$_)";
1657       }
1658       $search.= ")";
1660       # If there's any host inside of the search string, procress them
1661       if (!($search =~ /macAddress/)){
1662         return;
1663       }
1665       # Perform search for Unit Tag
1666       my $mesg = $ldap_handle->search(
1667           base   => $ldap_base,
1668           scope  => 'sub',
1669           attrs  => ['dn', 'gotoMode'],
1670           filter => "$search"
1671           );
1673       if ($mesg->count) {
1674         my @entries = $mesg->entries;
1675         foreach my $entry (@entries) {
1677           # Only modify entry if it is not set to '$state'
1678           if ($entry->get_value("gotoMode") ne $state){
1680             daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
1681             my $result;
1682             $result= $ldap_handle->modify($entry->dn, changes => [
1683                                                 replace => [ gotoMode => $state ] ]);
1685             # Errors?
1686             if ($result->code){
1687               &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
1688             }
1690           }
1691         }
1692       }
1694     }
1698 sub run_create_fai_server_db {
1699     my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
1700     my $session_id = $session->ID;
1701     my $task = POE::Wheel::Run->new(
1702             Program => sub { &create_fai_server_db($table_name,$kernel, undef, $session_id) },
1703             StdoutEvent  => "session_run_result",
1704             StderrEvent  => "session_run_debug",
1705             CloseEvent   => "session_run_done",
1706             );
1708     $heap->{task}->{ $task->ID } = $task;
1709     return;
1713 sub create_fai_server_db {
1714     my ($table_name, $kernel, $dont_create_packages_list, $session_id) = @_;
1715         my $result;
1717         if (not defined $session_id) { $session_id = 0; }
1718     my $ldap_handle = &get_ldap_handle();
1719         if(defined($ldap_handle)) {
1720                 daemon_log("$session_id INFO: create_fai_server_db: start", 5);
1721                 my $mesg= $ldap_handle->search(
1722                         base   => $ldap_base,
1723                         scope  => 'sub',
1724                         attrs  => ['FAIrepository', 'gosaUnitTag'],
1725                         filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
1726                 );
1727                 if($mesg->{'resultCode'} == 0 &&
1728                    $mesg->count != 0) {
1729                    foreach my $entry (@{$mesg->{entries}}) {
1730                            if($entry->exists('FAIrepository')) {
1731                                    # Add an entry for each Repository configured for server
1732                                    foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
1733                                                    my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
1734                                                    my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
1735                                                    $result= $fai_server_db->add_dbentry( { 
1736                                                                    table => $table_name,
1737                                                                    primkey => ['server', 'release', 'tag'],
1738                                                                    server => $tmp_url,
1739                                                                    release => $tmp_release,
1740                                                                    sections => $tmp_sections,
1741                                                                    tag => (length($tmp_tag)>0)?$tmp_tag:"",
1742                                                            } );
1743                                            }
1744                                    }
1745                            }
1746                    }
1747                 daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
1749                 # TODO: Find a way to post the 'create_packages_list_db' event
1750                 if(not defined($dont_create_packages_list)) {
1751                         &create_packages_list_db(undef, undef, $session_id);
1752                 }
1753         }       
1754     
1755     $ldap_handle->disconnect;
1756         return $result;
1760 sub run_create_fai_release_db {
1761     my ($session, $heap, $table_name) = @_[SESSION, HEAP, ARG0];
1762         my $session_id = $session->ID;
1763     my $task = POE::Wheel::Run->new(
1764             Program => sub { &create_fai_release_db($table_name, $session_id) },
1765             StdoutEvent  => "session_run_result",
1766             StderrEvent  => "session_run_debug",
1767             CloseEvent   => "session_run_done",
1768             );
1770     $heap->{task}->{ $task->ID } = $task;
1771     return;
1775 sub create_fai_release_db {
1776         my ($table_name, $session_id) = @_;
1777         my $result;
1779     # used for logging
1780     if (not defined $session_id) { $session_id = 0; }
1782     my $ldap_handle = &get_ldap_handle();
1783         if(defined($ldap_handle)) {
1784                 daemon_log("$session_id INFO: create_fai_release_db: start",5);
1785                 my $mesg= $ldap_handle->search(
1786                         base   => $ldap_base,
1787                         scope  => 'sub',
1788                         attrs  => [],
1789                         filter => "(&(objectClass=organizationalUnit)(ou=fai))",
1790                 );
1791                 if($mesg->{'resultCode'} == 0 &&
1792                         $mesg->count != 0) {
1793                         # Walk through all possible FAI container ou's
1794                         my @sql_list;
1795                         my $timestamp= &get_time();
1796                         foreach my $ou (@{$mesg->{entries}}) {
1797                                 my $tmp_classes= resolve_fai_classes($ou->dn, $ldap_handle, $session_id);
1798                                 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
1799                                         my @tmp_array=get_fai_release_entries($tmp_classes);
1800                                         if(@tmp_array) {
1801                                                 foreach my $entry (@tmp_array) {
1802                                                         if(defined($entry) && ref($entry) eq 'HASH') {
1803                                                                 my $sql= 
1804                                                                 "INSERT INTO $table_name "
1805                                                                 ."(timestamp, release, class, type, state) VALUES ("
1806                                                                 .$timestamp.","
1807                                                                 ."'".$entry->{'release'}."',"
1808                                                                 ."'".$entry->{'class'}."',"
1809                                                                 ."'".$entry->{'type'}."',"
1810                                                                 ."'".$entry->{'state'}."')";
1811                                                                 push @sql_list, $sql;
1812                                                         }
1813                                                 }
1814                                         }
1815                                 }
1816                         }
1818                         daemon_log("$session_id DEBUG: Inserting ".scalar @sql_list." entries to DB",8);
1819                         if(@sql_list) {
1820                                 unshift @sql_list, "VACUUM";
1821                                 unshift @sql_list, "DELETE FROM $table_name";
1822                                 $fai_release_db->exec_statementlist(\@sql_list);
1823                         }
1824                         daemon_log("$session_id DEBUG: Done with inserting",7);
1825                 }
1826                 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
1827         }
1828     $ldap_handle->disconnect;
1829         return $result;
1832 sub get_fai_types {
1833         my $tmp_classes = shift || return undef;
1834         my @result;
1836         foreach my $type(keys %{$tmp_classes}) {
1837                 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
1838                         my $entry = {
1839                                 type => $type,
1840                                 state => $tmp_classes->{$type}[0],
1841                         };
1842                         push @result, $entry;
1843                 }
1844         }
1846         return @result;
1849 sub get_fai_state {
1850         my $result = "";
1851         my $tmp_classes = shift || return $result;
1853         foreach my $type(keys %{$tmp_classes}) {
1854                 if(defined($tmp_classes->{$type}[0])) {
1855                         $result = $tmp_classes->{$type}[0];
1856                         
1857                 # State is equal for all types in class
1858                         last;
1859                 }
1860         }
1862         return $result;
1865 sub resolve_fai_classes {
1866         my ($fai_base, $ldap_handle, $session_id) = @_;
1867         if (not defined $session_id) { $session_id = 0; }
1868         my $result;
1869         my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
1870         my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
1871         my $fai_classes;
1873         daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base",7);
1874         my $mesg= $ldap_handle->search(
1875                 base   => $fai_base,
1876                 scope  => 'sub',
1877                 attrs  => ['cn','objectClass','FAIstate'],
1878                 filter => $fai_filter,
1879         );
1880         daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries",7);
1882         if($mesg->{'resultCode'} == 0 &&
1883                 $mesg->count != 0) {
1884                 foreach my $entry (@{$mesg->{entries}}) {
1885                         if($entry->exists('cn')) {
1886                                 my $tmp_dn= $entry->dn();
1888                                 # Skip classname and ou dn parts for class
1889                                 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?),$fai_base$/;
1891                                 # Skip classes without releases
1892                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
1893                                         next;
1894                                 }
1896                                 my $tmp_cn= $entry->get_value('cn');
1897                                 my $tmp_state= $entry->get_value('FAIstate');
1899                                 my $tmp_type;
1900                                 # Get FAI type
1901                                 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
1902                                         if(grep $_ eq $oclass, @possible_fai_classes) {
1903                                                 $tmp_type= $oclass;
1904                                                 last;
1905                                         }
1906                                 }
1908                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
1909                                         # A Subrelease
1910                                         my @sub_releases = split(/,/, $tmp_release);
1912                                         # Walk through subreleases and build hash tree
1913                                         my $hash;
1914                                         while(my $tmp_sub_release = pop @sub_releases) {
1915                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
1916                                         }
1917                                         eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
1918                                 } else {
1919                                         # A branch, no subrelease
1920                                         push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
1921                                 }
1922                         } elsif (!$entry->exists('cn')) {
1923                                 my $tmp_dn= $entry->dn();
1924                                 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?),$fai_base$/;
1926                                 # Skip classes without releases
1927                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
1928                                         next;
1929                                 }
1931                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
1932                                         # A Subrelease
1933                                         my @sub_releases= split(/,/, $tmp_release);
1935                                         # Walk through subreleases and build hash tree
1936                                         my $hash;
1937                                         while(my $tmp_sub_release = pop @sub_releases) {
1938                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
1939                                         }
1940                                         # Remove the last two characters
1941                                         chop($hash);
1942                                         chop($hash);
1944                                         eval('$fai_classes->'.$hash.'= {}');
1945                                 } else {
1946                                         # A branch, no subrelease
1947                                         if(!exists($fai_classes->{$tmp_release})) {
1948                                                 $fai_classes->{$tmp_release} = {};
1949                                         }
1950                                 }
1951                         }
1952                 }
1954                 # The hash is complete, now we can honor the copy-on-write based missing entries
1955                 foreach my $release (keys %$fai_classes) {
1956                         $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
1957                 }
1958         }
1959         return $result;
1962 sub apply_fai_inheritance {
1963        my $fai_classes = shift || return {};
1964        my $tmp_classes;
1966        # Get the classes from the branch
1967        foreach my $class (keys %{$fai_classes}) {
1968                # Skip subreleases
1969                if($class =~ /^ou=.*$/) {
1970                        next;
1971                } else {
1972                        $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
1973                }
1974        }
1976        # Apply to each subrelease
1977        foreach my $subrelease (keys %{$fai_classes}) {
1978                if($subrelease =~ /ou=/) {
1979                        foreach my $tmp_class (keys %{$tmp_classes}) {
1980                                if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
1981                                        $fai_classes->{$subrelease}->{$tmp_class} =
1982                                        deep_copy($tmp_classes->{$tmp_class});
1983                                } else {
1984                                        foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
1985                                                if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
1986                                                        $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
1987                                                        deep_copy($tmp_classes->{$tmp_class}->{$type});
1988                                                }
1989                                        }
1990                                }
1991                        }
1992                }
1993        }
1995        # Find subreleases in deeper levels
1996        foreach my $subrelease (keys %{$fai_classes}) {
1997                if($subrelease =~ /ou=/) {
1998                        foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
1999                                if($subsubrelease =~ /ou=/) {
2000                                        apply_fai_inheritance($fai_classes->{$subrelease});
2001                                }
2002                        }
2003                }
2004        }
2006        return $fai_classes;
2009 sub get_fai_release_entries {
2010         my $tmp_classes = shift || return;
2011         my $parent = shift || "";
2012         my @result = shift || ();
2014         foreach my $entry (keys %{$tmp_classes}) {
2015                 if(defined($entry)) {
2016                         if($entry =~ /^ou=.*$/) {
2017                                 my $release_name = $entry;
2018                                 $release_name =~ s/ou=//g;
2019                                 if(length($parent)>0) {
2020                                         $release_name = $parent."/".$release_name;
2021                                 }
2022                                 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2023                                 foreach my $bufentry(@bufentries) {
2024                                         push @result, $bufentry;
2025                                 }
2026                         } else {
2027                                 my @types = get_fai_types($tmp_classes->{$entry});
2028                                 foreach my $type (@types) {
2029                                         push @result, 
2030                                         {
2031                                                 'class' => $entry,
2032                                                 'type' => $type->{'type'},
2033                                                 'release' => $parent,
2034                                                 'state' => $type->{'state'},
2035                                         };
2036                                 }
2037                         }
2038                 }
2039         }
2041         return @result;
2044 sub deep_copy {
2045         my $this = shift;
2046         if (not ref $this) {
2047                 $this;
2048         } elsif (ref $this eq "ARRAY") {
2049                 [map deep_copy($_), @$this];
2050         } elsif (ref $this eq "HASH") {
2051                 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2052         } else { die "what type is $_?" }
2056 sub session_run_result {
2057     my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];    
2058     $kernel->sig(CHLD => "child_reap");
2061 sub session_run_debug {
2062     my $result = $_[ARG0];
2063     print STDERR "$result\n";
2066 sub session_run_done {
2067     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2068     delete $heap->{task}->{$task_id};
2072 sub create_sources_list {
2073         my $session_id = shift;
2074         my $ldap_handle = &main::get_ldap_handle;
2075         my $result="/tmp/gosa_si_tmp_sources_list";
2077         # Remove old file
2078         if(stat($result)) {
2079                 unlink($result);
2080                 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7); 
2081         }
2083         my $fh;
2084         open($fh, ">$result");
2085         if (not defined $fh) {
2086                 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7); 
2087                 return undef;
2088         }
2089         if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2090                 my $mesg=$ldap_handle->search(
2091                         base    => $main::ldap_server_dn,
2092                         scope   => 'base',
2093                         attrs   => 'FAIrepository',
2094                         filter  => 'objectClass=FAIrepositoryServer'
2095                 );
2096                 if($mesg->count) {
2097                         foreach my $entry(@{$mesg->{'entries'}}) {
2098                                 foreach my $value(@{$entry->get_value('FAIrepository', asref => 1)}) {
2099                                         my ($server, $tag, $release, $sections)= split /\|/, $value;
2100                                         my $line = "deb $server $release";
2101                                         $sections =~ s/,/ /g;
2102                                         $line.= " $sections";
2103                                         print $fh $line."\n";
2104                                 }
2105                         }
2106                 }
2107         } else {
2108                 if (defined $main::ldap_server_dn){
2109                         &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1); 
2110                 } else {
2111                         &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2112                 }
2113         }
2114         close($fh);
2116         return $result;
2120 sub run_create_packages_list_db {
2121     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2122         my $session_id = $session->ID;
2124         my $task = POE::Wheel::Run->new(
2125                                         Priority => +20,
2126                                         Program => sub {&create_packages_list_db(undef, undef, $session_id)},
2127                                         StdoutEvent  => "session_run_result",
2128                                         StderrEvent  => "session_run_debug",
2129                                         CloseEvent   => "session_run_done",
2130                                         );
2131         $heap->{task}->{ $task->ID } = $task;
2135 sub create_packages_list_db {
2136         my ($ldap_handle, $sources_file, $session_id) = @_;
2137         
2138         # it should not be possible to trigger a recreation of packages_list_db
2139         # while packages_list_db is under construction, so set flag packages_list_under_construction
2140         # which is tested befor recreation can be started
2141         if (-r $packages_list_under_construction) {
2142                 daemon_log("$session_id WARNING: packages_list_db is right now under construction, please wait until this process is finished", 3);
2143                 return;
2144         } else {
2145                 daemon_log("$session_id INFO: create_packages_list_db: start", 5); 
2146                 # set packages_list_under_construction to true
2147                 system("touch $packages_list_under_construction");
2148                 @packages_list_statements=();
2149         }
2151         if (not defined $session_id) { $session_id = 0; }
2152         if (not defined $ldap_handle) { 
2153                 $ldap_handle= &get_ldap_handle();
2155                 if (not defined $ldap_handle) {
2156                         daemon_log("$session_id ERROR: no ldap_handle available to create_packages_list_db", 1);
2157                         unlink($packages_list_under_construction);
2158                         return;
2159                 }
2160         }
2161         if (not defined $sources_file) { 
2162                 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5); 
2163                 $sources_file = &create_sources_list($session_id);
2164         }
2166         if (not defined $sources_file) {
2167                 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1); 
2168                 unlink($packages_list_under_construction);
2169                 return;
2170         }
2172         my $line;
2174         open(CONFIG, "<$sources_file") or do {
2175                 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2176                 unlink($packages_list_under_construction);
2177                 return;
2178         };
2180         # Read lines
2181         while ($line = <CONFIG>){
2182                 # Unify
2183                 chop($line);
2184                 $line =~ s/^\s+//;
2185                 $line =~ s/^\s+/ /;
2187                 # Strip comments
2188                 $line =~ s/#.*$//g;
2190                 # Skip empty lines
2191                 if ($line =~ /^\s*$/){
2192                         next;
2193                 }
2195                 # Interpret deb line
2196                 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2197                         my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2198                         my $section;
2199                         foreach $section (split(' ', $sections)){
2200                                 &parse_package_info( $baseurl, $dist, $section, $session_id );
2201                         }
2202                 }
2203         }
2205         close (CONFIG);
2207         find(\&cleanup_and_extract, keys( %repo_dirs ));
2208         &main::strip_packages_list_statements();
2209         unshift @packages_list_statements, "VACUUM";
2210         $packages_list_db->exec_statementlist(\@packages_list_statements);
2211         unlink($packages_list_under_construction);
2212         daemon_log("$session_id INFO: create_packages_list_db: finished", 5); 
2213         return;
2216 # This function should do some intensive task to minimize the db-traffic
2217 sub strip_packages_list_statements {
2218     my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2219         my @new_statement_list=();
2220         my $hash;
2221         my $insert_hash;
2222         my $update_hash;
2223         my $delete_hash;
2224         my $local_timestamp=get_time();
2226         foreach my $existing_entry (@existing_entries) {
2227                 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
2228         }
2230         foreach my $statement (@packages_list_statements) {
2231                 if($statement =~ /^INSERT/i) {
2232                         # Assign the values from the insert statement
2233                         my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~ 
2234                         /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
2235                         if(exists($hash->{$distribution}->{$package}->{$version})) {
2236                                 # If section or description has changed, update the DB
2237                                 if( 
2238                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or 
2239                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
2240                                 ) {
2241                                         @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
2242                                 }
2243                         } else {
2244                                 # Insert a non-existing entry to db
2245                                 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2246                         }
2247                 } elsif ($statement =~ /^UPDATE/i) {
2248                         my ($template,$package,$version) = ($1,$2,$3) if $statement =~
2249                         /^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;
2250                         foreach my $distribution (keys %{$hash}) {
2251                                 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
2252                                         # update the insertion hash to execute only one query per package (insert instead insert+update)
2253                                         @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
2254                                 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
2255                                         if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
2256                                                 my $section;
2257                                                 my $description;
2258                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
2259                                                         length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
2260                                                         $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
2261                                                 }
2262                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2263                                                         $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
2264                                                 }
2265                                                 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2266                                         }
2267                                 }
2268                         }
2269                 }
2270         }
2272         # TODO: Check for orphaned entries
2274         # unroll the insert_hash
2275         foreach my $distribution (keys %{$insert_hash}) {
2276                 foreach my $package (keys %{$insert_hash->{$distribution}}) {
2277                         foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
2278                                 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
2279                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
2280                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
2281                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
2282                                 ."'$local_timestamp')";
2283                         }
2284                 }
2285         }
2287         # unroll the update hash
2288         foreach my $distribution (keys %{$update_hash}) {
2289                 foreach my $package (keys %{$update_hash->{$distribution}}) {
2290                         foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
2291                                 my $set = "";
2292                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
2293                                         $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
2294                                 }
2295                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2296                                         $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
2297                                 }
2298                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
2299                                         $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
2300                                 }
2301                                 if(defined($set) and length($set) > 0) {
2302                                         $set .= "timestamp = '$local_timestamp'";
2303                                 } else {
2304                                         next;
2305                                 }
2306                                 push @new_statement_list, 
2307                                         "UPDATE $main::packages_list_tn SET $set WHERE"
2308                                         ." distribution = '$distribution'"
2309                                         ." AND package = '$package'"
2310                                         ." AND version = '$version'";
2311                         }
2312                 }
2313         }
2315         @packages_list_statements = @new_statement_list;
2319 sub parse_package_info {
2320     my ($baseurl, $dist, $section, $session_id)= @_;
2321     my ($package);
2322     if (not defined $session_id) { $session_id = 0; }
2323     my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
2324     $repo_dirs{ "${repo_path}/pool" } = 1;
2326     foreach $package ("Packages.gz"){
2327         daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
2328         get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
2329         parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
2330     }
2331     
2335 sub get_package {
2336     my ($url, $dest, $session_id)= @_;
2337     if (not defined $session_id) { $session_id = 0; }
2339     my $tpath = dirname($dest);
2340     -d "$tpath" || mkpath "$tpath";
2342     # This is ugly, but I've no time to take a look at "how it works in perl"
2343     if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
2344         system("gunzip -cd '$dest' > '$dest.in'");
2345         daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 5);
2346         unlink($dest);
2347         daemon_log("$session_id DEBUG: delete file '$dest'", 5); 
2348     } else {
2349         daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' failed!", 1);
2350     }
2351     return 0;
2355 sub parse_package {
2356     my ($path, $dist, $srv_path, $session_id)= @_;
2357     if (not defined $session_id) { $session_id = 0;}
2358     my ($package, $version, $section, $description);
2359     my $PACKAGES;
2360     my $timestamp = &get_time();
2362     if(not stat("$path.in")) {
2363         daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
2364         return;
2365     }
2367     open($PACKAGES, "<$path.in");
2368     if(not defined($PACKAGES)) {
2369         daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1); 
2370         return;
2371     }
2373     # Read lines
2374     while (<$PACKAGES>){
2375         my $line = $_;
2376         # Unify
2377         chop($line);
2379         # Use empty lines as a trigger
2380         if ($line =~ /^\s*$/){
2381             my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
2382             push(@packages_list_statements, $sql);
2383             $package = "none";
2384             $version = "none";
2385             $section = "none";
2386             $description = "none"; 
2387             next;
2388         }
2390         # Trigger for package name
2391         if ($line =~ /^Package:\s/){
2392             ($package)= ($line =~ /^Package: (.*)$/);
2393             next;
2394         }
2396         # Trigger for version
2397         if ($line =~ /^Version:\s/){
2398             ($version)= ($line =~ /^Version: (.*)$/);
2399             next;
2400         }
2402         # Trigger for description
2403         if ($line =~ /^Description:\s/){
2404             ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
2405             next;
2406         }
2408         # Trigger for section
2409         if ($line =~ /^Section:\s/){
2410             ($section)= ($line =~ /^Section: (.*)$/);
2411             next;
2412         }
2414         # Trigger for filename
2415         if ($line =~ /^Filename:\s/){
2416             my ($filename) = ($line =~ /^Filename: (.*)$/);
2417             store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
2418             next;
2419         }
2420     }
2422     close( $PACKAGES );
2423     unlink( "$path.in" );
2424     &main::daemon_log("$session_id DEBUG: unlink '$path.in'", 1); 
2428 sub store_fileinfo {
2429     my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
2431     my %fileinfo = (
2432         'package' => $package,
2433         'dist' => $dist,
2434         'version' => $vers,
2435     );
2437     $repo_files{ "${srvdir}/$file" } = \%fileinfo;
2441 sub cleanup_and_extract {
2442     my $fileinfo = $repo_files{ $File::Find::name };
2444     if( defined $fileinfo ) {
2446         my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
2447         my $sql;
2448         my $package = $fileinfo->{ 'package' };
2449         my $newver = $fileinfo->{ 'version' };
2451         mkpath($dir);
2452         system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
2454                 if( -f "$dir/DEBIAN/templates" ) {
2456                         daemon_log("DEBUG: Found debconf templates in '$package' - $newver", 5);
2458                         my $tmpl= "";
2459                         {
2460                                 local $/=undef;
2461                                 open FILE, "$dir/DEBIAN/templates";
2462                                 $tmpl = &encode_base64(<FILE>);
2463                                 close FILE;
2464                         }
2465                         rmtree("$dir/DEBIAN/templates");
2467                         $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
2468                 push @packages_list_statements, $sql;
2469                 }
2470     }
2472     return;
2476 #==== MAIN = main ==============================================================
2477 #  parse commandline options
2478 Getopt::Long::Configure( "bundling" );
2479 GetOptions("h|help" => \&usage,
2480         "c|config=s" => \$cfg_file,
2481         "f|foreground" => \$foreground,
2482         "v|verbose+" => \$verbose,
2483         "no-bus+" => \$no_bus,
2484         "no-arp+" => \$no_arp,
2485            );
2487 #  read and set config parameters
2488 &check_cmdline_param ;
2489 &read_configfile;
2490 &check_pid;
2492 $SIG{CHLD} = 'IGNORE';
2494 # forward error messages to logfile
2495 if( ! $foreground ) {
2496   open( STDIN,  '+>/dev/null' );
2497   open( STDOUT, '+>&STDIN'    );
2498   open( STDERR, '+>&STDIN'    );
2501 # Just fork, if we are not in foreground mode
2502 if( ! $foreground ) { 
2503     chdir '/'                 or die "Can't chdir to /: $!";
2504     $pid = fork;
2505     setsid                    or die "Can't start a new session: $!";
2506     umask 0;
2507 } else { 
2508     $pid = $$; 
2511 # Do something useful - put our PID into the pid_file
2512 if( 0 != $pid ) {
2513     open( LOCK_FILE, ">$pid_file" );
2514     print LOCK_FILE "$pid\n";
2515     close( LOCK_FILE );
2516     if( !$foreground ) { 
2517         exit( 0 ) 
2518     };
2521 daemon_log(" ", 1);
2522 daemon_log("$0 started!", 1);
2524 if ($no_bus > 0) {
2525     $bus_activ = "false"
2528 # connect to gosa-si job queue
2529 $job_db = GOSA::DBsqlite->new($job_queue_file_name);
2530 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
2532 # connect to known_clients_db
2533 $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
2534 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
2536 # connect to known_server_db
2537 $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
2538 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
2540 # connect to login_usr_db
2541 $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
2542 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
2544 # connect to fai_server_db and fai_release_db
2545 unlink($fai_server_file_name);
2546 $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
2547 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
2549 unlink($fai_release_file_name);
2550 $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
2551 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
2553 # connect to packages_list_db
2554 #unlink($packages_list_file_name);
2555 unlink($packages_list_under_construction);
2556 $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
2557 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
2559 # connect to messaging_db
2560 $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
2561 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
2564 # create xml object used for en/decrypting
2565 $xml = new XML::Simple();
2567 # create socket for incoming xml messages
2569 POE::Component::Server::TCP->new(
2570         Port => $server_port,
2571         ClientInput => sub {
2572         my ($kernel, $input) = @_[KERNEL, ARG0];
2573         push(@tasks, $input);
2574         $kernel->yield("next_task");
2575         },
2576     InlineStates => {
2577         next_task => \&next_task,
2578         task_result => \&handle_task_result,
2579         task_done   => \&handle_task_done,
2580         task_debug  => \&handle_task_debug,
2581         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
2582     }
2583 );
2585 daemon_log("start socket for incoming xml messages at port '$server_port' ", 1);
2587 # create session for repeatedly checking the job queue for jobs
2588 POE::Session->create(
2589         inline_states => {
2590                 _start => \&_start,
2591                 sig_handler => \&sig_handler,
2592         watch_for_new_messages => \&watch_for_new_messages,
2593         watch_for_delivery_messages => \&watch_for_delivery_messages,
2594         watch_for_done_messages => \&watch_for_done_messages,
2595                 watch_for_new_jobs => \&watch_for_new_jobs,
2596         watch_for_done_jobs => \&watch_for_done_jobs,
2597         create_packages_list_db => \&run_create_packages_list_db,
2598         create_fai_server_db => \&run_create_fai_server_db,
2599         create_fai_release_db => \&run_create_fai_release_db,
2600         session_run_result => \&session_run_result,
2601         session_run_debug => \&session_run_debug,
2602         session_run_done => \&session_run_done,
2603         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
2604         }
2605 );
2608 # import all modules
2609 &import_modules;
2611 # check wether all modules are gosa-si valid passwd check
2613 POE::Kernel->run();
2614 exit;