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