Code

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