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