Code

add job entries if missing while installation
[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     );
1331     # Return if this is unknown
1332     if (!exists $mapActions{ $st }){
1333         daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1); 
1334       return;
1335     }
1337     my $state= $mapActions{ $st };
1339     &refresh_ldap_handle();
1340     if( defined($ldap_handle) ) {
1342       # Build search filter for hosts
1343         my $search= "(&(objectClass=GOhard)";
1344         foreach (@{$targets}){
1345             $search.= "(macAddress=$_)";
1346         }
1347         $search.= ")";
1349       # If there's any host inside of the search string, procress them
1350         if (!($search =~ /macAddress/)){
1351             daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);    
1352             return;
1353         }
1355       # Perform search for Unit Tag
1356       my $mesg = $ldap_handle->search(
1357           base   => $ldap_base,
1358           scope  => 'sub',
1359           attrs  => ['dn', 'FAIstate', 'objectClass'],
1360           filter => "$search"
1361           );
1363       if ($mesg->count) {
1364         my @entries = $mesg->entries;
1365         foreach my $entry (@entries) {
1366           # Only modify entry if it is not set to '$state'
1367           if ($entry->get_value("FAIstate") ne "$state"){
1368             daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
1369             my $result;
1370             my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
1371             if (exists $tmp{'FAIobject'}){
1372               if ($state eq ''){
1373                 $result= $ldap_handle->modify($entry->dn, changes => [
1374                             delete => [ FAIstate => [] ] ]);
1375               } else {
1376                 $result= $ldap_handle->modify($entry->dn, changes => [
1377                             replace => [ FAIstate => $state ] ]);
1378               }
1379             } elsif ($state ne ''){
1380               $result= $ldap_handle->modify($entry->dn, changes => [
1381                           add     => [ objectClass => 'FAIobject' ],
1382                           add     => [ FAIstate => $state ] ]);
1383             }
1385             # Errors?
1386             if ($result->code){
1387               daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
1388             }
1390           } else {
1391             daemon_log("$session_id DEBUG FAIstate at host found with filter statement '$search' already at state '$st'", 7); 
1392           }  
1393         }
1394       }
1395     # if no ldap handle defined
1396     } else {
1397         daemon_log("$session_id ERROR: no LDAP handle defined for update FAIstate", 1); 
1398     }
1402 sub change_goto_state {
1403     my ($st, $targets, $session_id) = @_;
1404     $session_id = 0  if not defined $session_id;
1406     # Switch on or off?
1407     my $state= $st eq 'active' ? 'active': 'locked';
1409     &refresh_ldap_handle();
1410     if( defined($ldap_handle) ) {
1412       # Build search filter for hosts
1413       my $search= "(&(objectClass=GOhard)";
1414       foreach (@{$targets}){
1415         $search.= "(macAddress=$_)";
1416       }
1417       $search.= ")";
1419       # If there's any host inside of the search string, procress them
1420       if (!($search =~ /macAddress/)){
1421         return;
1422       }
1424       # Perform search for Unit Tag
1425       my $mesg = $ldap_handle->search(
1426           base   => $ldap_base,
1427           scope  => 'sub',
1428           attrs  => ['dn', 'gotoMode'],
1429           filter => "$search"
1430           );
1432       if ($mesg->count) {
1433         my @entries = $mesg->entries;
1434         foreach my $entry (@entries) {
1436           # Only modify entry if it is not set to '$state'
1437           if ($entry->get_value("gotoMode") ne $state){
1439             daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
1440             my $result;
1441             $result= $ldap_handle->modify($entry->dn, changes => [
1442                                                 replace => [ gotoMode => $state ] ]);
1444             # Errors?
1445             if ($result->code){
1446               &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
1447             }
1449           }
1450         }
1451       }
1453     }
1457 sub create_fai_server_db {
1458     my ($table_name, $kernel) = @_;
1459         my $result;
1461         if(defined($ldap_handle)) {
1462                 daemon_log("INFO: create_fai_server_db: start", 5);
1463                 my $mesg= $ldap_handle->search(
1464                         base   => $ldap_base,
1465                         scope  => 'sub',
1466                         attrs  => ['FAIrepository', 'gosaUnitTag'],
1467                         filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
1468                 );
1469                 if($mesg->{'resultCode'} == 0 &&
1470                    $mesg->count != 0) {
1471                    foreach my $entry (@{$mesg->{entries}}) {
1472                            if($entry->exists('FAIrepository')) {
1473                                    # Add an entry for each Repository configured for server
1474                                    foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
1475                                                    my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
1476                                                    my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
1477                                                    $result= $fai_server_db->add_dbentry( { 
1478                                                                    table => $table_name,
1479                                                                    primkey => ['server', 'release', 'tag'],
1480                                                                    server => $tmp_url,
1481                                                                    release => $tmp_release,
1482                                                                    sections => $tmp_sections,
1483                                                                    tag => (length($tmp_tag)>0)?$tmp_tag:"",
1484                                                            } );
1485                                            }
1486                                    }
1487                            }
1488                    }
1489                 daemon_log("INFO: create_fai_server_db: finished", 5);
1491                 # TODO: Find a way to post the 'create_packages_list_db' event
1492                 &create_packages_list_db();
1493         }       
1495         return $result;
1498 sub run_create_fai_server_db {
1499     my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
1500     my $task = POE::Wheel::Run->new(
1501             Program => sub { &create_fai_server_db($table_name,$kernel) },
1502             StdoutEvent  => "session_run_result",
1503             StderrEvent  => "session_run_debug",
1504             CloseEvent   => "session_run_done",
1505             );
1507     $heap->{task}->{ $task->ID } = $task;
1508     return;
1512 sub create_fai_release_db {
1513         my ($table_name) = @_;
1514         my $result;
1516         if(defined($ldap_handle)) {
1517                 daemon_log("INFO: create_fai_release_db: start",5);
1518                 my $mesg= $ldap_handle->search(
1519                         base   => $ldap_base,
1520                         scope  => 'sub',
1521                         attrs  => [],
1522                         filter => "(&(objectClass=organizationalUnit)(ou=fai))",
1523                 );
1524                 if($mesg->{'resultCode'} == 0 &&
1525                         $mesg->count != 0) {
1526                         # Walk through all possible FAI container ou's
1527                         my @sql_list;
1528                         my $timestamp= &get_time();
1529                         foreach my $ou (@{$mesg->{entries}}) {
1530                                 my $tmp_classes= resolve_fai_classes($ou->dn);
1531                                 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
1532                                         my @tmp_array=get_fai_release_entries($tmp_classes);
1533                                         if(@tmp_array) {
1534                                                 foreach my $entry (@tmp_array) {
1535                                                         if(defined($entry) && ref($entry) eq 'HASH') {
1536                                                                 my $sql= 
1537                                                                 "INSERT INTO $table_name "
1538                                                                 ."(timestamp, release, class, type, state) VALUES ("
1539                                                                 .$timestamp.","
1540                                                                 ."'".$entry->{'release'}."',"
1541                                                                 ."'".$entry->{'class'}."',"
1542                                                                 ."'".$entry->{'type'}."',"
1543                                                                 ."'".$entry->{'state'}."')";
1544                                                                 push @sql_list, $sql;
1545                                                         }
1546                                                 }
1547                                         }
1548                                 }
1549                         }
1550                         daemon_log("DEBUG: Inserting ".scalar @sql_list." entries to DB",6);
1551                         if(@sql_list) {
1552                                 unshift @sql_list, "DELETE FROM $table_name";
1553                                 $fai_server_db->exec_statementlist(\@sql_list);
1554                         }
1555                         daemon_log("DEBUG: Done with inserting",6);
1556                 }
1557                 daemon_log("INFO: create_fai_release_db: finished",5);
1558         }
1560         return $result;
1562 sub run_create_fai_release_db {
1563     my ($session, $heap, $table_name) = @_[SESSION, HEAP, ARG0];
1564     my $task = POE::Wheel::Run->new(
1565             Program => sub { &create_fai_release_db($table_name) },
1566             StdoutEvent  => "session_run_result",
1567             StderrEvent  => "session_run_debug",
1568             CloseEvent   => "session_run_done",
1569             );
1571     $heap->{task}->{ $task->ID } = $task;
1572     return;
1575 sub get_fai_types {
1576         my $tmp_classes = shift || return undef;
1577         my @result;
1579         foreach my $type(keys %{$tmp_classes}) {
1580                 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
1581                         my $entry = {
1582                                 type => $type,
1583                                 state => $tmp_classes->{$type}[0],
1584                         };
1585                         push @result, $entry;
1586                 }
1587         }
1589         return @result;
1592 sub get_fai_state {
1593         my $result = "";
1594         my $tmp_classes = shift || return $result;
1596         foreach my $type(keys %{$tmp_classes}) {
1597                 if(defined($tmp_classes->{$type}[0])) {
1598                         $result = $tmp_classes->{$type}[0];
1599                         
1600                 # State is equal for all types in class
1601                         last;
1602                 }
1603         }
1605         return $result;
1608 sub resolve_fai_classes {
1609         my $result;
1610         my $fai_base= shift;
1611         my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
1612         my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
1613         my $fai_classes;
1615         daemon_log("DEBUG: Searching for FAI entries in base $fai_base",6);
1616         my $mesg= $ldap_handle->search(
1617                 base   => $fai_base,
1618                 scope  => 'sub',
1619                 attrs  => ['cn','objectClass','FAIstate'],
1620                 filter => $fai_filter,
1621         );
1622         daemon_log("DEBUG: Found ".$mesg->count()." FAI entries",6);
1624         if($mesg->{'resultCode'} == 0 &&
1625                 $mesg->count != 0) {
1626                 foreach my $entry (@{$mesg->{entries}}) {
1627                         if($entry->exists('cn')) {
1628                                 my $tmp_dn= $entry->dn();
1630                                 # Skip classname and ou dn parts for class
1631                                 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?),$fai_base$/;
1633                                 # Skip classes without releases
1634                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
1635                                         next;
1636                                 }
1638                                 my $tmp_cn= $entry->get_value('cn');
1639                                 my $tmp_state= $entry->get_value('FAIstate');
1641                                 my $tmp_type;
1642                                 # Get FAI type
1643                                 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
1644                                         if(grep $_ eq $oclass, @possible_fai_classes) {
1645                                                 $tmp_type= $oclass;
1646                                                 last;
1647                                         }
1648                                 }
1650                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
1651                                         # A Subrelease
1652                                         my @sub_releases = split(/,/, $tmp_release);
1654                                         # Walk through subreleases and build hash tree
1655                                         my $hash;
1656                                         while(my $tmp_sub_release = pop @sub_releases) {
1657                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
1658                                         }
1659                                         eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
1660                                 } else {
1661                                         # A branch, no subrelease
1662                                         push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
1663                                 }
1664                         } elsif (!$entry->exists('cn')) {
1665                                 my $tmp_dn= $entry->dn();
1666                                 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?),$fai_base$/;
1668                                 # Skip classes without releases
1669                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
1670                                         next;
1671                                 }
1673                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
1674                                         # A Subrelease
1675                                         my @sub_releases= split(/,/, $tmp_release);
1677                                         # Walk through subreleases and build hash tree
1678                                         my $hash;
1679                                         while(my $tmp_sub_release = pop @sub_releases) {
1680                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
1681                                         }
1682                                         # Remove the last two characters
1683                                         chop($hash);
1684                                         chop($hash);
1686                                         eval('$fai_classes->'.$hash.'= {}');
1687                                 } else {
1688                                         # A branch, no subrelease
1689                                         if(!exists($fai_classes->{$tmp_release})) {
1690                                                 $fai_classes->{$tmp_release} = {};
1691                                         }
1692                                 }
1693                         }
1694                 }
1696                 # The hash is complete, now we can honor the copy-on-write based missing entries
1697                 foreach my $release (keys %$fai_classes) {
1698                         $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
1699                 }
1700         }
1701         return $result;
1704 sub apply_fai_inheritance {
1705        my $fai_classes = shift || return {};
1706        my $tmp_classes;
1708        # Get the classes from the branch
1709        foreach my $class (keys %{$fai_classes}) {
1710                # Skip subreleases
1711                if($class =~ /^ou=.*$/) {
1712                        next;
1713                } else {
1714                        $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
1715                }
1716        }
1718        # Apply to each subrelease
1719        foreach my $subrelease (keys %{$fai_classes}) {
1720                if($subrelease =~ /ou=/) {
1721                        foreach my $tmp_class (keys %{$tmp_classes}) {
1722                                if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
1723                                        $fai_classes->{$subrelease}->{$tmp_class} =
1724                                        deep_copy($tmp_classes->{$tmp_class});
1725                                } else {
1726                                        foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
1727                                                if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
1728                                                        $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
1729                                                        deep_copy($tmp_classes->{$tmp_class}->{$type});
1730                                                }
1731                                        }
1732                                }
1733                        }
1734                }
1735        }
1737        # Find subreleases in deeper levels
1738        foreach my $subrelease (keys %{$fai_classes}) {
1739                if($subrelease =~ /ou=/) {
1740                        foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
1741                                if($subsubrelease =~ /ou=/) {
1742                                        apply_fai_inheritance($fai_classes->{$subrelease});
1743                                }
1744                        }
1745                }
1746        }
1748        return $fai_classes;
1751 sub get_fai_release_entries {
1752         my $tmp_classes = shift || return;
1753         my $parent = shift || "";
1754         my @result = shift || ();
1756         foreach my $entry (keys %{$tmp_classes}) {
1757                 if(defined($entry)) {
1758                         if($entry =~ /^ou=.*$/) {
1759                                 my $release_name = $entry;
1760                                 $release_name =~ s/ou=//g;
1761                                 if(length($parent)>0) {
1762                                         $release_name = $parent."/".$release_name;
1763                                 }
1764                                 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
1765                                 foreach my $bufentry(@bufentries) {
1766                                         push @result, $bufentry;
1767                                 }
1768                         } else {
1769                                 my @types = get_fai_types($tmp_classes->{$entry});
1770                                 foreach my $type (@types) {
1771                                         push @result, 
1772                                         {
1773                                                 'class' => $entry,
1774                                                 'type' => $type->{'type'},
1775                                                 'release' => $parent,
1776                                                 'state' => $type->{'state'},
1777                                         };
1778                                 }
1779                         }
1780                 }
1781         }
1783         return @result;
1786 sub deep_copy {
1787         my $this = shift;
1788         if (not ref $this) {
1789                 $this;
1790         } elsif (ref $this eq "ARRAY") {
1791                 [map deep_copy($_), @$this];
1792         } elsif (ref $this eq "HASH") {
1793                 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
1794         } else { die "what type is $_?" }
1798 sub session_run_result {
1799     my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];    
1800     $kernel->sig(CHLD => "child_reap");
1803 sub session_run_debug {
1804     my $result = $_[ARG0];
1805     print STDERR "$result\n";
1808 sub session_run_done {
1809     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1810     delete $heap->{task}->{$task_id};
1813 sub create_sources_list {
1814         my $result="/tmp/gosa_si_tmp_sources_list";
1816         # Remove old file
1817         if(stat($result)) {
1818                 unlink($result);
1819         }
1821         my $fh;
1822         open($fh, ">$result") or return undef;
1823         if(defined($ldap_server_dn) && length($ldap_server_dn)>0) {
1824                 my $mesg=$ldap_handle->search(
1825                         base    => $ldap_server_dn,
1826                         scope   => 'base',
1827                         attrs   => 'FAIrepository',
1828                 );
1830                 if($mesg->count) {
1831                         foreach my $entry(@{$mesg->{'entries'}}) {
1832                                 my ($server, $tag, $release, $sections)= split /\|/, $entry->get_value('FAIrepository');
1833                                 my $line = "deb $server $release";
1834                                 $sections =~ s/,/ /g;
1835                                 $line.= " $sections";
1836                                 print $fh $line."\n";
1837                         }
1838                 }
1839         }
1840         close($fh);
1842         return $result;
1845 sub create_packages_list_db {
1846     my ($sources_file) = @_ || &create_sources_list;
1847     my $line;
1848     daemon_log("INFO: create_packages_list_db: start", 5); 
1850     open(CONFIG, "<$sources_file") or do {
1851         daemon_log( "ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
1852         return;
1853     };
1854     
1855     # Read lines
1856     while ($line = <CONFIG>){
1857         # Unify
1858         chop($line);
1859         $line =~ s/^\s+//;
1860         $line =~ s/^\s+/ /;
1862         # Strip comments
1863         $line =~ s/#.*$//g;
1865         # Skip empty lines
1866         if ($line =~ /^\s*$/){
1867             next;
1868         }
1870         # Interpret deb line
1871         if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
1872             my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
1873             my $section;
1874             foreach $section (split(' ', $sections)){
1875                 &parse_package_info( $baseurl, $dist, $section );
1876             }
1877         }
1878     }
1880     close (CONFIG);
1882     daemon_log("INFO: create_packages_list_db: finished", 5); 
1883     return;
1885 sub run_create_packages_list_db {
1886     my ($session, $heap) = @_[SESSION, HEAP];
1887     my $task = POE::Wheel::Run->new(
1888             Program => sub {&create_packages_list_db},
1889             StdoutEvent  => "session_run_result",
1890             StderrEvent  => "session_run_debug",
1891             CloseEvent   => "session_run_done",
1892             );
1893     $heap->{task}->{ $task->ID } = $task;
1895 sub parse_package_info {
1896   my ($baseurl, $dist, $section)= @_;
1897   my ($package);
1899   my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
1901   foreach $package ("Packages.gz"){
1902     daemon_log("DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
1903     get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section" );
1904     parse_package( "$outdir/$dist/$section", $dist, $path );
1905   }
1907 sub get_package {
1908   my ($url, $dest)= @_;
1910   my $tpath = dirname($dest);
1911   -d "$tpath" || mkpath "$tpath";
1913   # This is ugly, but I've no time to take a look at "how it works in perl"
1914   if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
1915       system("gunzip -cd '$dest' > '$dest.in'");
1916       system("rm -f '$dest'");
1917   } else {
1918       daemon_log("ERROR: create_packages_list_db: get_packages: fetching '$url' failed!", 1);
1919   }
1920   return 0;
1922 sub parse_package {
1923     my ($path, $dist, $srv_path )= @_;
1924     my ($package, $version, $section, $description);
1925     my @sql_list;
1926     my $PACKAGES;
1928     if(not stat("$path.in")) {
1929         daemon_log("ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
1930         return;
1931     }
1933     open($PACKAGES, "<$path.in");
1934         if(not defined($PACKAGES)) {
1935         daemon_log("ERROR: create_packages_list_db: parse_package: can not open '$path.in'",1); 
1936         return;
1937     }
1939     # Read lines
1940     while (<$PACKAGES>){
1941         my $line = $_;
1942         # Unify
1943         chop($line);
1945         # Use empty lines as a trigger
1946         if ($line =~ /^\s*$/){
1947             my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', 'xxx', 'none', '0')";
1948             push(@sql_list, $sql);
1949             $package = "none";
1950             $version = "none";
1951             $section = "none";
1952             $description = "none"; 
1953             next;
1954         }
1956         # Trigger for package name
1957         if ($line =~ /^Package:\s/){
1958             ($package)= ($line =~ /^Package: (.*)$/);
1959             next;
1960         }
1962         # Trigger for version
1963         if ($line =~ /^Version:\s/){
1964             ($version)= ($line =~ /^Version: (.*)$/);
1965             next;
1966         }
1968         # Trigger for description
1969         if ($line =~ /^Description:\s/){
1970             ($description)= ($line =~ /^Description: (.*)$/);
1971             next;
1972         }
1974         # Trigger for section
1975         if ($line =~ /^Section:\s/){
1976             ($section)= ($line =~ /^Section: (.*)$/);
1977             next;
1978         }
1980     }
1982     close( $PACKAGES );
1983     unlink( "$path.in" );
1984     
1985     $packages_list_db->exec_statementlist(\@sql_list);
1989 #sub store_fileinfo {
1990 #  my( $package, $file, $dist, $path, $vers ) = @_;
1992 #  my %fileinfo = (
1993 #    'package' => $package,
1994 #    'dist' => $dist,
1995 #    'version' => $vers
1996 #  );
1998 #  $repo_files{ "${srvdir}${path}/$file" } = \%fileinfo;
1999 #}
2002 #==== MAIN = main ==============================================================
2003 #  parse commandline options
2004 Getopt::Long::Configure( "bundling" );
2005 GetOptions("h|help" => \&usage,
2006         "c|config=s" => \$cfg_file,
2007         "f|foreground" => \$foreground,
2008         "v|verbose+" => \$verbose,
2009         "no-bus+" => \$no_bus,
2010         "no-arp+" => \$no_arp,
2011            );
2013 #  read and set config parameters
2014 &check_cmdline_param ;
2015 &read_configfile;
2016 &check_pid;
2018 $SIG{CHLD} = 'IGNORE';
2020 # forward error messages to logfile
2021 if( ! $foreground ) {
2022   open( STDIN,  '+>/dev/null' );
2023   open( STDOUT, '+>&STDIN'    );
2024   open( STDERR, '+>&STDIN'    );
2027 # Just fork, if we are not in foreground mode
2028 if( ! $foreground ) { 
2029     chdir '/'                 or die "Can't chdir to /: $!";
2030     $pid = fork;
2031     setsid                    or die "Can't start a new session: $!";
2032     umask 0;
2033 } else { 
2034     $pid = $$; 
2037 # Do something useful - put our PID into the pid_file
2038 if( 0 != $pid ) {
2039     open( LOCK_FILE, ">$pid_file" );
2040     print LOCK_FILE "$pid\n";
2041     close( LOCK_FILE );
2042     if( !$foreground ) { 
2043         exit( 0 ) 
2044     };
2047 daemon_log(" ", 1);
2048 daemon_log("$0 started!", 1);
2050 if ($no_bus > 0) {
2051     $bus_activ = "false"
2056 # delete old DBsqlite lock files
2057 #unlink('/tmp/gosa_si_lock*');
2059 # connect to gosa-si job queue
2060 $job_db = GOSA::DBsqlite->new($job_queue_file_name);
2061 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
2063 # connect to known_clients_db
2064 $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
2065 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
2067 # connect to known_server_db
2068 $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
2069 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
2071 # connect to login_usr_db
2072 $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
2073 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
2075 # connect to fai_server_db and fai_release_db
2076 $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
2077 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
2078 $fai_server_db->create_table($fai_release_tn, \@fai_release_col_names);
2080 # connect to packages_list_db
2081 unlink($packages_list_file_name);
2082 $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
2083 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
2085 # connect to messaging_db
2086 $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
2087 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
2090 # create xml object used for en/decrypting
2091 $xml = new XML::Simple();
2093 # create socket for incoming xml messages
2095 POE::Component::Server::TCP->new(
2096         Port => $server_port,
2097         ClientInput => sub {
2098         my ($kernel, $input) = @_[KERNEL, ARG0];
2099         push(@tasks, $input);
2100         $kernel->yield("next_task");
2101         },
2102     InlineStates => {
2103         next_task => \&next_task,
2104         task_result => \&handle_task_result,
2105         task_done   => \&handle_task_done,
2106         task_debug  => \&handle_task_debug,
2107         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
2108     }
2109 );
2111 daemon_log("start socket for incoming xml messages at port '$server_port' ", 1);
2113 # create session for repeatedly checking the job queue for jobs
2114 POE::Session->create(
2115         inline_states => {
2116                 _start => \&_start,
2117                 sig_handler => \&sig_handler,
2118                 watch_for_new_jobs => \&watch_for_new_jobs,
2119         watch_for_done_jobs => \&watch_for_done_jobs,
2120         create_packages_list_db => \&run_create_packages_list_db,
2121         create_fai_server_db => \&run_create_fai_server_db,
2122         create_fai_release_db => \&run_create_fai_release_db,
2123         session_run_result => \&session_run_result,
2124         session_run_debug => \&session_run_debug,
2125         session_run_done => \&session_run_done,
2126         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
2127         }
2128 );
2131 # import all modules
2132 &import_modules;
2134 # check wether all modules are gosa-si valid passwd check
2136 POE::Kernel->run();
2137 exit;