Code

7653c67f444b051358bafcc6e5e7971182df2453
[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 #===============================================================================
23 use strict;
24 use warnings;
25 use Getopt::Long;
26 use Config::IniFiles;
27 use POSIX;
29 use Fcntl;
30 use IO::Socket::INET;
31 use IO::Handle;
32 use IO::Select;
33 use Symbol qw(qualify_to_ref);
34 use Crypt::Rijndael;
35 use MIME::Base64;
36 use Digest::MD5  qw(md5 md5_hex md5_base64);
37 use XML::Simple;
38 use Data::Dumper;
39 use Sys::Syslog qw( :DEFAULT setlogsock);
40 use Cwd;
41 use File::Spec;
42 use File::Basename;
43 use File::Path;
44 use GOSA::DBsqlite;
45 use GOSA::GosaSupportDaemon;
46 use POE qw(Component::Server::TCP Wheel::Run Filter::Reference);
47 use Net::LDAP;
48 use Net::LDAP::Util qw(:escape);
50 my $modules_path = "/usr/lib/gosa-si/modules";
51 use lib "/usr/lib/gosa-si/modules";
53 my (%cfg_defaults, $foreground, $verbose, $ping_timeout);
54 my ($bus_activ, $bus, $msg_to_bus, $bus_cipher);
55 my ($server, $server_mac_address);
56 my ($gosa_server, $job_queue_timeout, $job_queue_loop_delay);
57 my ($known_modules);
58 my ($pid_file, $procid, $pid, $log_file);
59 my ($arp_activ, $arp_fifo);
60 my ($xml);
61 my $sources_list;
62 my $max_clients;
63 # variables declared in config file are always set to 'our'
64 our (%cfg_defaults, $log_file, $pid_file, 
65     $server_ip, $server_port, $SIPackages_key, 
66     $arp_activ, $gosa_unit_tag,
67     $GosaPackages_key, $gosa_ip, $gosa_port, $gosa_timeout,
68 );
70 # additional variable which should be globaly accessable
71 our $server_address;
72 our $bus_address;
73 our $gosa_address;
74 our $no_bus;
75 our $no_arp;
76 our $verbose;
77 our $forground;
78 our $cfg_file;
79 our ($ldap_handle, $ldap_uri, $ldap_base, $ldap_admin_dn, $ldap_admin_password, $ldap_server_dn);
82 # specifies the verbosity of the daemon_log
83 $verbose = 0 ;
85 # if foreground is not null, script will be not forked to background
86 $foreground = 0 ;
88 # specifies the timeout seconds while checking the online status of a registrating client
89 $ping_timeout = 5;
91 $no_bus = 0;
92 $bus_activ = "true";
94 $no_arp = 0;
96 our $prg= basename($0);
98 # holds all gosa jobs
99 our $job_db;
100 our $job_queue_tn = 'jobs';
101 my $job_queue_file_name;
102 my @job_queue_col_names = ("id INTEGER", 
103                 "timestamp", 
104                 "status DEFAULT 'none'", 
105                 "result DEFAULT 'none'", 
106                 "progress DEFAULT 'none'", 
107                 "headertag DEFAULT 'none'", 
108                 "targettag DEFAULT 'none'", 
109                 "xmlmessage DEFAULT 'none'", 
110                 "macaddress DEFAULT 'none'",
111                 );
113 # holds all other gosa-sd as well as the gosa-sd-bus
114 our $known_server_db;
115 our $known_server_tn = "known_server";
116 my $known_server_file_name;
117 my @known_server_col_names = ('hostname', 'status', 'hostkey', 'timestamp');
119 # holds all registrated clients
120 our $known_clients_db;
121 our $known_clients_tn = "known_clients";
122 my $known_clients_file_name;
123 my @known_clients_col_names = ('hostname', 'status', 'hostkey', 'timestamp', 'macaddress', 'events');
125 # holds all logged in user at each client 
126 our $login_users_db;
127 our $login_users_tn = "login_users";
128 my $login_users_file_name;
129 my @login_users_col_names = ('client', 'user', 'timestamp');
131 # holds all fai server, the debian release and tag
132 our $fai_server_db;
133 our $fai_server_tn = "fai_server"; 
134 my $fai_server_file_name;
135 our @fai_server_col_names = ('timestamp', 'server', 'release', 'sections', 'tag'); 
136 our $fai_release_tn = "fai_release"; 
137 our @fai_release_col_names = ('timestamp', 'release', 'class', 'type', 'state'); 
139 # holds all packages available from different repositories
140 our $packages_list_db;
141 our $packages_list_tn = "packages_list";
142 my $packages_list_file_name;
143 our @packages_list_col_names = ('distribution', 'package', 'version', 'section', 'description', 'template', 'timestamp');
144 my $outdir = "/tmp/packages_list_db";
145 my $arch = "i386"; 
147 # holds all messages which should be delivered to a user
148 our $messaging_db;
149 our $messaging_tn = "messaging"; 
150 our @messaging_col_names = ('subject', 'from', 'to', 'flag', 'direction', 'delivery_time', 'message', 'timestamp', 'id INTEGER', );
151 my $messaging_file_name;
153 # path to directory to store client install log files
154 our $client_fai_log_dir = "/var/log/fai"; 
156 # queue which stores taskes until one of the $max_children children are ready to process the task
157 my @tasks = qw();
158 my $max_children = 2;
161 %cfg_defaults = (
162 "general" => {
163     "log-file" => [\$log_file, "/var/run/".$prg.".log"],
164     "pid-file" => [\$pid_file, "/var/run/".$prg.".pid"],
165     },
166 "bus" => {
167     "activ" => [\$bus_activ, "true"],
168     },
169 "server" => {
170     "port" => [\$server_port, "20081"],
171     "known-clients" => [\$known_clients_file_name, '/var/lib/gosa-si/clients.db' ],
172     "known-servers" => [\$known_server_file_name, '/var/lib/gosa-si/servers.db'],
173     "login-users" => [\$login_users_file_name, '/var/lib/gosa-si/users.db'],
174     "fai-server" => [\$fai_server_file_name, '/var/lib/gosa-si/fai.db'],
175     "packages-list" => [\$packages_list_file_name, '/var/lib/gosa-si/packages.db'],
176     "messaging" => [\$messaging_file_name, '/var/lib/gosa-si/messaging.db'],
177     "source-list" => [\$sources_list, '/etc/apt/sources.list'],
178     "ldap-uri" => [\$ldap_uri, ""],
179     "ldap-base" => [\$ldap_base, ""],
180     "ldap-admin-dn" => [\$ldap_admin_dn, ""],
181     "ldap-admin-password" => [\$ldap_admin_password, ""],
182     "gosa-unit-tag" => [\$gosa_unit_tag, ""],
183     "max-clients" => [\$max_clients, 10],
184     },
185 "GOsaPackages" => {
186     "ip" => [\$gosa_ip, "0.0.0.0"],
187     "port" => [\$gosa_port, "20082"],
188     "job-queue" => [\$job_queue_file_name, '/var/lib/gosa-si/jobs.db'],
189     "job-queue-loop-delay" => [\$job_queue_loop_delay, 3],
190     "key" => [\$GosaPackages_key, "none"],
191     },
192 "SIPackages" => {
193     "key" => [\$SIPackages_key, "none"],
194     },
195 );
198 #===  FUNCTION  ================================================================
199 #         NAME:  usage
200 #   PARAMETERS:  nothing
201 #      RETURNS:  nothing
202 #  DESCRIPTION:  print out usage text to STDERR
203 #===============================================================================
204 sub usage {
205     print STDERR << "EOF" ;
206 usage: $prg [-hvf] [-c config]
208            -h        : this (help) message
209            -c <file> : config file
210            -f        : foreground, process will not be forked to background
211            -v        : be verbose (multiple to increase verbosity)
212            -no-bus   : starts $prg without connection to bus
213            -no-arp   : starts $prg without connection to arp module
214  
215 EOF
216     print "\n" ;
220 #===  FUNCTION  ================================================================
221 #         NAME:  read_configfile
222 #   PARAMETERS:  cfg_file - string -
223 #      RETURNS:  nothing
224 #  DESCRIPTION:  read cfg_file and set variables
225 #===============================================================================
226 sub read_configfile {
227     my $cfg;
228     if( defined( $cfg_file) && ( length($cfg_file) > 0 )) {
229         if( -r $cfg_file ) {
230             $cfg = Config::IniFiles->new( -file => $cfg_file );
231         } else {
232             print STDERR "Couldn't read config file!\n";
233         }
234     } else {
235         $cfg = Config::IniFiles->new() ;
236     }
237     foreach my $section (keys %cfg_defaults) {
238         foreach my $param (keys %{$cfg_defaults{ $section }}) {
239             my $pinfo = $cfg_defaults{ $section }{ $param };
240             ${@$pinfo[ 0 ]} = $cfg->val( $section, $param, @$pinfo[ 1 ] );
241         }
242     }
246 #===  FUNCTION  ================================================================
247 #         NAME:  logging
248 #   PARAMETERS:  level - string - default 'info'
249 #                msg - string -
250 #                facility - string - default 'LOG_DAEMON'
251 #      RETURNS:  nothing
252 #  DESCRIPTION:  function for logging
253 #===============================================================================
254 sub daemon_log {
255     # log into log_file
256     my( $msg, $level ) = @_;
257     if(not defined $msg) { return }
258     if(not defined $level) { $level = 1 }
259     if(defined $log_file){
260         open(LOG_HANDLE, ">>$log_file");
261         if(not defined open( LOG_HANDLE, ">>$log_file" )) {
262             print STDERR "cannot open $log_file: $!";
263             return }
264             chomp($msg);
265             if($level <= $verbose){
266                 my ($seconds, $minutes, $hours, $monthday, $month,
267                         $year, $weekday, $yearday, $sommertime) = localtime(time);
268                 $hours = $hours < 10 ? $hours = "0".$hours : $hours;
269                 $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
270                 $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
271                 my @monthnames = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
272                 $month = $monthnames[$month];
273                 $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
274                 $year+=1900;
275                 my $name = $prg;
277                 my $log_msg = "$month $monthday $hours:$minutes:$seconds $name $msg\n";
278                 print LOG_HANDLE $log_msg;
279                 if( $foreground ) { 
280                     print STDERR $log_msg;
281                 }
282             }
283         close( LOG_HANDLE );
284     }
288 #===  FUNCTION  ================================================================
289 #         NAME:  check_cmdline_param
290 #   PARAMETERS:  nothing
291 #      RETURNS:  nothing
292 #  DESCRIPTION:  validates commandline parameter
293 #===============================================================================
294 sub check_cmdline_param () {
295     my $err_config;
296     my $err_counter = 0;
297         if(not defined($cfg_file)) {
298                 $cfg_file = "/etc/gosa-si/server.conf";
299                 if(! -r $cfg_file) {
300                         $err_config = "please specify a config file";
301                         $err_counter += 1;
302                 }
303     }
304     if( $err_counter > 0 ) {
305         &usage( "", 1 );
306         if( defined( $err_config)) { print STDERR "$err_config\n"}
307         print STDERR "\n";
308         exit( -1 );
309     }
313 #===  FUNCTION  ================================================================
314 #         NAME:  check_pid
315 #   PARAMETERS:  nothing
316 #      RETURNS:  nothing
317 #  DESCRIPTION:  handels pid processing
318 #===============================================================================
319 sub check_pid {
320     $pid = -1;
321     # Check, if we are already running
322     if( open(LOCK_FILE, "<$pid_file") ) {
323         $pid = <LOCK_FILE>;
324         if( defined $pid ) {
325             chomp( $pid );
326             if( -f "/proc/$pid/stat" ) {
327                 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
328                 if( $stat ) {
329                                         daemon_log("ERROR: Already running",1);
330                     close( LOCK_FILE );
331                     exit -1;
332                 }
333             }
334         }
335         close( LOCK_FILE );
336         unlink( $pid_file );
337     }
339     # create a syslog msg if it is not to possible to open PID file
340     if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
341         my($msg) = "Couldn't obtain lockfile '$pid_file' ";
342         if (open(LOCK_FILE, '<', $pid_file)
343                 && ($pid = <LOCK_FILE>))
344         {
345             chomp($pid);
346             $msg .= "(PID $pid)\n";
347         } else {
348             $msg .= "(unable to read PID)\n";
349         }
350         if( ! ($foreground) ) {
351             openlog( $0, "cons,pid", "daemon" );
352             syslog( "warning", $msg );
353             closelog();
354         }
355         else {
356             print( STDERR " $msg " );
357         }
358         exit( -1 );
359     }
362 #===  FUNCTION  ================================================================
363 #         NAME:  import_modules
364 #   PARAMETERS:  module_path - string - abs. path to the directory the modules 
365 #                are stored
366 #      RETURNS:  nothing
367 #  DESCRIPTION:  each file in module_path which ends with '.pm' and activation 
368 #                state is on is imported by "require 'file';"
369 #===============================================================================
370 sub import_modules {
371     daemon_log(" ", 1);
373     if (not -e $modules_path) {
374         daemon_log("ERROR: cannot find directory or directory is not readable: $modules_path", 1);   
375     }
377     opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
378     while (defined (my $file = readdir (DIR))) {
379         if (not $file =~ /(\S*?).pm$/) {
380             next;
381         }
382                 my $mod_name = $1;
384         if( $file =~ /ArpHandler.pm/ ) {
385             if( $no_arp > 0 ) {
386                 next;
387             }
388         }
389         
390         eval { require $file; };
391         if ($@) {
392             daemon_log("ERROR: gosa-si-server could not load module $file", 1);
393             daemon_log("$@", 5);
394                 } else {
395                         my $info = eval($mod_name.'::get_module_info()');
396                         # Only load module if get_module_info() returns a non-null object
397                         if( $info ) {
398                                 my ($input_address, $input_key, $input, $input_active, $input_type) = @{$info};
399                                 $known_modules->{$mod_name} = $info;
400                                 daemon_log("INFO: module $mod_name loaded", 5);
401                         }
402                 }
403     }   
404     close (DIR);
408 #===  FUNCTION  ================================================================
409 #         NAME:  sig_int_handler
410 #   PARAMETERS:  signal - string - signal arose from system
411 #      RETURNS:  noting
412 #  DESCRIPTION:  handels tasks to be done befor signal becomes active
413 #===============================================================================
414 sub sig_int_handler {
415     my ($signal) = @_;
417         if(defined($ldap_handle)) {
418                 $ldap_handle->disconnect;
419         }
421     daemon_log("shutting down gosa-si-server", 1);
422     system("killall gosa-si-server");
424 $SIG{INT} = \&sig_int_handler;
427 sub check_key_and_xml_validity {
428     my ($crypted_msg, $module_key, $session_id) = @_;
429     my $msg;
430     my $msg_hash;
431     my $error_string;
432     eval{
433         $msg = &decrypt_msg($crypted_msg, $module_key);
435         if ($msg =~ /<xml>/i){
436             $msg =~ s/\s+/ /g;  # just for better daemon_log
437             daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 8);
438             $msg_hash = $xml->XMLin($msg, ForceArray=>1);
440             ##############
441             # check header
442             if( not exists $msg_hash->{'header'} ) { die "no header specified"; }
443             my $header_l = $msg_hash->{'header'};
444             if( 1 > @{$header_l} ) { die 'empty header tag'; }
445             if( 1 < @{$header_l} ) { die 'more than one header specified'; }
446             my $header = @{$header_l}[0];
447             if( 0 == length $header) { die 'empty string in header tag'; }
449             ##############
450             # check source
451             if( not exists $msg_hash->{'source'} ) { die "no source specified"; }
452             my $source_l = $msg_hash->{'source'};
453             if( 1 > @{$source_l} ) { die 'empty source tag'; }
454             if( 1 < @{$source_l} ) { die 'more than one source specified'; }
455             my $source = @{$source_l}[0];
456             if( 0 == length $source) { die 'source error'; }
458             ##############
459             # check target
460             if( not exists $msg_hash->{'target'} ) { die "no target specified"; }
461             my $target_l = $msg_hash->{'target'};
462             if( 1 > @{$target_l} ) { die 'empty target tag'; }
463         }
464     };
465     if($@) {
466         daemon_log("$session_id DEBUG: do not understand the message: $@", 7);
467         $msg = undef;
468         $msg_hash = undef;
469     }
471     return ($msg, $msg_hash);
475 sub check_outgoing_xml_validity {
476     my ($msg) = @_;
478     my $msg_hash;
479     eval{
480         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
482         ##############
483         # check header
484         my $header_l = $msg_hash->{'header'};
485         if( 1 != @{$header_l} ) {
486             die 'no or more than one headers specified';
487         }
488         my $header = @{$header_l}[0];
489         if( 0 == length $header) {
490             die 'header has length 0';
491         }
493         ##############
494         # check source
495         my $source_l = $msg_hash->{'source'};
496         if( 1 != @{$source_l} ) {
497             die 'no or more than 1 sources specified';
498         }
499         my $source = @{$source_l}[0];
500         if( 0 == length $source) {
501             die 'source has length 0';
502         }
503         unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
504                 $source =~ /^GOSA$/i ) {
505             die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
506         }
507         
508         ##############
509         # check target  
510         my $target_l = $msg_hash->{'target'};
511         if( 0 == @{$target_l} ) {
512             die "no targets specified";
513         }
514         foreach my $target (@$target_l) {
515             if( 0 == length $target) {
516                 die "target has length 0";
517             }
518             unless( $target =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
519                     $target =~ /^GOSA$/i ||
520                     $target =~ /^\*$/ ||
521                     $target =~ /KNOWN_SERVER/i ||
522                     $target =~ /JOBDB/i ||
523                     $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 ){
524                 die "target '$target' is not a complete ip-address with port or a valid target name or a mac-address";
525             }
526         }
527     };
528     if($@) {
529         daemon_log("WARNING: outgoing msg is not gosa-si envelope conform", 5);
530         daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 8);
531         $msg_hash = undef;
532     }
534     return ($msg_hash);
538 sub input_from_known_server {
539     my ($input, $remote_ip, $session_id) = @_ ;  
540     my ($msg, $msg_hash, $module);
542     my $sql_statement= "SELECT * FROM known_server";
543     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
545     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
546         my $host_name = $hit->{hostname};
547         if( not $host_name =~ "^$remote_ip") {
548             next;
549         }
550         my $host_key = $hit->{hostkey};
551         daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 7);
552         daemon_log("DEBUG: input_from_known_server: host_key: $host_key", 7);
554         # check if module can open msg envelope with module key
555         my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
556         if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
557             daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 7);
558             daemon_log("$@", 8);
559             next;
560         }
561         else {
562             $msg = $tmp_msg;
563             $msg_hash = $tmp_msg_hash;
564             $module = "SIPackages";
565             last;
566         }
567     }
569     if( (!$msg) || (!$msg_hash) || (!$module) ) {
570         daemon_log("$session_id DEBUG: Incoming message is not from a known server", 7);
571     }
572   
573     return ($msg, $msg_hash, $module);
577 sub input_from_known_client {
578     my ($input, $remote_ip, $session_id) = @_ ;  
579     my ($msg, $msg_hash, $module);
581     my $sql_statement= "SELECT * FROM known_clients";
582     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
583     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
584         my $host_name = $hit->{hostname};
585         if( not $host_name =~ /^$remote_ip:\d*$/) {
586                 next;
587                 }
588         my $host_key = $hit->{hostkey};
589         &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 7);
590         &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 7);
592         # check if module can open msg envelope with module key
593         ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
595         if( (!$msg) || (!$msg_hash) ) {
596             &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 7);
597             &daemon_log("$@", 8);
598             next;
599         }
600         else {
601             $module = "SIPackages";
602             last;
603         }
604     }
606     if( (!$msg) || (!$msg_hash) || (!$module) ) {
607         &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 7);
608     }
610     return ($msg, $msg_hash, $module);
614 sub input_from_unknown_host {
615     no strict "refs";
616     my ($input, $session_id) = @_ ;
617     my ($msg, $msg_hash, $module);
618     my $error_string;
619     
620         my %act_modules = %$known_modules;
622         while( my ($mod, $info) = each(%act_modules)) {
624         # check a key exists for this module
625         my $module_key = ${$mod."_key"};
626         if( not defined $module_key ) {
627             if( $mod eq 'ArpHandler' ) {
628                 next;
629             }
630             daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
631             next;
632         }
633         daemon_log("$session_id DEBUG: $mod: $module_key", 7);
635         # check if module can open msg envelope with module key
636         ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
637         if( (not defined $msg) || (not defined $msg_hash) ) {
638             next;
639         }
640         else {
641             $module = $mod;
642             last;
643         }
644     }
646     if( (!$msg) || (!$msg_hash) || (!$module)) {
647         daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 7);
648     }
650     return ($msg, $msg_hash, $module);
654 sub create_ciphering {
655     my ($passwd) = @_;
656         if((!defined($passwd)) || length($passwd)==0) {
657                 $passwd = "";
658         }
659     $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
660     my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
661     my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
662     $my_cipher->set_iv($iv);
663     return $my_cipher;
667 sub encrypt_msg {
668     my ($msg, $key) = @_;
669     my $my_cipher = &create_ciphering($key);
670     my $len;
671     {
672             use bytes;
673             $len= 16-length($msg)%16;
674     }
675     $msg = "\0"x($len).$msg;
676     $msg = $my_cipher->encrypt($msg);
677     chomp($msg = &encode_base64($msg));
678     # there are no newlines allowed inside msg
679     $msg=~ s/\n//g;
680     return $msg;
684 sub decrypt_msg {
686     my ($msg, $key) = @_ ;
687     $msg = &decode_base64($msg);
688     my $my_cipher = &create_ciphering($key);
689     $msg = $my_cipher->decrypt($msg); 
690     $msg =~ s/\0*//g;
691     return $msg;
695 sub get_encrypt_key {
696     my ($target) = @_ ;
697     my $encrypt_key;
698     my $error = 0;
700     # target can be in known_server
701     if( not defined $encrypt_key ) {
702         my $sql_statement= "SELECT * FROM known_server WHERE hostname='$target'";
703         my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
704         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
705             my $host_name = $hit->{hostname};
706             if( $host_name ne $target ) {
707                 next;
708             }
709             $encrypt_key = $hit->{hostkey};
710             last;
711         }
712     }
714     # target can be in known_client
715     if( not defined $encrypt_key ) {
716         my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$target'";
717         my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
718         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
719             my $host_name = $hit->{hostname};
720             if( $host_name ne $target ) {
721                 next;
722             }
723             $encrypt_key = $hit->{hostkey};
724             last;
725         }
726     }
728     return $encrypt_key;
732 #===  FUNCTION  ================================================================
733 #         NAME:  open_socket
734 #   PARAMETERS:  PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
735 #                [PeerPort] string necessary if port not appended by PeerAddr
736 #      RETURNS:  socket IO::Socket::INET
737 #  DESCRIPTION:  open a socket to PeerAddr
738 #===============================================================================
739 sub open_socket {
740     my ($PeerAddr, $PeerPort) = @_ ;
741     if(defined($PeerPort)){
742         $PeerAddr = $PeerAddr.":".$PeerPort;
743     }
744     my $socket;
745     $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
746             Porto => "tcp",
747             Type => SOCK_STREAM,
748             Timeout => 5,
749             );
750     if(not defined $socket) {
751         return;
752     }
753 #    &daemon_log("DEBUG: open_socket: $PeerAddr", 7);
754     return $socket;
758 #===  FUNCTION  ================================================================
759 #         NAME:  get_ip 
760 #   PARAMETERS:  interface name (i.e. eth0)
761 #      RETURNS:  (ip address) 
762 #  DESCRIPTION:  Uses ioctl to get ip address directly from system.
763 #===============================================================================
764 sub get_ip {
765         my $ifreq= shift;
766         my $result= "";
767         my $SIOCGIFADDR= 0x8915;       # man 2 ioctl_list
768         my $proto= getprotobyname('ip');
770         socket SOCKET, PF_INET, SOCK_DGRAM, $proto
771                 or die "socket: $!";
773         if(ioctl SOCKET, $SIOCGIFADDR, $ifreq) {
774                 my ($if, $sin)    = unpack 'a16 a16', $ifreq;
775                 my ($port, $addr) = sockaddr_in $sin;
776                 my $ip            = inet_ntoa $addr;
778                 if ($ip && length($ip) > 0) {
779                         $result = $ip;
780                 }
781         }
783         return $result;
787 sub get_local_ip_for_remote_ip {
788         my $remote_ip= shift;
789         my $result="0.0.0.0";
791         if($remote_ip =~ /^(\d\d?\d?\.){3}\d\d?\d?$/) {
792                 if($remote_ip eq "127.0.0.1") {
793                         $result = "127.0.0.1";
794                 } else {
795                         my $PROC_NET_ROUTE= ('/proc/net/route');
797                         open(PROC_NET_ROUTE, "<$PROC_NET_ROUTE")
798                                 or die "Could not open $PROC_NET_ROUTE";
800                         my @ifs = <PROC_NET_ROUTE>;
802                         close(PROC_NET_ROUTE);
804                         # Eat header line
805                         shift @ifs;
806                         chomp @ifs;
807                         foreach my $line(@ifs) {
808                                 my ($Iface,$Destination,$Gateway,$Flags,$RefCnt,$Use,$Metric,$Mask,$MTU,$Window,$IRTT)=split(/\s/, $line);
809                                 my $destination;
810                                 my $mask;
811                                 my ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Destination);
812                                 $destination= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
813                                 ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Mask);
814                                 $mask= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
815                                 if(new NetAddr::IP($remote_ip)->within(new NetAddr::IP($destination, $mask))) {
816                                         # destination matches route, save mac and exit
817                                         $result= &get_ip($Iface);
818                                         last;
819                                 }
820                         }
821                 }
822         } else {
823                 daemon_log("get_local_ip_for_remote_ip was called with a non-ip parameter: $remote_ip", 1);
824         }
825         return $result;
829 sub send_msg_to_target {
830     my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
831     my $error = 0;
832     my $header;
833     my $new_status;
834     my $act_status;
835     my ($sql_statement, $res);
836   
837     if( $msg_header ) {
838         $header = "'$msg_header'-";
839     } else {
840         $header = "";
841     }
843         # Patch the source ip
844         if($msg =~ /<source>0\.0\.0\.0:\d*?<\/source>/) {
845                 my $remote_ip = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
846                 $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$remote_ip:$2<\/source>/s;
847         }
849     # encrypt xml msg
850     my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
852     # opensocket
853     my $socket = &open_socket($address);
854     if( !$socket ) {
855         daemon_log("$session_id ERROR: cannot send ".$header."msg to $address , host not reachable", 1);
856         $error++;
857     }
858     
859     if( $error == 0 ) {
860         # send xml msg
861         print $socket $crypted_msg."\n";
863         daemon_log("$session_id INFO: send ".$header."msg to $address", 5);
864         #daemon_log("DEBUG: message:\n$msg", 9);
865         
866     }
868     # close socket in any case
869     if( $socket ) {
870         close $socket;
871     }
873     if( $error > 0 ) { $new_status = "down"; }
874     else { $new_status = $msg_header; }
877     # known_clients
878     $sql_statement = "SELECT * FROM known_clients WHERE hostname='$address'";
879     $res = $known_clients_db->select_dbentry($sql_statement);
880     if( keys(%$res) > 0) {
881         $act_status = $res->{1}->{'status'};
882         if( $act_status eq "down" ) {
883             $sql_statement = "DELETE FROM known_clients WHERE hostname='$address'";
884             $res = $known_clients_db->del_dbentry($sql_statement);
885             daemon_log("$session_id WARNING: failed 2x to send msg to host '$address', delete host from known_clients", 3);
886         } else { 
887             $sql_statement = "UPDATE known_clients SET status='$new_status' WHERE hostname='$address'";
888             $res = $known_clients_db->update_dbentry($sql_statement);
889             if($new_status eq "down"){
890                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
891             } else {
892                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
893             }
894         }
895     }
897     # known_server
898     $sql_statement = "SELECT * FROM known_server WHERE hostname='$address'";
899     $res = $known_server_db->select_dbentry($sql_statement);
900     if( keys(%$res) > 0 ) {
901         $act_status = $res->{1}->{'status'};
902         if( $act_status eq "down" ) {
903             $sql_statement = "DELETE FROM known_server WHERE hostname='$address'";
904             $res = $known_server_db->del_dbentry($sql_statement);
905             daemon_log("$session_id WARNING: failed 2x to a send msg to host '$address', delete host from known_server", 3);
906         } 
907         else { 
908             $sql_statement = "UPDATE known_server SET status='$new_status' WHERE hostname='$address'";
909             $res = $known_server_db->update_dbentry($sql_statement);
910             if($new_status eq "down"){
911                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
912             }
913             else {
914                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
915             }
916         }
917     }
918     return $error; 
922 sub update_jobdb_status_for_send_msgs {
923     my ($answer, $error) = @_;
924     if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
925         my $jobdb_id = $1;
926             
927         # sending msg faild
928         if( $error ) {
929             if (not $answer =~ /<header>trigger_action_reinstall<\/header>/) {
930                 my $sql_statement = "UPDATE $job_queue_tn ".
931                     "SET status='error', result='can not deliver msg, please consult log file' ".
932                     "WHERE id='$jobdb_id'";
933                 my $res = $job_db->update_dbentry($sql_statement);
934             }
936         # sending msg was successful
937         } else {
938             my $sql_statement = "UPDATE $job_queue_tn ".
939                 "SET status='done' ".
940                 "WHERE id='$jobdb_id' AND status='processed'";
941             my $res = $job_db->update_dbentry($sql_statement);
942         }
943     }
946 sub _start {
947     my ($kernel) = $_[KERNEL];
948     &trigger_db_loop($kernel);
949         #$kernel->yield('create_fai_server_db', $fai_server_tn );
950         #$kernel->yield('create_fai_release_db', $fai_release_tn );
951         $kernel->sig(USR1 => "sig_handler");
954 sub sig_handler {
955         my ($kernel, $signal) = @_[KERNEL, ARG0] ;
956         daemon_log("0 INFO got signal '$signal'", 1); 
957         $kernel->sig_handled();
958         return;
961 sub next_task {
962     my ($session, $heap) = @_[SESSION, HEAP];
964     while ( keys( %{ $heap->{task} } ) < $max_children ) {
965         my $next_task = shift @tasks;
966         last unless defined $next_task;
968         my $task = POE::Wheel::Run->new(
969                 Program => sub { process_task($session, $heap, $next_task) },
970                 StdioFilter => POE::Filter::Reference->new(),
971                 StdoutEvent  => "task_result",
972                 StderrEvent  => "task_debug",
973                 CloseEvent   => "task_done",
974                );
976         $heap->{task}->{ $task->ID } = $task;
977     }
980 sub handle_task_result {
981     my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
982     my $client_answer = $result->{'answer'};
983     if( $client_answer =~ s/session_id=(\d+)$// ) {
984         my $session_id = $1;
985         if( defined $session_id ) {
986             my $session_reference = $kernel->ID_id_to_session($session_id);
987             if( defined $session_reference ) {
988                 $heap = $session_reference->get_heap();
989             }
990         }
992         if(exists $heap->{'client'}) {
993             $heap->{'client'}->put($client_answer);
994         }
995     }
996     $kernel->sig(CHLD => "child_reap");
999 sub handle_task_debug {
1000     my $result = $_[ARG0];
1001     print STDERR "$result\n";
1004 sub handle_task_done {
1005     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1006     delete $heap->{task}->{$task_id};
1007     $kernel->yield("next_task");
1010 sub process_task {
1011     no strict "refs";
1012     my ($session, $heap, $input) = @_;
1013     my $session_id = $session->ID;
1014     my ($msg, $msg_hash, $module);
1015     my $error = 0;
1016     my $answer_l;
1017     my ($answer_header, @answer_target_l, $answer_source);
1018     my $client_answer = "";
1020     daemon_log("", 5); 
1021     daemon_log("$session_id INFO: Incoming msg with session ID $session_id from '".$heap->{'remote_ip'}."'", 5);
1022     daemon_log("$session_id DEBUG: Incoming msg:\n$input", 9);
1024     ####################
1025     # check incoming msg
1026     # msg is from a new client or gosa
1027     ($msg, $msg_hash, $module) = &input_from_unknown_host($input, $session_id);
1028     # msg is from a gosa-si-server or gosa-si-bus
1029     if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1030         ($msg, $msg_hash, $module) = &input_from_known_server($input, $heap->{'remote_ip'}, $session_id);
1031     }
1032     # msg is from a gosa-si-client
1033     if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1034         ($msg, $msg_hash, $module) = &input_from_known_client($input, $heap->{'remote_ip'}, $session_id);
1035     }
1036     # an error occurred
1037     if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1038         # if an incoming msg could not be decrypted (maybe a wrong key), send client a ping. If the client
1039         # could not understand a msg from its server the client cause a re-registering process
1040         my $sql_statement = "SELECT * FROM $main::known_clients_tn WHERE (hostname LIKE '".$heap->{'remote_ip'}."%')";
1041         my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
1042         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1043             my $host_name = $hit->{'hostname'};
1044             my $host_key = $hit->{'hostkey'};
1045             my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source> <target>$host_name</target></xml>";
1046             my $error = &send_msg_to_target($ping_msg, $host_name, $host_key, "gosa_ping", $session_id);
1047             &update_jobdb_status_for_send_msgs($ping_msg, $error);
1048         }
1049         $error++;
1050     }
1052     ######################
1053     # process incoming msg
1054     if( $error == 0) {
1055         daemon_log("$session_id INFO: Incoming msg with header '".@{$msg_hash->{'header'}}[0]."'", 5); 
1056         daemon_log("$session_id DEBUG: Processing module ".$module, 7);
1057         $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1059         if ( 0 < @{$answer_l} ) {
1060             my $answer_str = join("\n", @{$answer_l});
1061             daemon_log("$session_id DEBUG: $module: Got answer from module: \n".$answer_str,8);
1062         }
1063     }
1064     if( !$answer_l ) { $error++ };
1066     ########
1067     # answer
1068     if( $error == 0 ) {
1070         foreach my $answer ( @{$answer_l} ) {
1071             # for each answer in answer list
1072             
1073             # check outgoing msg to xml validity
1074             my $answer_hash = &check_outgoing_xml_validity($answer);
1075             if( not defined $answer_hash ) {
1076                 next;
1077             }
1078             
1079             $answer_header = @{$answer_hash->{'header'}}[0];
1080             @answer_target_l = @{$answer_hash->{'target'}};
1081             $answer_source = @{$answer_hash->{'source'}}[0];
1083             # deliver msg to all targets 
1084             foreach my $answer_target ( @answer_target_l ) {
1086                 # targets of msg are all gosa-si-clients in known_clients_db
1087                 if( $answer_target eq "*" ) {
1088                     # answer is for all clients
1089                     my $sql_statement= "SELECT * FROM known_clients";
1090                     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
1091                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1092                         my $host_name = $hit->{hostname};
1093                         my $host_key = $hit->{hostkey};
1094                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1095                         &update_jobdb_status_for_send_msgs($answer, $error);
1096                     }
1097                 }
1099                 # targets of msg are all gosa-si-server in known_server_db
1100                 elsif( $answer_target eq "KNOWN_SERVER" ) {
1101                     # answer is for all server in known_server
1102                     my $sql_statement= "SELECT * FROM known_server";
1103                     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
1104                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1105                         my $host_name = $hit->{hostname};
1106                         my $host_key = $hit->{hostkey};
1107                         $answer =~ s/KNOWN_SERVER/$host_name/g;
1108                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1109                         &update_jobdb_status_for_send_msgs($answer, $error);
1110                     }
1111                 }
1113                 # target of msg is GOsa
1114                                 elsif( $answer_target eq "GOSA" ) {
1115                                         my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1116                                         my $add_on = "";
1117                     if( defined $session_id ) {
1118                         $add_on = ".session_id=$session_id";
1119                     }
1120                     # answer is for GOSA and has to returned to connected client
1121                     my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1122                     $client_answer = $gosa_answer.$add_on;
1123                 }
1125                 # target of msg is job queue at this host
1126                 elsif( $answer_target eq "JOBDB") {
1127                     $answer =~ /<header>(\S+)<\/header>/;   
1128                     my $header;
1129                     if( defined $1 ) { $header = $1; }
1130                     my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1131                     &update_jobdb_status_for_send_msgs($answer, $error);
1132                 }
1134                 # target of msg is a mac address
1135                 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 ) {
1136                     daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients", 5);
1137                     my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1138                     my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1139                     my $found_ip_flag = 0;
1140                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1141                         my $host_name = $hit->{hostname};
1142                         my $host_key = $hit->{hostkey};
1143                         $answer =~ s/$answer_target/$host_name/g;
1144                         daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1145                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1146                         &update_jobdb_status_for_send_msgs($answer, $error);
1147                         $found_ip_flag++ ;
1148                     }   
1149                     if( $found_ip_flag == 0) {
1150                         daemon_log("$session_id WARNING: no host found in known_clients with mac address '$answer_target'", 3);
1151                         if( $bus_activ eq "true" ) { 
1152                             daemon_log("$session_id INFO: try to forward msg '$answer_header' to bus '$bus_address'", 5);
1153                             my $sql_statement = "SELECT * FROM known_server WHERE hostname='$bus_address'";
1154                             my $query_res = $known_server_db->select_dbentry( $sql_statement );
1155                             while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1156                                 my $bus_address = $hit->{hostname};
1157                                 my $bus_key = $hit->{hostkey};
1158                                 my $error = &send_msg_to_target($answer, $bus_address, $bus_key, $answer_header, $session_id);
1159                                 &update_jobdb_status_for_send_msgs($answer, $error);
1160                                 last;
1161                             }
1162                         }
1164                     }
1166                 #  answer is for one specific host   
1167                 } else {
1168                     # get encrypt_key
1169                     my $encrypt_key = &get_encrypt_key($answer_target);
1170                     if( not defined $encrypt_key ) {
1171                         # unknown target, forward msg to bus
1172                         daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1173                         if( $bus_activ eq "true" ) { 
1174                             daemon_log("$session_id INFO: try to forward msg '$answer_header' to bus '$bus_address'", 5);
1175                             my $sql_statement = "SELECT * FROM known_server WHERE hostname='$bus_address'";
1176                             my $query_res = $known_server_db->select_dbentry( $sql_statement );
1177                             my $res_length = keys( %{$query_res} );
1178                             if( $res_length == 0 ){
1179                                 daemon_log("$session_id WARNING: send '$answer_header' to '$bus_address' failed, ".
1180                                         "no bus found in known_server", 3);
1181                             }
1182                             else {
1183                                 while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1184                                     my $bus_key = $hit->{hostkey};
1185                                     my $error = &send_msg_to_target($answer, $bus_address, $bus_key, $answer_header,$session_id );
1186                                     &update_jobdb_status_for_send_msgs($answer, $error);
1187                                 }
1188                             }
1189                         }
1190                         next;
1191                     }
1192                     my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1193                     &update_jobdb_status_for_send_msgs($answer, $error);
1194                 }
1195             }
1196         }
1197     }
1199     my $filter = POE::Filter::Reference->new();
1200     my %result = ( 
1201             status => "seems ok to me",
1202             answer => $client_answer,
1203             );
1205     my $output = $filter->put( [ \%result ] );
1206     print @$output;
1212 sub trigger_db_loop {
1213         my ($kernel) = @_ ;
1214         $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1215     $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay); 
1218 sub watch_for_done_jobs {
1219     my ($kernel,$heap) = @_[KERNEL, HEAP];
1221     my $sql_statement = "SELECT * FROM ".$job_queue_tn.
1222         " WHERE status='done'";
1223         my $res = $job_db->select_dbentry( $sql_statement );
1225     while( my ($id, $hit) = each %{$res} ) {
1226         my $jobdb_id = $hit->{id};
1227         my $sql_statement = "DELETE FROM $job_queue_tn WHERE id='$jobdb_id'"; 
1228         my $res = $job_db->del_dbentry($sql_statement);
1229     }
1231     $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1234 sub watch_for_new_jobs {
1235         my ($kernel,$heap) = @_[KERNEL, HEAP];
1237         # check gosa job queue for jobs with executable timestamp
1238     my $timestamp = &get_time();
1239     my $sql_statement = "SELECT * FROM ".$job_queue_tn.
1240         " WHERE status='waiting' AND timestamp<'$timestamp'";
1241         my $res = $job_db->select_dbentry( $sql_statement );
1243         while( my ($id, $hit) = each %{$res} ) {         
1244                 my $jobdb_id = $hit->{id};
1245                 my $macaddress = $hit->{'macaddress'};
1246         my $job_msg = $hit->{'xmlmessage'};
1247         daemon_log("J DEBUG: its time to execute $job_msg", 7); 
1248         my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1249                 my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1250                 # expect macaddress is unique!!!!!!
1251                 my $target = $res_hash->{1}->{hostname};
1253 #        if (not defined $target) {
1254 #                       &daemon_log("ERROR: no host found for mac address: $macaddress", 1);
1255 #                       &daemon_log("$hit->{xmlmessage}", 8);
1256 #            my $sql_statement = "UPDATE $job_queue_tn ".
1257 #                "SET status='error', result='no host found for mac address' ".
1258 #                "WHERE id='$jobdb_id'";
1259 #                       my $res = $job_db->update_dbentry($sql_statement);
1260 #                       next;
1261 #               }
1263                 # change header
1264         $job_msg =~ s/<header>job_/<header>gosa_/;
1266                 # add sqlite_id 
1267         $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1269         $job_msg =~ /<header>(\S+)<\/header>/;
1270         my $header = $1 ;
1271                 my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");
1273         # update status in job queue to 'processing'
1274         $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id='$jobdb_id'";
1275         my $res = $job_db->update_dbentry($sql_statement);
1276     }
1278         $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1282 sub refresh_ldap_handle {
1283   my $mesg;
1285   # Get an ldap handle, if we don't have one
1286   if( ! defined $ldap_handle ){
1287           $ldap_handle = Net::LDAP->new( $ldap_uri );
1288   }
1289   # Still not defined?
1290   if( ! defined $ldap_handle ) {
1291           daemon_log( "ch $$: Net::LDAP constructor failed: $!\n" );
1292           return 0;
1293   }
1295   # Bind to ldap server - eventually authenticate
1296   if( defined $ldap_admin_dn ) {
1297     if( defined $ldap_admin_password ) {
1298       $mesg = $ldap_handle->bind( $ldap_admin_dn, password => $ldap_admin_password );
1299     } else {
1300       $mesg = $ldap_handle->bind( $ldap_admin_dn );
1301     }
1302   } else {
1303     $mesg = $ldap_handle->bind();
1304   }
1306   if( 0 != $mesg->code ) {
1307     undef( $ldap_handle ) if( 81 == $mesg->code );
1308     daemon_log( "ch $$: LDAP bind: error (". $mesg->code . ') - ' . $mesg->error . "\n", 1);
1309     return 0;
1310   }
1312   return 1;
1316 sub change_fai_state {
1317     my ($st, $targets, $session_id) = @_;
1318     $session_id = 0 if not defined $session_id;
1319     # Set FAI state to localboot
1320     my %mapActions= (
1321         reboot    => '',
1322         update    => 'softupdate',
1323         localboot => 'localboot',
1324         reinstall => 'install',
1325         rescan    => '',
1326         wake      => '',
1327         memcheck  => 'memcheck',
1328         sysinfo   => 'sysinfo',
1329                 install   => 'install',
1330     );
1332     # Return if this is unknown
1333     if (!exists $mapActions{ $st }){
1334         daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1); 
1335       return;
1336     }
1338     my $state= $mapActions{ $st };
1340     &refresh_ldap_handle();
1341     if( defined($ldap_handle) ) {
1343       # Build search filter for hosts
1344         my $search= "(&(objectClass=GOhard)";
1345         foreach (@{$targets}){
1346             $search.= "(macAddress=$_)";
1347         }
1348         $search.= ")";
1350       # If there's any host inside of the search string, procress them
1351         if (!($search =~ /macAddress/)){
1352             daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);    
1353             return;
1354         }
1356       # Perform search for Unit Tag
1357       my $mesg = $ldap_handle->search(
1358           base   => $ldap_base,
1359           scope  => 'sub',
1360           attrs  => ['dn', 'FAIstate', 'objectClass'],
1361           filter => "$search"
1362           );
1364       if ($mesg->count) {
1365         my @entries = $mesg->entries;
1366         foreach my $entry (@entries) {
1367           # Only modify entry if it is not set to '$state'
1368           if ($entry->get_value("FAIstate") ne "$state"){
1369             daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
1370             my $result;
1371             my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
1372             if (exists $tmp{'FAIobject'}){
1373               if ($state eq ''){
1374                 $result= $ldap_handle->modify($entry->dn, changes => [
1375                             delete => [ FAIstate => [] ] ]);
1376               } else {
1377                 $result= $ldap_handle->modify($entry->dn, changes => [
1378                             replace => [ FAIstate => $state ] ]);
1379               }
1380             } elsif ($state ne ''){
1381               $result= $ldap_handle->modify($entry->dn, changes => [
1382                           add     => [ objectClass => 'FAIobject' ],
1383                           add     => [ FAIstate => $state ] ]);
1384             }
1386             # Errors?
1387             if ($result->code){
1388               daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
1389             }
1391           } else {
1392             daemon_log("$session_id DEBUG FAIstate at host found with filter statement '$search' already at state '$st'", 7); 
1393           }  
1394         }
1395       }
1396     # if no ldap handle defined
1397     } else {
1398         daemon_log("$session_id ERROR: no LDAP handle defined for update FAIstate", 1); 
1399     }
1403 sub change_goto_state {
1404     my ($st, $targets, $session_id) = @_;
1405     $session_id = 0  if not defined $session_id;
1407     # Switch on or off?
1408     my $state= $st eq 'active' ? 'active': 'locked';
1410     &refresh_ldap_handle();
1411     if( defined($ldap_handle) ) {
1413       # Build search filter for hosts
1414       my $search= "(&(objectClass=GOhard)";
1415       foreach (@{$targets}){
1416         $search.= "(macAddress=$_)";
1417       }
1418       $search.= ")";
1420       # If there's any host inside of the search string, procress them
1421       if (!($search =~ /macAddress/)){
1422         return;
1423       }
1425       # Perform search for Unit Tag
1426       my $mesg = $ldap_handle->search(
1427           base   => $ldap_base,
1428           scope  => 'sub',
1429           attrs  => ['dn', 'gotoMode'],
1430           filter => "$search"
1431           );
1433       if ($mesg->count) {
1434         my @entries = $mesg->entries;
1435         foreach my $entry (@entries) {
1437           # Only modify entry if it is not set to '$state'
1438           if ($entry->get_value("gotoMode") ne $state){
1440             daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
1441             my $result;
1442             $result= $ldap_handle->modify($entry->dn, changes => [
1443                                                 replace => [ gotoMode => $state ] ]);
1445             # Errors?
1446             if ($result->code){
1447               &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
1448             }
1450           }
1451         }
1452       }
1454     }
1458 sub create_fai_server_db {
1459     my ($table_name, $kernel) = @_;
1460         my $result;
1462         if(defined($ldap_handle)) {
1463                 daemon_log("INFO: create_fai_server_db: start", 5);
1464                 my $mesg= $ldap_handle->search(
1465                         base   => $ldap_base,
1466                         scope  => 'sub',
1467                         attrs  => ['FAIrepository', 'gosaUnitTag'],
1468                         filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
1469                 );
1470                 if($mesg->{'resultCode'} == 0 &&
1471                    $mesg->count != 0) {
1472                    foreach my $entry (@{$mesg->{entries}}) {
1473                            if($entry->exists('FAIrepository')) {
1474                                    # Add an entry for each Repository configured for server
1475                                    foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
1476                                                    my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
1477                                                    my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
1478                                                    $result= $fai_server_db->add_dbentry( { 
1479                                                                    table => $table_name,
1480                                                                    primkey => ['server', 'release', 'tag'],
1481                                                                    server => $tmp_url,
1482                                                                    release => $tmp_release,
1483                                                                    sections => $tmp_sections,
1484                                                                    tag => (length($tmp_tag)>0)?$tmp_tag:"",
1485                                                            } );
1486                                            }
1487                                    }
1488                            }
1489                    }
1490                 daemon_log("INFO: create_fai_server_db: finished", 5);
1492                 # TODO: Find a way to post the 'create_packages_list_db' event
1493                 &create_packages_list_db();
1494         }       
1496         return $result;
1499 sub run_create_fai_server_db {
1500     my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
1501     my $task = POE::Wheel::Run->new(
1502             Program => sub { &create_fai_server_db($table_name,$kernel) },
1503             StdoutEvent  => "session_run_result",
1504             StderrEvent  => "session_run_debug",
1505             CloseEvent   => "session_run_done",
1506             );
1508     $heap->{task}->{ $task->ID } = $task;
1509     return;
1513 sub create_fai_release_db {
1514         my ($table_name) = @_;
1515         my $result;
1517         if(defined($ldap_handle)) {
1518                 daemon_log("INFO: create_fai_release_db: start",5);
1519                 my $mesg= $ldap_handle->search(
1520                         base   => $ldap_base,
1521                         scope  => 'sub',
1522                         attrs  => [],
1523                         filter => "(&(objectClass=organizationalUnit)(ou=fai))",
1524                 );
1525                 if($mesg->{'resultCode'} == 0 &&
1526                         $mesg->count != 0) {
1527                         # Walk through all possible FAI container ou's
1528                         my @sql_list;
1529                         my $timestamp= &get_time();
1530                         foreach my $ou (@{$mesg->{entries}}) {
1531                                 my $tmp_classes= resolve_fai_classes($ou->dn);
1532                                 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
1533                                         my @tmp_array=get_fai_release_entries($tmp_classes);
1534                                         if(@tmp_array) {
1535                                                 foreach my $entry (@tmp_array) {
1536                                                         if(defined($entry) && ref($entry) eq 'HASH') {
1537                                                                 my $sql= 
1538                                                                 "INSERT INTO $table_name "
1539                                                                 ."(timestamp, release, class, type, state) VALUES ("
1540                                                                 .$timestamp.","
1541                                                                 ."'".$entry->{'release'}."',"
1542                                                                 ."'".$entry->{'class'}."',"
1543                                                                 ."'".$entry->{'type'}."',"
1544                                                                 ."'".$entry->{'state'}."')";
1545                                                                 push @sql_list, $sql;
1546                                                         }
1547                                                 }
1548                                         }
1549                                 }
1550                         }
1551                         daemon_log("DEBUG: Inserting ".scalar @sql_list." entries to DB",6);
1552                         if(@sql_list) {
1553                                 unshift @sql_list, "DELETE FROM $table_name";
1554                                 $fai_server_db->exec_statementlist(\@sql_list);
1555                         }
1556                         daemon_log("DEBUG: Done with inserting",6);
1557                 }
1558                 daemon_log("INFO: create_fai_release_db: finished",5);
1559         }
1561         return $result;
1563 sub run_create_fai_release_db {
1564     my ($session, $heap, $table_name) = @_[SESSION, HEAP, ARG0];
1565     my $task = POE::Wheel::Run->new(
1566             Program => sub { &create_fai_release_db($table_name) },
1567             StdoutEvent  => "session_run_result",
1568             StderrEvent  => "session_run_debug",
1569             CloseEvent   => "session_run_done",
1570             );
1572     $heap->{task}->{ $task->ID } = $task;
1573     return;
1576 sub get_fai_types {
1577         my $tmp_classes = shift || return undef;
1578         my @result;
1580         foreach my $type(keys %{$tmp_classes}) {
1581                 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
1582                         my $entry = {
1583                                 type => $type,
1584                                 state => $tmp_classes->{$type}[0],
1585                         };
1586                         push @result, $entry;
1587                 }
1588         }
1590         return @result;
1593 sub get_fai_state {
1594         my $result = "";
1595         my $tmp_classes = shift || return $result;
1597         foreach my $type(keys %{$tmp_classes}) {
1598                 if(defined($tmp_classes->{$type}[0])) {
1599                         $result = $tmp_classes->{$type}[0];
1600                         
1601                 # State is equal for all types in class
1602                         last;
1603                 }
1604         }
1606         return $result;
1609 sub resolve_fai_classes {
1610         my $result;
1611         my $fai_base= shift;
1612         my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
1613         my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
1614         my $fai_classes;
1616         daemon_log("DEBUG: Searching for FAI entries in base $fai_base",6);
1617         my $mesg= $ldap_handle->search(
1618                 base   => $fai_base,
1619                 scope  => 'sub',
1620                 attrs  => ['cn','objectClass','FAIstate'],
1621                 filter => $fai_filter,
1622         );
1623         daemon_log("DEBUG: Found ".$mesg->count()." FAI entries",6);
1625         if($mesg->{'resultCode'} == 0 &&
1626                 $mesg->count != 0) {
1627                 foreach my $entry (@{$mesg->{entries}}) {
1628                         if($entry->exists('cn')) {
1629                                 my $tmp_dn= $entry->dn();
1631                                 # Skip classname and ou dn parts for class
1632                                 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?),$fai_base$/;
1634                                 # Skip classes without releases
1635                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
1636                                         next;
1637                                 }
1639                                 my $tmp_cn= $entry->get_value('cn');
1640                                 my $tmp_state= $entry->get_value('FAIstate');
1642                                 my $tmp_type;
1643                                 # Get FAI type
1644                                 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
1645                                         if(grep $_ eq $oclass, @possible_fai_classes) {
1646                                                 $tmp_type= $oclass;
1647                                                 last;
1648                                         }
1649                                 }
1651                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
1652                                         # A Subrelease
1653                                         my @sub_releases = split(/,/, $tmp_release);
1655                                         # Walk through subreleases and build hash tree
1656                                         my $hash;
1657                                         while(my $tmp_sub_release = pop @sub_releases) {
1658                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
1659                                         }
1660                                         eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
1661                                 } else {
1662                                         # A branch, no subrelease
1663                                         push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
1664                                 }
1665                         } elsif (!$entry->exists('cn')) {
1666                                 my $tmp_dn= $entry->dn();
1667                                 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?),$fai_base$/;
1669                                 # Skip classes without releases
1670                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
1671                                         next;
1672                                 }
1674                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
1675                                         # A Subrelease
1676                                         my @sub_releases= split(/,/, $tmp_release);
1678                                         # Walk through subreleases and build hash tree
1679                                         my $hash;
1680                                         while(my $tmp_sub_release = pop @sub_releases) {
1681                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
1682                                         }
1683                                         # Remove the last two characters
1684                                         chop($hash);
1685                                         chop($hash);
1687                                         eval('$fai_classes->'.$hash.'= {}');
1688                                 } else {
1689                                         # A branch, no subrelease
1690                                         if(!exists($fai_classes->{$tmp_release})) {
1691                                                 $fai_classes->{$tmp_release} = {};
1692                                         }
1693                                 }
1694                         }
1695                 }
1697                 # The hash is complete, now we can honor the copy-on-write based missing entries
1698                 foreach my $release (keys %$fai_classes) {
1699                         $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
1700                 }
1701         }
1702         return $result;
1705 sub apply_fai_inheritance {
1706        my $fai_classes = shift || return {};
1707        my $tmp_classes;
1709        # Get the classes from the branch
1710        foreach my $class (keys %{$fai_classes}) {
1711                # Skip subreleases
1712                if($class =~ /^ou=.*$/) {
1713                        next;
1714                } else {
1715                        $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
1716                }
1717        }
1719        # Apply to each subrelease
1720        foreach my $subrelease (keys %{$fai_classes}) {
1721                if($subrelease =~ /ou=/) {
1722                        foreach my $tmp_class (keys %{$tmp_classes}) {
1723                                if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
1724                                        $fai_classes->{$subrelease}->{$tmp_class} =
1725                                        deep_copy($tmp_classes->{$tmp_class});
1726                                } else {
1727                                        foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
1728                                                if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
1729                                                        $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
1730                                                        deep_copy($tmp_classes->{$tmp_class}->{$type});
1731                                                }
1732                                        }
1733                                }
1734                        }
1735                }
1736        }
1738        # Find subreleases in deeper levels
1739        foreach my $subrelease (keys %{$fai_classes}) {
1740                if($subrelease =~ /ou=/) {
1741                        foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
1742                                if($subsubrelease =~ /ou=/) {
1743                                        apply_fai_inheritance($fai_classes->{$subrelease});
1744                                }
1745                        }
1746                }
1747        }
1749        return $fai_classes;
1752 sub get_fai_release_entries {
1753         my $tmp_classes = shift || return;
1754         my $parent = shift || "";
1755         my @result = shift || ();
1757         foreach my $entry (keys %{$tmp_classes}) {
1758                 if(defined($entry)) {
1759                         if($entry =~ /^ou=.*$/) {
1760                                 my $release_name = $entry;
1761                                 $release_name =~ s/ou=//g;
1762                                 if(length($parent)>0) {
1763                                         $release_name = $parent."/".$release_name;
1764                                 }
1765                                 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
1766                                 foreach my $bufentry(@bufentries) {
1767                                         push @result, $bufentry;
1768                                 }
1769                         } else {
1770                                 my @types = get_fai_types($tmp_classes->{$entry});
1771                                 foreach my $type (@types) {
1772                                         push @result, 
1773                                         {
1774                                                 'class' => $entry,
1775                                                 'type' => $type->{'type'},
1776                                                 'release' => $parent,
1777                                                 'state' => $type->{'state'},
1778                                         };
1779                                 }
1780                         }
1781                 }
1782         }
1784         return @result;
1787 sub deep_copy {
1788         my $this = shift;
1789         if (not ref $this) {
1790                 $this;
1791         } elsif (ref $this eq "ARRAY") {
1792                 [map deep_copy($_), @$this];
1793         } elsif (ref $this eq "HASH") {
1794                 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
1795         } else { die "what type is $_?" }
1799 sub session_run_result {
1800     my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];    
1801     $kernel->sig(CHLD => "child_reap");
1804 sub session_run_debug {
1805     my $result = $_[ARG0];
1806     print STDERR "$result\n";
1809 sub session_run_done {
1810     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1811     delete $heap->{task}->{$task_id};
1814 sub create_sources_list {
1815         my $result="/tmp/gosa_si_tmp_sources_list";
1817         # Remove old file
1818         if(stat($result)) {
1819                 unlink($result);
1820         }
1822         my $fh;
1823         open($fh, ">$result") or return undef;
1824         if(defined($ldap_server_dn) && length($ldap_server_dn)>0) {
1825                 my $mesg=$ldap_handle->search(
1826                         base    => $ldap_server_dn,
1827                         scope   => 'base',
1828                         attrs   => 'FAIrepository',
1829                 );
1831                 if($mesg->count) {
1832                         foreach my $entry(@{$mesg->{'entries'}}) {
1833                                 my ($server, $tag, $release, $sections)= split /\|/, $entry->get_value('FAIrepository');
1834                                 my $line = "deb $server $release";
1835                                 $sections =~ s/,/ /g;
1836                                 $line.= " $sections";
1837                                 print $fh $line."\n";
1838                         }
1839                 }
1840         }
1841         close($fh);
1843         return $result;
1846 sub create_packages_list_db {
1847     my ($sources_file) = @_ || &create_sources_list;
1848     my $line;
1849     daemon_log("INFO: create_packages_list_db: start", 5); 
1851     open(CONFIG, "<$sources_file") or do {
1852         daemon_log( "ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
1853         return;
1854     };
1855     
1856     # Read lines
1857     while ($line = <CONFIG>){
1858         # Unify
1859         chop($line);
1860         $line =~ s/^\s+//;
1861         $line =~ s/^\s+/ /;
1863         # Strip comments
1864         $line =~ s/#.*$//g;
1866         # Skip empty lines
1867         if ($line =~ /^\s*$/){
1868             next;
1869         }
1871         # Interpret deb line
1872         if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
1873             my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
1874             my $section;
1875             foreach $section (split(' ', $sections)){
1876                 &parse_package_info( $baseurl, $dist, $section );
1877             }
1878         }
1879     }
1881     close (CONFIG);
1883     daemon_log("INFO: create_packages_list_db: finished", 5); 
1884     return;
1886 sub run_create_packages_list_db {
1887     my ($session, $heap) = @_[SESSION, HEAP];
1888     my $task = POE::Wheel::Run->new(
1889             Program => sub {&create_packages_list_db},
1890             StdoutEvent  => "session_run_result",
1891             StderrEvent  => "session_run_debug",
1892             CloseEvent   => "session_run_done",
1893             );
1894     $heap->{task}->{ $task->ID } = $task;
1896 sub parse_package_info {
1897   my ($baseurl, $dist, $section)= @_;
1898   my ($package);
1900   my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
1902   foreach $package ("Packages.gz"){
1903     daemon_log("DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
1904     get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section" );
1905     parse_package( "$outdir/$dist/$section", $dist, $path );
1906   }
1908 sub get_package {
1909   my ($url, $dest)= @_;
1911   my $tpath = dirname($dest);
1912   -d "$tpath" || mkpath "$tpath";
1914   # This is ugly, but I've no time to take a look at "how it works in perl"
1915   if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
1916       system("gunzip -cd '$dest' > '$dest.in'");
1917       unlink($dest);
1918   } else {
1919       daemon_log("ERROR: create_packages_list_db: get_packages: fetching '$url' failed!", 1);
1920   }
1921   return 0;
1923 sub parse_package {
1924     my ($path, $dist, $srv_path )= @_;
1925     my ($package, $version, $section, $description);
1926     my @sql_list;
1927     my $PACKAGES;
1929     if(not stat("$path.in")) {
1930         daemon_log("ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
1931         return;
1932     }
1934     open($PACKAGES, "<$path.in");
1935         if(not defined($PACKAGES)) {
1936         daemon_log("ERROR: create_packages_list_db: parse_package: can not open '$path.in'",1); 
1937         return;
1938     }
1940     # Read lines
1941     while (<$PACKAGES>){
1942         my $line = $_;
1943         # Unify
1944         chop($line);
1946         # Use empty lines as a trigger
1947         if ($line =~ /^\s*$/){
1948             my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '', 'none', '0')";
1949             push(@sql_list, $sql);
1950             $package = "none";
1951             $version = "none";
1952             $section = "none";
1953             $description = "none"; 
1954             next;
1955         }
1957         # Trigger for package name
1958         if ($line =~ /^Package:\s/){
1959             ($package)= ($line =~ /^Package: (.*)$/);
1960             next;
1961         }
1963         # Trigger for version
1964         if ($line =~ /^Version:\s/){
1965             ($version)= ($line =~ /^Version: (.*)$/);
1966             next;
1967         }
1969         # Trigger for description
1970         if ($line =~ /^Description:\s/){
1971             ($description)= ($line =~ /^Description: (.*)$/);
1972             next;
1973         }
1975         # Trigger for section
1976         if ($line =~ /^Section:\s/){
1977             ($section)= ($line =~ /^Section: (.*)$/);
1978             next;
1979         }
1981     }
1983     close( $PACKAGES );
1984     unlink( "$path.in" );
1985     
1986     $packages_list_db->exec_statementlist(\@sql_list);
1990 sub store_fileinfo {
1991                 my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
1993                 my %fileinfo = (
1994                                                 'package' => $package,
1995                                                 'dist' => $dist,
1996                                                 'version' => $vers,
1997                                            );
1999                 $repo_files{ "${srvdir}/$file" } = \%fileinfo;
2003 sub cleanup_and_extract {
2004   my $fileinfo = $repo_files{ $File::Find::name };
2006   if( defined $fileinfo ) {
2008     my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
2009     my $sql;
2010     my $package = $fileinfo->{ 'package' };
2011     my $newver = $fileinfo->{ 'version' };
2013     mkpath($dir);
2014     system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
2016     if( -f "$dir/DEBIAN/templates" ) {
2018       daemon_log("DEBUG: Found debconf templates in '$package' - $newver", 5);
2020       my $tmpl= "";
2021       {
2022           local $/=undef;
2023           open FILE, "$dir/DEBIAN/templates";
2024           $tmpl = &encode_base64(<FILE>);
2025           close FILE;
2026       }
2027       rmtree("$dir/DEBIAN/templates");
2029       $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
2031     } else {
2032       $sql= "update $main::packages_list_tn set template = '' where package = '$package' and version = '$newver';";
2033     }
2035     my $res= $main::packages_list_db->update_dbentry($sql);
2036   }
2040 #==== MAIN = main ==============================================================
2041 #  parse commandline options
2042 Getopt::Long::Configure( "bundling" );
2043 GetOptions("h|help" => \&usage,
2044         "c|config=s" => \$cfg_file,
2045         "f|foreground" => \$foreground,
2046         "v|verbose+" => \$verbose,
2047         "no-bus+" => \$no_bus,
2048         "no-arp+" => \$no_arp,
2049            );
2051 #  read and set config parameters
2052 &check_cmdline_param ;
2053 &read_configfile;
2054 &check_pid;
2056 $SIG{CHLD} = 'IGNORE';
2058 # forward error messages to logfile
2059 if( ! $foreground ) {
2060   open( STDIN,  '+>/dev/null' );
2061   open( STDOUT, '+>&STDIN'    );
2062   open( STDERR, '+>&STDIN'    );
2065 # Just fork, if we are not in foreground mode
2066 if( ! $foreground ) { 
2067     chdir '/'                 or die "Can't chdir to /: $!";
2068     $pid = fork;
2069     setsid                    or die "Can't start a new session: $!";
2070     umask 0;
2071 } else { 
2072     $pid = $$; 
2075 # Do something useful - put our PID into the pid_file
2076 if( 0 != $pid ) {
2077     open( LOCK_FILE, ">$pid_file" );
2078     print LOCK_FILE "$pid\n";
2079     close( LOCK_FILE );
2080     if( !$foreground ) { 
2081         exit( 0 ) 
2082     };
2085 daemon_log(" ", 1);
2086 daemon_log("$0 started!", 1);
2088 if ($no_bus > 0) {
2089     $bus_activ = "false"
2094 # delete old DBsqlite lock files
2095 #unlink('/tmp/gosa_si_lock*');
2097 # connect to gosa-si job queue
2098 $job_db = GOSA::DBsqlite->new($job_queue_file_name);
2099 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
2101 # connect to known_clients_db
2102 $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
2103 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
2105 # connect to known_server_db
2106 $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
2107 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
2109 # connect to login_usr_db
2110 $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
2111 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
2113 # connect to fai_server_db and fai_release_db
2114 $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
2115 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
2116 $fai_server_db->create_table($fai_release_tn, \@fai_release_col_names);
2118 # connect to packages_list_db
2119 unlink($packages_list_file_name);
2120 $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
2121 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
2123 # connect to messaging_db
2124 $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
2125 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
2128 # create xml object used for en/decrypting
2129 $xml = new XML::Simple();
2131 # create socket for incoming xml messages
2133 POE::Component::Server::TCP->new(
2134         Port => $server_port,
2135         ClientInput => sub {
2136         my ($kernel, $input) = @_[KERNEL, ARG0];
2137         push(@tasks, $input);
2138         $kernel->yield("next_task");
2139         },
2140     InlineStates => {
2141         next_task => \&next_task,
2142         task_result => \&handle_task_result,
2143         task_done   => \&handle_task_done,
2144         task_debug  => \&handle_task_debug,
2145         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
2146     }
2147 );
2149 daemon_log("start socket for incoming xml messages at port '$server_port' ", 1);
2151 # create session for repeatedly checking the job queue for jobs
2152 POE::Session->create(
2153         inline_states => {
2154                 _start => \&_start,
2155                 sig_handler => \&sig_handler,
2156                 watch_for_new_jobs => \&watch_for_new_jobs,
2157         watch_for_done_jobs => \&watch_for_done_jobs,
2158         create_packages_list_db => \&run_create_packages_list_db,
2159         create_fai_server_db => \&run_create_fai_server_db,
2160         create_fai_release_db => \&run_create_fai_release_db,
2161         session_run_result => \&session_run_result,
2162         session_run_debug => \&session_run_debug,
2163         session_run_done => \&session_run_done,
2164         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
2165         }
2166 );
2169 # import all modules
2170 &import_modules;
2172 # check wether all modules are gosa-si valid passwd check
2174 POE::Kernel->run();
2175 exit;