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 # TODO
25 #
26 # max_children wird momentan nicht mehr verwendet, jede eingehende nachricht bekommt ein eigenes POE child
28 use strict;
29 use warnings;
30 use Getopt::Long;
31 use Config::IniFiles;
32 use POSIX;
34 use Fcntl;
35 use IO::Socket::INET;
36 use IO::Handle;
37 use IO::Select;
38 use Symbol qw(qualify_to_ref);
39 use Crypt::Rijndael;
40 use MIME::Base64;
41 use Digest::MD5 qw(md5 md5_hex md5_base64);
42 use XML::Simple;
43 use Data::Dumper;
44 use Sys::Syslog qw( :DEFAULT setlogsock);
45 use Cwd;
46 use File::Spec;
47 use File::Basename;
48 use File::Find;
49 use File::Copy;
50 use File::Path;
51 use GOSA::DBsqlite;
52 use GOSA::GosaSupportDaemon;
53 use POE qw(Component::Server::TCP Wheel::Run Filter::Reference);
54 use Net::LDAP;
55 use Net::LDAP::Util qw(:escape);
56 use Time::HiRes qw( usleep);
58 my $modules_path = "/usr/lib/gosa-si/modules";
59 use lib "/usr/lib/gosa-si/modules";
61 # revision number of server and program name
62 my $server_version = '$HeadURL: https://oss.gonicus.de/repositories/gosa/trunk/gosa-si/gosa-si-server $:$Rev: 10826 $';
63 my $server_headURL;
64 my $server_revision;
65 my $server_status;
66 our $prg= basename($0);
68 our $global_kernel;
69 my (%cfg_defaults, $foreground, $verbose, $ping_timeout);
70 my ($bus_activ, $bus, $msg_to_bus, $bus_cipher);
71 my ($server);
72 my ($gosa_server, $job_queue_timeout, $job_queue_loop_delay);
73 my ($messaging_db_loop_delay);
74 my ($known_modules);
75 my ($pid_file, $procid, $pid, $log_file);
76 my ($arp_activ, $arp_fifo);
77 my ($xml);
78 my $sources_list;
79 my $max_clients;
80 my %repo_files=();
81 my $repo_path;
82 my %repo_dirs=();
83 # variables declared in config file are always set to 'our'
84 our (%cfg_defaults, $log_file, $pid_file,
85 $server_ip, $server_port, $SIPackages_key,
86 $arp_activ, $gosa_unit_tag,
87 $GosaPackages_key, $gosa_ip, $gosa_port, $gosa_timeout,
88 );
90 # additional variable which should be globaly accessable
91 our $server_address;
92 our $server_mac_address;
93 our $bus_address;
94 our $gosa_address;
95 our $no_bus;
96 our $no_arp;
97 our $verbose;
98 our $forground;
99 our $cfg_file;
100 our ($ldap_uri, $ldap_base, $ldap_admin_dn, $ldap_admin_password, $ldap_server_dn);
103 # specifies the verbosity of the daemon_log
104 $verbose = 0 ;
106 # if foreground is not null, script will be not forked to background
107 $foreground = 0 ;
109 # specifies the timeout seconds while checking the online status of a registrating client
110 $ping_timeout = 5;
112 $no_bus = 0;
113 $bus_activ = "true";
114 $no_arp = 0;
115 my $packages_list_under_construction = "/tmp/packages_list_creation_in_progress";
116 my @packages_list_statements;
117 my $watch_for_new_jobs_in_progress = 0;
119 # holds all incoming decrypted messages
120 our $incoming_db;
121 our $incoming_tn = 'incoming';
122 my $incoming_file_name;
123 my @incoming_col_names = ("id INTEGER PRIMARY KEY",
124 "timestamp DEFAULT 'none'",
125 "headertag DEFAULT 'none'",
126 "targettag DEFAULT 'none'",
127 "xmlmessage DEFAULT 'none'",
128 "module DEFAULT 'none'",
129 );
131 # holds all gosa jobs
132 our $job_db;
133 our $job_queue_tn = 'jobs';
134 my $job_queue_file_name;
135 my @job_queue_col_names = ("id INTEGER PRIMARY KEY",
136 "timestamp DEFAULT 'none'",
137 "status DEFAULT 'none'",
138 "result DEFAULT 'none'",
139 "progress DEFAULT 'none'",
140 "headertag DEFAULT 'none'",
141 "targettag DEFAULT 'none'",
142 "xmlmessage DEFAULT 'none'",
143 "macaddress DEFAULT 'none'",
144 "plainname DEFAULT 'none'",
145 );
147 # holds all other gosa-sd as well as the gosa-sd-bus
148 our $known_server_db;
149 our $known_server_tn = "known_server";
150 my $known_server_file_name;
151 my @known_server_col_names = ("hostname", "status", "hostkey", "timestamp");
153 # holds all registrated clients
154 our $known_clients_db;
155 our $known_clients_tn = "known_clients";
156 my $known_clients_file_name;
157 my @known_clients_col_names = ("hostname", "status", "hostkey", "timestamp", "macaddress", "events");
159 # holds all logged in user at each client
160 our $login_users_db;
161 our $login_users_tn = "login_users";
162 my $login_users_file_name;
163 my @login_users_col_names = ("client", "user", "timestamp");
165 # holds all fai server, the debian release and tag
166 our $fai_server_db;
167 our $fai_server_tn = "fai_server";
168 my $fai_server_file_name;
169 our @fai_server_col_names = ("timestamp", "server", "release", "sections", "tag");
171 our $fai_release_db;
172 our $fai_release_tn = "fai_release";
173 my $fai_release_file_name;
174 our @fai_release_col_names = ("timestamp", "release", "class", "type", "state");
176 # holds all packages available from different repositories
177 our $packages_list_db;
178 our $packages_list_tn = "packages_list";
179 my $packages_list_file_name;
180 our @packages_list_col_names = ("distribution", "package", "version", "section", "description", "template", "timestamp");
181 my $outdir = "/tmp/packages_list_db";
182 my $arch = "i386";
184 # holds all messages which should be delivered to a user
185 our $messaging_db;
186 our $messaging_tn = "messaging";
187 our @messaging_col_names = ("id INTEGER", "subject", "message_from", "message_to",
188 "flag", "direction", "delivery_time", "message", "timestamp" );
189 my $messaging_file_name;
191 # path to directory to store client install log files
192 our $client_fai_log_dir = "/var/log/fai";
194 # queue which stores taskes until one of the $max_children children are ready to process the task
195 my @tasks = qw();
196 my @msgs_to_decrypt = qw();
197 my $max_children = 2;
200 %cfg_defaults = (
201 "general" => {
202 "log-file" => [\$log_file, "/var/run/".$prg.".log"],
203 "pid-file" => [\$pid_file, "/var/run/".$prg.".pid"],
204 },
205 "bus" => {
206 "activ" => [\$bus_activ, "true"],
207 },
208 "server" => {
209 "port" => [\$server_port, "20081"],
210 "known-clients" => [\$known_clients_file_name, '/var/lib/gosa-si/clients.db' ],
211 "known-servers" => [\$known_server_file_name, '/var/lib/gosa-si/servers.db'],
212 "incoming" => [\$incoming_file_name, '/var/lib/gosa-si/incoming.db'],
213 "login-users" => [\$login_users_file_name, '/var/lib/gosa-si/users.db'],
214 "fai-server" => [\$fai_server_file_name, '/var/lib/gosa-si/fai_server.db'],
215 "fai-release" => [\$fai_release_file_name, '/var/lib/gosa-si/fai_release.db'],
216 "packages-list" => [\$packages_list_file_name, '/var/lib/gosa-si/packages.db'],
217 "messaging" => [\$messaging_file_name, '/var/lib/gosa-si/messaging.db'],
218 "source-list" => [\$sources_list, '/etc/apt/sources.list'],
219 "repo-path" => [\$repo_path, '/srv/www/repository'],
220 "ldap-uri" => [\$ldap_uri, ""],
221 "ldap-base" => [\$ldap_base, ""],
222 "ldap-admin-dn" => [\$ldap_admin_dn, ""],
223 "ldap-admin-password" => [\$ldap_admin_password, ""],
224 "gosa-unit-tag" => [\$gosa_unit_tag, ""],
225 "max-clients" => [\$max_clients, 10],
226 },
227 "GOsaPackages" => {
228 "ip" => [\$gosa_ip, "0.0.0.0"],
229 "port" => [\$gosa_port, "20082"],
230 "job-queue" => [\$job_queue_file_name, '/var/lib/gosa-si/jobs.db'],
231 "job-queue-loop-delay" => [\$job_queue_loop_delay, 3],
232 "messaging-db-loop-delay" => [\$messaging_db_loop_delay, 3],
233 "key" => [\$GosaPackages_key, "none"],
234 },
235 "SIPackages" => {
236 "key" => [\$SIPackages_key, "none"],
237 },
238 );
241 #=== FUNCTION ================================================================
242 # NAME: usage
243 # PARAMETERS: nothing
244 # RETURNS: nothing
245 # DESCRIPTION: print out usage text to STDERR
246 #===============================================================================
247 sub usage {
248 print STDERR << "EOF" ;
249 usage: $prg [-hvf] [-c config]
251 -h : this (help) message
252 -c <file> : config file
253 -f : foreground, process will not be forked to background
254 -v : be verbose (multiple to increase verbosity)
255 -no-bus : starts $prg without connection to bus
256 -no-arp : starts $prg without connection to arp module
258 EOF
259 print "\n" ;
260 }
263 #=== FUNCTION ================================================================
264 # NAME: read_configfile
265 # PARAMETERS: cfg_file - string -
266 # RETURNS: nothing
267 # DESCRIPTION: read cfg_file and set variables
268 #===============================================================================
269 sub read_configfile {
270 my $cfg;
271 if( defined( $cfg_file) && ( (-s $cfg_file) > 0 )) {
272 if( -r $cfg_file ) {
273 $cfg = Config::IniFiles->new( -file => $cfg_file );
274 } else {
275 print STDERR "Couldn't read config file!\n";
276 }
277 } else {
278 $cfg = Config::IniFiles->new() ;
279 }
280 foreach my $section (keys %cfg_defaults) {
281 foreach my $param (keys %{$cfg_defaults{ $section }}) {
282 my $pinfo = $cfg_defaults{ $section }{ $param };
283 ${@$pinfo[ 0 ]} = $cfg->val( $section, $param, @$pinfo[ 1 ] );
284 }
285 }
286 }
289 #=== FUNCTION ================================================================
290 # NAME: logging
291 # PARAMETERS: level - string - default 'info'
292 # msg - string -
293 # facility - string - default 'LOG_DAEMON'
294 # RETURNS: nothing
295 # DESCRIPTION: function for logging
296 #===============================================================================
297 sub daemon_log {
298 # log into log_file
299 my( $msg, $level ) = @_;
300 if(not defined $msg) { return }
301 if(not defined $level) { $level = 1 }
302 if(defined $log_file){
303 open(LOG_HANDLE, ">>$log_file");
304 if(not defined open( LOG_HANDLE, ">>$log_file" )) {
305 print STDERR "cannot open $log_file: $!";
306 return }
307 chomp($msg);
308 $msg =~s/\n//g; # no newlines are allowed in log messages, this is important for later log parsing
309 if($level <= $verbose){
310 my ($seconds, $minutes, $hours, $monthday, $month,
311 $year, $weekday, $yearday, $sommertime) = localtime(time);
312 $hours = $hours < 10 ? $hours = "0".$hours : $hours;
313 $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
314 $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
315 my @monthnames = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
316 $month = $monthnames[$month];
317 $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
318 $year+=1900;
319 my $name = $prg;
321 my $log_msg = "$month $monthday $hours:$minutes:$seconds $name $msg\n";
322 print LOG_HANDLE $log_msg;
323 if( $foreground ) {
324 print STDERR $log_msg;
325 }
326 }
327 close( LOG_HANDLE );
328 }
329 }
332 #=== FUNCTION ================================================================
333 # NAME: check_cmdline_param
334 # PARAMETERS: nothing
335 # RETURNS: nothing
336 # DESCRIPTION: validates commandline parameter
337 #===============================================================================
338 sub check_cmdline_param () {
339 my $err_config;
340 my $err_counter = 0;
341 if(not defined($cfg_file)) {
342 $cfg_file = "/etc/gosa-si/server.conf";
343 if(! -r $cfg_file) {
344 $err_config = "please specify a config file";
345 $err_counter += 1;
346 }
347 }
348 if( $err_counter > 0 ) {
349 &usage( "", 1 );
350 if( defined( $err_config)) { print STDERR "$err_config\n"}
351 print STDERR "\n";
352 exit( -1 );
353 }
354 }
357 #=== FUNCTION ================================================================
358 # NAME: check_pid
359 # PARAMETERS: nothing
360 # RETURNS: nothing
361 # DESCRIPTION: handels pid processing
362 #===============================================================================
363 sub check_pid {
364 $pid = -1;
365 # Check, if we are already running
366 if( open(LOCK_FILE, "<$pid_file") ) {
367 $pid = <LOCK_FILE>;
368 if( defined $pid ) {
369 chomp( $pid );
370 if( -f "/proc/$pid/stat" ) {
371 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
372 if( $stat ) {
373 daemon_log("ERROR: Already running",1);
374 close( LOCK_FILE );
375 exit -1;
376 }
377 }
378 }
379 close( LOCK_FILE );
380 unlink( $pid_file );
381 }
383 # create a syslog msg if it is not to possible to open PID file
384 if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
385 my($msg) = "Couldn't obtain lockfile '$pid_file' ";
386 if (open(LOCK_FILE, '<', $pid_file)
387 && ($pid = <LOCK_FILE>))
388 {
389 chomp($pid);
390 $msg .= "(PID $pid)\n";
391 } else {
392 $msg .= "(unable to read PID)\n";
393 }
394 if( ! ($foreground) ) {
395 openlog( $0, "cons,pid", "daemon" );
396 syslog( "warning", $msg );
397 closelog();
398 }
399 else {
400 print( STDERR " $msg " );
401 }
402 exit( -1 );
403 }
404 }
406 #=== FUNCTION ================================================================
407 # NAME: import_modules
408 # PARAMETERS: module_path - string - abs. path to the directory the modules
409 # are stored
410 # RETURNS: nothing
411 # DESCRIPTION: each file in module_path which ends with '.pm' and activation
412 # state is on is imported by "require 'file';"
413 #===============================================================================
414 sub import_modules {
415 daemon_log(" ", 1);
417 if (not -e $modules_path) {
418 daemon_log("ERROR: cannot find directory or directory is not readable: $modules_path", 1);
419 }
421 opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
422 while (defined (my $file = readdir (DIR))) {
423 if (not $file =~ /(\S*?).pm$/) {
424 next;
425 }
426 my $mod_name = $1;
428 if( $file =~ /ArpHandler.pm/ ) {
429 if( $no_arp > 0 ) {
430 next;
431 }
432 }
434 eval { require $file; };
435 if ($@) {
436 daemon_log("ERROR: gosa-si-server could not load module $file", 1);
437 daemon_log("$@", 5);
438 } else {
439 my $info = eval($mod_name.'::get_module_info()');
440 # Only load module if get_module_info() returns a non-null object
441 if( $info ) {
442 my ($input_address, $input_key, $input, $input_active, $input_type) = @{$info};
443 $known_modules->{$mod_name} = $info;
444 daemon_log("INFO: module $mod_name loaded", 5);
445 }
446 }
447 }
448 close (DIR);
449 }
452 #=== FUNCTION ================================================================
453 # NAME: sig_int_handler
454 # PARAMETERS: signal - string - signal arose from system
455 # RETURNS: noting
456 # DESCRIPTION: handels tasks to be done befor signal becomes active
457 #===============================================================================
458 sub sig_int_handler {
459 my ($signal) = @_;
461 # if (defined($ldap_handle)) {
462 # $ldap_handle->disconnect;
463 # }
464 # TODO alle verbliebenden ldap verbindungen aus allen heaps beenden
467 daemon_log("shutting down gosa-si-server", 1);
468 system("kill `ps -C gosa-si-server-nobus -o pid=`");
469 }
470 $SIG{INT} = \&sig_int_handler;
473 sub check_key_and_xml_validity {
474 my ($crypted_msg, $module_key, $session_id) = @_;
475 my $msg;
476 my $msg_hash;
477 my $error_string;
478 eval{
479 $msg = &decrypt_msg($crypted_msg, $module_key);
481 if ($msg =~ /<xml>/i){
482 $msg =~ s/\s+/ /g; # just for better daemon_log
483 daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 8);
484 $msg_hash = $xml->XMLin($msg, ForceArray=>1);
486 ##############
487 # check header
488 if( not exists $msg_hash->{'header'} ) { die "no header specified"; }
489 my $header_l = $msg_hash->{'header'};
490 if( 1 > @{$header_l} ) { die 'empty header tag'; }
491 if( 1 < @{$header_l} ) { die 'more than one header specified'; }
492 my $header = @{$header_l}[0];
493 if( 0 == length $header) { die 'empty string in header tag'; }
495 ##############
496 # check source
497 if( not exists $msg_hash->{'source'} ) { die "no source specified"; }
498 my $source_l = $msg_hash->{'source'};
499 if( 1 > @{$source_l} ) { die 'empty source tag'; }
500 if( 1 < @{$source_l} ) { die 'more than one source specified'; }
501 my $source = @{$source_l}[0];
502 if( 0 == length $source) { die 'source error'; }
504 ##############
505 # check target
506 if( not exists $msg_hash->{'target'} ) { die "no target specified"; }
507 my $target_l = $msg_hash->{'target'};
508 if( 1 > @{$target_l} ) { die 'empty target tag'; }
509 }
510 };
511 if($@) {
512 daemon_log("$session_id DEBUG: do not understand the message: $@", 7);
513 $msg = undef;
514 $msg_hash = undef;
515 }
517 return ($msg, $msg_hash);
518 }
521 sub check_outgoing_xml_validity {
522 my ($msg) = @_;
524 my $msg_hash;
525 eval{
526 $msg_hash = $xml->XMLin($msg, ForceArray=>1);
528 ##############
529 # check header
530 my $header_l = $msg_hash->{'header'};
531 if( 1 != @{$header_l} ) {
532 die 'no or more than one headers specified';
533 }
534 my $header = @{$header_l}[0];
535 if( 0 == length $header) {
536 die 'header has length 0';
537 }
539 ##############
540 # check source
541 my $source_l = $msg_hash->{'source'};
542 if( 1 != @{$source_l} ) {
543 die 'no or more than 1 sources specified';
544 }
545 my $source = @{$source_l}[0];
546 if( 0 == length $source) {
547 die 'source has length 0';
548 }
549 unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
550 $source =~ /^GOSA$/i ) {
551 die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
552 }
554 ##############
555 # check target
556 my $target_l = $msg_hash->{'target'};
557 if( 0 == @{$target_l} ) {
558 die "no targets specified";
559 }
560 foreach my $target (@$target_l) {
561 if( 0 == length $target) {
562 die "target has length 0";
563 }
564 unless( $target =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
565 $target =~ /^GOSA$/i ||
566 $target =~ /^\*$/ ||
567 $target =~ /KNOWN_SERVER/i ||
568 $target =~ /JOBDB/i ||
569 $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 ){
570 die "target '$target' is not a complete ip-address with port or a valid target name or a mac-address";
571 }
572 }
573 };
574 if($@) {
575 daemon_log("WARNING: outgoing msg is not gosa-si envelope conform", 5);
576 daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 8);
577 $msg_hash = undef;
578 }
580 return ($msg_hash);
581 }
584 sub input_from_known_server {
585 my ($input, $remote_ip, $session_id) = @_ ;
586 my ($msg, $msg_hash, $module);
588 my $sql_statement= "SELECT * FROM known_server";
589 my $query_res = $known_server_db->select_dbentry( $sql_statement );
591 while( my ($hit_num, $hit) = each %{ $query_res } ) {
592 my $host_name = $hit->{hostname};
593 if( not $host_name =~ "^$remote_ip") {
594 next;
595 }
596 my $host_key = $hit->{hostkey};
597 daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 7);
598 daemon_log("$session_id DEBUG: input_from_known_server: host_key: $host_key", 7);
600 # check if module can open msg envelope with module key
601 my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
602 if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
603 daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 7);
604 daemon_log("$@", 8);
605 next;
606 }
607 else {
608 $msg = $tmp_msg;
609 $msg_hash = $tmp_msg_hash;
610 $module = "SIPackages";
611 last;
612 }
613 }
615 if( (!$msg) || (!$msg_hash) || (!$module) ) {
616 daemon_log("$session_id DEBUG: Incoming message is not from a known server", 7);
617 }
619 return ($msg, $msg_hash, $module);
620 }
623 sub input_from_known_client {
624 my ($input, $remote_ip, $session_id) = @_ ;
625 my ($msg, $msg_hash, $module);
627 my $sql_statement= "SELECT * FROM known_clients";
628 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
629 while( my ($hit_num, $hit) = each %{ $query_res } ) {
630 my $host_name = $hit->{hostname};
631 if( not $host_name =~ /^$remote_ip:\d*$/) {
632 next;
633 }
634 my $host_key = $hit->{hostkey};
635 &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 7);
636 &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 7);
638 # check if module can open msg envelope with module key
639 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
641 if( (!$msg) || (!$msg_hash) ) {
642 &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 7);
643 &daemon_log("$@", 8);
644 next;
645 }
646 else {
647 $module = "SIPackages";
648 last;
649 }
650 }
652 if( (!$msg) || (!$msg_hash) || (!$module) ) {
653 &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 7);
654 }
656 return ($msg, $msg_hash, $module);
657 }
660 sub input_from_unknown_host {
661 no strict "refs";
662 my ($input, $session_id) = @_ ;
663 my ($msg, $msg_hash, $module);
664 my $error_string;
666 my %act_modules = %$known_modules;
668 while( my ($mod, $info) = each(%act_modules)) {
670 # check a key exists for this module
671 my $module_key = ${$mod."_key"};
672 if( not defined $module_key ) {
673 if( $mod eq 'ArpHandler' ) {
674 next;
675 }
676 daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
677 next;
678 }
679 daemon_log("$session_id DEBUG: $mod: $module_key", 7);
681 # check if module can open msg envelope with module key
682 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
683 if( (not defined $msg) || (not defined $msg_hash) ) {
684 next;
685 }
686 else {
687 $module = $mod;
688 last;
689 }
690 }
692 if( (!$msg) || (!$msg_hash) || (!$module)) {
693 daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 7);
694 }
696 return ($msg, $msg_hash, $module);
697 }
700 sub create_ciphering {
701 my ($passwd) = @_;
702 if((!defined($passwd)) || length($passwd)==0) {
703 $passwd = "";
704 }
705 $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
706 my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
707 my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
708 $my_cipher->set_iv($iv);
709 return $my_cipher;
710 }
713 sub encrypt_msg {
714 my ($msg, $key) = @_;
715 my $my_cipher = &create_ciphering($key);
716 my $len;
717 {
718 use bytes;
719 $len= 16-length($msg)%16;
720 }
721 $msg = "\0"x($len).$msg;
722 $msg = $my_cipher->encrypt($msg);
723 chomp($msg = &encode_base64($msg));
724 # there are no newlines allowed inside msg
725 $msg=~ s/\n//g;
726 return $msg;
727 }
730 sub decrypt_msg {
732 my ($msg, $key) = @_ ;
733 $msg = &decode_base64($msg);
734 my $my_cipher = &create_ciphering($key);
735 $msg = $my_cipher->decrypt($msg);
736 $msg =~ s/\0*//g;
737 return $msg;
738 }
741 sub get_encrypt_key {
742 my ($target) = @_ ;
743 my $encrypt_key;
744 my $error = 0;
746 # target can be in known_server
747 if( not defined $encrypt_key ) {
748 my $sql_statement= "SELECT * FROM known_server WHERE hostname='$target'";
749 my $query_res = $known_server_db->select_dbentry( $sql_statement );
750 while( my ($hit_num, $hit) = each %{ $query_res } ) {
751 my $host_name = $hit->{hostname};
752 if( $host_name ne $target ) {
753 next;
754 }
755 $encrypt_key = $hit->{hostkey};
756 last;
757 }
758 }
760 # target can be in known_client
761 if( not defined $encrypt_key ) {
762 my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$target'";
763 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
764 while( my ($hit_num, $hit) = each %{ $query_res } ) {
765 my $host_name = $hit->{hostname};
766 if( $host_name ne $target ) {
767 next;
768 }
769 $encrypt_key = $hit->{hostkey};
770 last;
771 }
772 }
774 return $encrypt_key;
775 }
778 #=== FUNCTION ================================================================
779 # NAME: open_socket
780 # PARAMETERS: PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
781 # [PeerPort] string necessary if port not appended by PeerAddr
782 # RETURNS: socket IO::Socket::INET
783 # DESCRIPTION: open a socket to PeerAddr
784 #===============================================================================
785 sub open_socket {
786 my ($PeerAddr, $PeerPort) = @_ ;
787 if(defined($PeerPort)){
788 $PeerAddr = $PeerAddr.":".$PeerPort;
789 }
790 my $socket;
791 $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
792 Porto => "tcp",
793 Type => SOCK_STREAM,
794 Timeout => 5,
795 );
796 if(not defined $socket) {
797 return;
798 }
799 # &daemon_log("DEBUG: open_socket: $PeerAddr", 7);
800 return $socket;
801 }
804 #=== FUNCTION ================================================================
805 # NAME: get_ip
806 # PARAMETERS: interface name (i.e. eth0)
807 # RETURNS: (ip address)
808 # DESCRIPTION: Uses ioctl to get ip address directly from system.
809 #===============================================================================
810 sub get_ip {
811 my $ifreq= shift;
812 my $result= "";
813 my $SIOCGIFADDR= 0x8915; # man 2 ioctl_list
814 my $proto= getprotobyname('ip');
816 socket SOCKET, PF_INET, SOCK_DGRAM, $proto
817 or die "socket: $!";
819 if(ioctl SOCKET, $SIOCGIFADDR, $ifreq) {
820 my ($if, $sin) = unpack 'a16 a16', $ifreq;
821 my ($port, $addr) = sockaddr_in $sin;
822 my $ip = inet_ntoa $addr;
824 if ($ip && length($ip) > 0) {
825 $result = $ip;
826 }
827 }
829 return $result;
830 }
833 sub get_local_ip_for_remote_ip {
834 my $remote_ip= shift;
835 my $result="0.0.0.0";
837 if($remote_ip =~ /^(\d\d?\d?\.){3}\d\d?\d?$/) {
838 if($remote_ip eq "127.0.0.1") {
839 $result = "127.0.0.1";
840 } else {
841 my $PROC_NET_ROUTE= ('/proc/net/route');
843 open(PROC_NET_ROUTE, "<$PROC_NET_ROUTE")
844 or die "Could not open $PROC_NET_ROUTE";
846 my @ifs = <PROC_NET_ROUTE>;
848 close(PROC_NET_ROUTE);
850 # Eat header line
851 shift @ifs;
852 chomp @ifs;
853 foreach my $line(@ifs) {
854 my ($Iface,$Destination,$Gateway,$Flags,$RefCnt,$Use,$Metric,$Mask,$MTU,$Window,$IRTT)=split(/\s/, $line);
855 my $destination;
856 my $mask;
857 my ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Destination);
858 $destination= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
859 ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Mask);
860 $mask= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
861 if(new NetAddr::IP($remote_ip)->within(new NetAddr::IP($destination, $mask))) {
862 # destination matches route, save mac and exit
863 $result= &get_ip($Iface);
864 last;
865 }
866 }
867 }
868 } else {
869 daemon_log("get_local_ip_for_remote_ip was called with a non-ip parameter: $remote_ip", 1);
870 }
871 return $result;
872 }
875 sub send_msg_to_target {
876 my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
877 my $error = 0;
878 my $header;
879 my $new_status;
880 my $act_status;
881 my ($sql_statement, $res);
883 if( $msg_header ) {
884 $header = "'$msg_header'-";
885 } else {
886 $header = "";
887 }
889 # Patch the source ip
890 if($msg =~ /<source>0\.0\.0\.0:\d*?<\/source>/) {
891 my $remote_ip = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
892 $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$remote_ip:$2<\/source>/s;
893 }
895 # encrypt xml msg
896 my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
898 # opensocket
899 my $socket = &open_socket($address);
900 if( !$socket ) {
901 daemon_log("$session_id ERROR: cannot send ".$header."msg to $address , host not reachable", 1);
902 $error++;
903 }
905 if( $error == 0 ) {
906 # send xml msg
907 print $socket $crypted_msg."\n";
909 daemon_log("$session_id INFO: send ".$header."msg to $address", 5);
910 #daemon_log("DEBUG: message:\n$msg", 9);
912 }
914 # close socket in any case
915 if( $socket ) {
916 close $socket;
917 }
919 if( $error > 0 ) { $new_status = "down"; }
920 else { $new_status = $msg_header; }
923 # known_clients
924 $sql_statement = "SELECT * FROM known_clients WHERE hostname='$address'";
925 $res = $known_clients_db->select_dbentry($sql_statement);
926 if( keys(%$res) > 0) {
927 $act_status = $res->{1}->{'status'};
928 if( $act_status eq "down" ) {
929 $sql_statement = "DELETE FROM known_clients WHERE hostname='$address'";
930 $res = $known_clients_db->del_dbentry($sql_statement);
931 daemon_log("$session_id WARNING: failed 2x to send msg to host '$address', delete host from known_clients", 3);
932 } else {
933 $sql_statement = "UPDATE known_clients SET status='$new_status' WHERE hostname='$address'";
934 $res = $known_clients_db->update_dbentry($sql_statement);
935 if($new_status eq "down"){
936 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
937 } else {
938 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
939 }
940 }
941 }
943 # known_server
944 $sql_statement = "SELECT * FROM known_server WHERE hostname='$address'";
945 $res = $known_server_db->select_dbentry($sql_statement);
946 if( keys(%$res) > 0 ) {
947 $act_status = $res->{1}->{'status'};
948 if( $act_status eq "down" ) {
949 $sql_statement = "DELETE FROM known_server WHERE hostname='$address'";
950 $res = $known_server_db->del_dbentry($sql_statement);
951 daemon_log("$session_id WARNING: failed 2x to a send msg to host '$address', delete host from known_server", 3);
952 }
953 else {
954 $sql_statement = "UPDATE known_server SET status='$new_status' WHERE hostname='$address'";
955 $res = $known_server_db->update_dbentry($sql_statement);
956 if($new_status eq "down"){
957 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
958 }
959 else {
960 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
961 }
962 }
963 }
964 return $error;
965 }
968 sub update_jobdb_status_for_send_msgs {
969 my ($answer, $error) = @_;
970 if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
971 my $jobdb_id = $1;
973 # sending msg faild
974 if( $error ) {
975 if (not $answer =~ /<header>trigger_action_reinstall<\/header>/) {
976 my $sql_statement = "UPDATE $job_queue_tn ".
977 "SET status='error', result='can not deliver msg, please consult log file' ".
978 "WHERE id=$jobdb_id";
979 my $res = $job_db->update_dbentry($sql_statement);
980 }
982 # sending msg was successful
983 } else {
984 my $sql_statement = "UPDATE $job_queue_tn ".
985 "SET status='done' ".
986 "WHERE id=$jobdb_id AND status='processed'";
987 my $res = $job_db->update_dbentry($sql_statement);
988 }
989 }
990 }
992 sub _start {
993 my ($kernel) = $_[KERNEL];
994 &trigger_db_loop($kernel);
995 $global_kernel = $kernel;
996 $kernel->yield('create_fai_server_db', $fai_server_tn );
997 $kernel->yield('create_fai_release_db', $fai_release_tn );
998 $kernel->sig(USR1 => "sig_handler");
999 $kernel->sig(USR2 => "create_packages_list_db");
1000 }
1002 sub sig_handler {
1003 my ($kernel, $signal) = @_[KERNEL, ARG0] ;
1004 daemon_log("0 INFO got signal '$signal'", 1);
1005 $kernel->sig_handled();
1006 return;
1007 }
1010 sub msg_to_decrypt {
1011 my ($session, $heap) = @_[SESSION, HEAP];
1012 my $session_id = $session->ID;
1013 my ($msg, $msg_hash, $module);
1014 my $error = 0;
1016 # hole neue msg aus @msgs_to_decrypt
1017 my $next_msg = shift @msgs_to_decrypt;
1019 # entschlüssle sie
1021 # msg is from a new client or gosa
1022 ($msg, $msg_hash, $module) = &input_from_unknown_host($next_msg, $session_id);
1023 # msg is from a gosa-si-server or gosa-si-bus
1024 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1025 ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $heap->{'remote_ip'}, $session_id);
1026 }
1027 # msg is from a gosa-si-client
1028 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1029 ($msg, $msg_hash, $module) = &input_from_known_client($next_msg, $heap->{'remote_ip'}, $session_id);
1030 }
1031 # an error occurred
1032 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1033 # if an incoming msg could not be decrypted (maybe a wrong key), send client a ping. If the client
1034 # could not understand a msg from its server the client cause a re-registering process
1035 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);
1036 my $sql_statement = "SELECT * FROM $main::known_clients_tn WHERE (hostname LIKE '".$heap->{'remote_ip'}."%')";
1037 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1038 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1039 my $host_name = $hit->{'hostname'};
1040 my $host_key = $hit->{'hostkey'};
1041 my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source> <target>$host_name</target></xml>";
1042 my $error = &send_msg_to_target($ping_msg, $host_name, $host_key, "gosa_ping", $session_id);
1043 &update_jobdb_status_for_send_msgs($ping_msg, $error);
1044 }
1045 $error++;
1046 }
1048 # add message to incoming_db
1049 if( $error == 0) {
1050 my $header = @{$msg_hash->{'header'}}[0];
1051 my $target = @{$msg_hash->{'target'}}[0];
1052 my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1053 primkey=>[],
1054 headertag=>$header,
1055 targettag=>$target,
1056 xmlmessage=>$msg,
1057 timestamp=>&get_time,
1058 module=>$module,
1059 } );
1060 if ($res != 0) {
1061 # TODO ist das mit $! so ok???
1062 #&daemon_log("$session_id ERROR: cannot add message to incoming.db: $!", 1);
1063 }
1064 }
1066 }
1069 sub next_task {
1070 my ($session, $heap) = @_[SESSION, HEAP];
1071 my $task = POE::Wheel::Run->new(
1072 Program => sub { process_task($session, $heap) },
1073 StdioFilter => POE::Filter::Reference->new(),
1074 StdoutEvent => "task_result",
1075 StderrEvent => "task_debug",
1076 CloseEvent => "task_done",
1077 );
1079 $heap->{task}->{ $task->ID } = $task;
1080 }
1082 sub handle_task_result {
1083 my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
1084 my $client_answer = $result->{'answer'};
1085 if( $client_answer =~ s/session_id=(\d+)$// ) {
1086 my $session_id = $1;
1087 if( defined $session_id ) {
1088 my $session_reference = $kernel->ID_id_to_session($session_id);
1089 if( defined $session_reference ) {
1090 $heap = $session_reference->get_heap();
1091 }
1092 }
1094 if(exists $heap->{'client'}) {
1095 $heap->{'client'}->put($client_answer);
1096 }
1097 }
1098 $kernel->sig(CHLD => "child_reap");
1099 }
1101 sub handle_task_debug {
1102 my $result = $_[ARG0];
1103 print STDERR "$result\n";
1104 }
1106 sub handle_task_done {
1107 my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1108 delete $heap->{task}->{$task_id};
1109 }
1111 sub process_task {
1112 no strict "refs";
1113 my ($session, $heap, $input) = @_;
1114 my $session_id = $session->ID;
1115 my $error = 0;
1116 my $answer_l;
1117 my ($answer_header, @answer_target_l, $answer_source);
1118 my $client_answer = "";
1120 ##################################################
1121 # fetch first unprocessed message from incoming_db
1122 # sometimes the program is faster than sqlite, so wait until informations are present at db
1123 my $id_sql;
1124 my $id_res;
1125 my $message_id;
1126 # TODO : das hier ist sehr sehr unschön
1127 # to be tested: speed enhancement with usleep 100000???
1128 while (1) {
1129 $id_sql = "SELECT min(id) FROM $incoming_tn WHERE (NOT headertag LIKE 'answer%')";
1130 $id_res = $incoming_db->exec_statement($id_sql);
1131 $message_id = @{@$id_res[0]}[0];
1132 if (defined $message_id) { last }
1133 usleep(100000);
1134 }
1136 # fetch new message from incoming_db
1137 my $sql = "SELECT * FROM $incoming_tn WHERE id=$message_id";
1138 my $res = $incoming_db->exec_statement($sql);
1140 # prepare all variables needed to process message
1141 my $msg = @{@$res[0]}[4];
1142 my $incoming_id = @{@$res[0]}[0];
1143 my $module = @{@$res[0]}[5];
1144 my $header = @{@$res[0]}[2];
1145 my $msg_hash = $xml->XMLin($msg, ForceArray=>1);
1147 # messages which are an answer to a still running process should not be processed here
1148 if ($header =~ /^answer_(\d+)/) {
1149 return;
1150 }
1152 # delete message from db
1153 my $delete_sql = "DELETE FROM $incoming_tn WHERE id=$incoming_id";
1154 my $delete_res = $incoming_db->exec_statement($delete_sql);
1156 ######################
1157 # process incoming msg
1158 if( $error == 0) {
1159 daemon_log("$session_id INFO: Incoming msg (session_id=$session_id) with header '".@{$msg_hash->{'header'}}[0].
1160 "' from '".$heap->{'remote_ip'}."'", 5);
1161 daemon_log("$session_id DEBUG: Processing module ".$module, 7);
1162 $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1164 if ( 0 < @{$answer_l} ) {
1165 my $answer_str = join("\n", @{$answer_l});
1166 while ($answer_str =~ /<header>(\w+)<\/header>/g) {
1167 daemon_log("$session_id INFO: got answer message with header '$1'", 5);
1168 }
1169 daemon_log("$session_id DEBUG: $module: got answer from module: \n".$answer_str,8);
1170 } else {
1171 daemon_log("$session_id DEBUG: $module: got no answer from module!" ,8);
1172 }
1174 }
1175 if( !$answer_l ) { $error++ };
1177 ########
1178 # answer
1179 if( $error == 0 ) {
1181 foreach my $answer ( @{$answer_l} ) {
1182 # check outgoing msg to xml validity
1183 my $answer_hash = &check_outgoing_xml_validity($answer);
1184 if( not defined $answer_hash ) { next; }
1186 $answer_header = @{$answer_hash->{'header'}}[0];
1187 @answer_target_l = @{$answer_hash->{'target'}};
1188 $answer_source = @{$answer_hash->{'source'}}[0];
1190 # deliver msg to all targets
1191 foreach my $answer_target ( @answer_target_l ) {
1193 # targets of msg are all gosa-si-clients in known_clients_db
1194 if( $answer_target eq "*" ) {
1195 # answer is for all clients
1196 my $sql_statement= "SELECT * FROM known_clients";
1197 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1198 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1199 my $host_name = $hit->{hostname};
1200 my $host_key = $hit->{hostkey};
1201 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1202 &update_jobdb_status_for_send_msgs($answer, $error);
1203 }
1204 }
1206 # targets of msg are all gosa-si-server in known_server_db
1207 elsif( $answer_target eq "KNOWN_SERVER" ) {
1208 # answer is for all server in known_server
1209 my $sql_statement= "SELECT * FROM known_server";
1210 my $query_res = $known_server_db->select_dbentry( $sql_statement );
1211 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1212 my $host_name = $hit->{hostname};
1213 my $host_key = $hit->{hostkey};
1214 $answer =~ s/KNOWN_SERVER/$host_name/g;
1215 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1216 &update_jobdb_status_for_send_msgs($answer, $error);
1217 }
1218 }
1220 # target of msg is GOsa
1221 elsif( $answer_target eq "GOSA" ) {
1222 my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1223 my $add_on = "";
1224 if( defined $session_id ) {
1225 $add_on = ".session_id=$session_id";
1226 }
1227 # answer is for GOSA and has to returned to connected client
1228 my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1229 $client_answer = $gosa_answer.$add_on;
1230 }
1232 # target of msg is job queue at this host
1233 elsif( $answer_target eq "JOBDB") {
1234 $answer =~ /<header>(\S+)<\/header>/;
1235 my $header;
1236 if( defined $1 ) { $header = $1; }
1237 my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1238 &update_jobdb_status_for_send_msgs($answer, $error);
1239 }
1241 # target of msg is a mac address
1242 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 ) {
1243 daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients", 5);
1244 my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1245 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1246 my $found_ip_flag = 0;
1247 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1248 my $host_name = $hit->{hostname};
1249 my $host_key = $hit->{hostkey};
1250 $answer =~ s/$answer_target/$host_name/g;
1251 daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1252 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1253 &update_jobdb_status_for_send_msgs($answer, $error);
1254 $found_ip_flag++ ;
1255 }
1256 if( $found_ip_flag == 0) {
1257 daemon_log("$session_id WARNING: no host found in known_clients with mac address '$answer_target'", 3);
1258 if( $bus_activ eq "true" ) {
1259 daemon_log("$session_id INFO: try to forward msg '$answer_header' to bus '$bus_address'", 5);
1260 my $sql_statement = "SELECT * FROM known_server WHERE hostname='$bus_address'";
1261 my $query_res = $known_server_db->select_dbentry( $sql_statement );
1262 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1263 my $bus_address = $hit->{hostname};
1264 my $bus_key = $hit->{hostkey};
1265 my $error = &send_msg_to_target($answer, $bus_address, $bus_key, $answer_header, $session_id);
1266 &update_jobdb_status_for_send_msgs($answer, $error);
1267 last;
1268 }
1269 }
1271 }
1273 # answer is for one specific host
1274 } else {
1275 # get encrypt_key
1276 my $encrypt_key = &get_encrypt_key($answer_target);
1277 if( not defined $encrypt_key ) {
1278 # unknown target, forward msg to bus
1279 daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1280 if( $bus_activ eq "true" ) {
1281 daemon_log("$session_id INFO: try to forward msg '$answer_header' to bus '$bus_address'", 5);
1282 my $sql_statement = "SELECT * FROM known_server WHERE hostname='$bus_address'";
1283 my $query_res = $known_server_db->select_dbentry( $sql_statement );
1284 my $res_length = keys( %{$query_res} );
1285 if( $res_length == 0 ){
1286 daemon_log("$session_id WARNING: send '$answer_header' to '$bus_address' failed, ".
1287 "no bus found in known_server", 3);
1288 }
1289 else {
1290 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1291 my $bus_key = $hit->{hostkey};
1292 my $error = &send_msg_to_target($answer, $bus_address, $bus_key, $answer_header,$session_id );
1293 &update_jobdb_status_for_send_msgs($answer, $error);
1294 }
1295 }
1296 }
1297 next;
1298 }
1299 my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1300 &update_jobdb_status_for_send_msgs($answer, $error);
1301 }
1302 }
1303 }
1304 }
1306 my $filter = POE::Filter::Reference->new();
1307 my %result = (
1308 status => "seems ok to me",
1309 answer => $client_answer,
1310 );
1312 my $output = $filter->put( [ \%result ] );
1313 print @$output;
1316 }
1319 sub trigger_db_loop {
1320 my ($kernel) = @_ ;
1321 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1322 $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay);
1323 $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1324 $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1325 $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1326 }
1329 sub watch_for_done_jobs {
1330 my ($kernel,$heap) = @_[KERNEL, HEAP];
1332 my $sql_statement = "SELECT * FROM ".$job_queue_tn.
1333 " WHERE status='done'";
1334 my $res = $job_db->select_dbentry( $sql_statement );
1336 while( my ($id, $hit) = each %{$res} ) {
1337 my $jobdb_id = $hit->{id};
1338 my $sql_statement = "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1339 my $res = $job_db->del_dbentry($sql_statement);
1340 }
1342 $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1343 }
1346 sub watch_for_new_jobs {
1347 if($watch_for_new_jobs_in_progress == 0) {
1348 $watch_for_new_jobs_in_progress = 1;
1349 my ($kernel,$heap) = @_[KERNEL, HEAP];
1351 # check gosa job queue for jobs with executable timestamp
1352 my $timestamp = &get_time();
1353 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE status='waiting' AND (CAST (timestamp AS INTEGER)) < $timestamp ORDER BY timestamp";
1354 my $res = $job_db->exec_statement( $sql_statement );
1356 # Merge all new jobs that would do the same actions
1357 my @drops;
1358 my $hits;
1359 foreach my $hit (reverse @{$res} ) {
1360 my $macaddress= lc @{$hit}[8];
1361 my $headertag= @{$hit}[5];
1362 if(
1363 defined($hits->{$macaddress}) &&
1364 defined($hits->{$macaddress}->{$headertag}) &&
1365 defined($hits->{$macaddress}->{$headertag}[0])
1366 ) {
1367 push @drops, "DELETE FROM $job_queue_tn WHERE id = $hits->{$macaddress}->{$headertag}[0]";
1368 }
1369 $hits->{$macaddress}->{$headertag}= $hit;
1370 }
1372 # Delete new jobs with a matching job in state 'processing'
1373 foreach my $macaddress (keys %{$hits}) {
1374 foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1375 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1376 if(defined($jobdb_id)) {
1377 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND headertag='$jobdb_headertag' AND status='processing'";
1378 my $res = $job_db->exec_statement( $sql_statement );
1379 foreach my $hit (@{$res}) {
1380 push @drops, "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1381 }
1382 } else {
1383 daemon_log("J ERROR: Job without id exists for macaddress $macaddress!", 1);
1384 }
1385 }
1386 }
1388 # Commit deletion
1389 $job_db->exec_statementlist(\@drops);
1391 # Look for new jobs that could be executed
1392 foreach my $macaddress (keys %{$hits}) {
1394 # Look if there is an executing job
1395 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='processing'";
1396 my $res = $job_db->exec_statement( $sql_statement );
1398 # Skip new jobs for host if there is a processing job
1399 if(defined($res) and defined @{$res}[0]) {
1400 next;
1401 }
1403 foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1404 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1405 if(defined($jobdb_id)) {
1406 my $job_msg = @{$hits->{$macaddress}->{$jobdb_headertag}}[7];
1408 daemon_log("J DEBUG: its time to execute $job_msg", 7);
1409 my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1410 my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1412 # expect macaddress is unique!!!!!!
1413 my $target = $res_hash->{1}->{hostname};
1415 # change header
1416 $job_msg =~ s/<header>job_/<header>gosa_/;
1418 # add sqlite_id
1419 $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1421 $job_msg =~ /<header>(\S+)<\/header>/;
1422 my $header = $1 ;
1423 my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");
1425 # update status in job queue to 'processing'
1426 $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
1427 my $res = $job_db->update_dbentry($sql_statement);
1428 # TODO: abfangen ob alles in ordnung ist oder nicht, wenn nicht error schmeißen
1430 # We don't want parallel processing
1431 last;
1432 }
1433 }
1434 }
1436 $watch_for_new_jobs_in_progress = 0;
1437 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1438 }
1439 }
1442 sub watch_for_new_messages {
1443 my ($kernel,$heap) = @_[KERNEL, HEAP];
1444 my @coll_user_msg; # collection list of outgoing messages
1446 # check messaging_db for new incoming messages with executable timestamp
1447 my $timestamp = &get_time();
1448 my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS INTEGER))<$timestamp AND flag='n' AND direction='in' )";
1449 my $res = $messaging_db->exec_statement( $sql_statement );
1450 foreach my $hit (@{$res}) {
1452 # create outgoing messages
1453 my $message_to = @{$hit}[3];
1454 # translate message_to to plain login name
1455 my @message_to_l = split(/,/, $message_to);
1456 my %receiver_h;
1457 foreach my $receiver (@message_to_l) {
1458 if ($receiver =~ /^u_([\s\S]*)$/) {
1459 $receiver_h{$1} = 0;
1460 } elsif ($receiver =~ /^g_([\s\S]*)$/) {
1461 my $group_name = $1;
1462 # fetch all group members from ldap and add them to receiver hash
1463 my $ldap_handle = &get_ldap_handle();
1464 if (defined $ldap_handle) {
1465 my $mesg = $ldap_handle->search(
1466 base => $ldap_base,
1467 scope => 'sub',
1468 attrs => ['memberUid'],
1469 filter => "cn=$group_name",
1470 );
1471 if ($mesg->count) {
1472 my @entries = $mesg->entries;
1473 foreach my $entry (@entries) {
1474 my @receivers= $entry->get_value("memberUid");
1475 foreach my $receiver (@receivers) {
1476 $receiver_h{$1} = 0;
1477 }
1478 }
1479 }
1480 # translating errors ?
1481 if ($mesg->code) {
1482 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: $mesg->error", 1);
1483 }
1484 # ldap handle error ?
1485 } else {
1486 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: no ldap handle available", 1);
1487 }
1488 } else {
1489 my $sbjct = &encode_base64(@{$hit}[1]);
1490 my $msg = &encode_base64(@{$hit}[7]);
1491 &daemon_log("M WARNING: unknown receiver '$receiver' for a user-message '$sbjct - $msg'", 3);
1492 }
1493 }
1494 my @receiver_l = keys(%receiver_h);
1496 my $message_id = @{$hit}[0];
1498 #add each outgoing msg to messaging_db
1499 my $receiver;
1500 foreach $receiver (@receiver_l) {
1501 my $sql_statement = "INSERT INTO $messaging_tn (id, subject, message_from, message_to, flag, direction, delivery_time, message, timestamp) ".
1502 "VALUES ('".
1503 $message_id."', '". # id
1504 @{$hit}[1]."', '". # subject
1505 @{$hit}[2]."', '". # message_from
1506 $receiver."', '". # message_to
1507 "none"."', '". # flag
1508 "out"."', '". # direction
1509 @{$hit}[6]."', '". # delivery_time
1510 @{$hit}[7]."', '". # message
1511 $timestamp."'". # timestamp
1512 ")";
1513 &daemon_log("M DEBUG: $sql_statement", 1);
1514 my $res = $messaging_db->exec_statement($sql_statement);
1515 &daemon_log("M INFO: message '".@{$hit}[0]."' is prepared for delivery to receiver '$receiver'", 5);
1516 }
1518 # set incoming message to flag d=deliverd
1519 $sql_statement = "UPDATE $messaging_tn SET flag='p' WHERE id='$message_id'";
1520 &daemon_log("M DEBUG: $sql_statement", 7);
1521 $res = $messaging_db->update_dbentry($sql_statement);
1522 &daemon_log("M INFO: message '$message_id' is set to flag 'p' (processed)", 5);
1523 }
1525 $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1526 return;
1527 }
1529 sub watch_for_delivery_messages {
1530 my ($kernel, $heap) = @_[KERNEL, HEAP];
1532 # select outgoing messages
1533 my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( flag='p' AND direction='out' )";
1534 #&daemon_log("0 DEBUG: $sql", 7);
1535 my $res = $messaging_db->exec_statement( $sql_statement );
1537 # build out msg for each usr
1538 foreach my $hit (@{$res}) {
1539 my $receiver = @{$hit}[3];
1540 my $msg_id = @{$hit}[0];
1541 my $subject = @{$hit}[1];
1542 my $message = @{$hit}[7];
1544 # resolve usr -> host where usr is logged in
1545 my $sql = "SELECT * FROM $login_users_tn WHERE (user='$receiver')";
1546 #&daemon_log("0 DEBUG: $sql", 7);
1547 my $res = $login_users_db->exec_statement($sql);
1549 # reciver is logged in nowhere
1550 if (not ref(@$res[0]) eq "ARRAY") { next; }
1552 my $send_succeed = 0;
1553 foreach my $hit (@$res) {
1554 my $receiver_host = @$hit[0];
1555 &daemon_log("M DEBUG: user '$receiver' is logged in at host '$receiver_host'", 7);
1557 # fetch key to encrypt msg propperly for usr/host
1558 my $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$receiver_host')";
1559 &daemon_log("0 DEBUG: $sql", 7);
1560 my $res = $known_clients_db->exec_statement($sql);
1562 # host is already down
1563 if (not ref(@$res[0]) eq "ARRAY") { next; }
1565 # host is on
1566 my $receiver_key = @{@{$res}[0]}[2];
1567 my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
1568 my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data );
1569 my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0);
1570 if ($error == 0 ) {
1571 $send_succeed++ ;
1572 }
1573 }
1575 if ($send_succeed) {
1576 # set outgoing msg at db to deliverd
1577 my $sql = "UPDATE $messaging_tn SET flag='d' WHERE (id='$msg_id' AND direction='out' AND message_to='$receiver')";
1578 &daemon_log("0 DEBUG: $sql", 7);
1579 my $res = $messaging_db->exec_statement($sql);
1580 }
1581 }
1583 $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1584 return;
1585 }
1588 sub watch_for_done_messages {
1589 my ($kernel,$heap) = @_[KERNEL, HEAP];
1591 my $sql = "SELECT * FROM $messaging_tn WHERE (flag='p' AND direction='in')";
1592 #&daemon_log("0 DEBUG: $sql", 7);
1593 my $res = $messaging_db->exec_statement($sql);
1595 foreach my $hit (@{$res}) {
1596 my $msg_id = @{$hit}[0];
1598 my $sql = "SELECT * FROM $messaging_tn WHERE (id='$msg_id' AND direction='out' AND (NOT flag='s'))";
1599 #&daemon_log("0 DEBUG: $sql", 7);
1600 my $res = $messaging_db->exec_statement($sql);
1602 # not all usr msgs have been seen till now
1603 if ( ref(@$res[0]) eq "ARRAY") { next; }
1605 $sql = "DELETE FROM $messaging_tn WHERE (id='$msg_id')";
1606 #&daemon_log("0 DEBUG: $sql", 7);
1607 $res = $messaging_db->exec_statement($sql);
1609 }
1611 $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1612 return;
1613 }
1616 sub get_ldap_handle {
1617 my ($session_id) = @_;
1618 my $heap;
1619 my $ldap_handle;
1621 if (not defined $session_id ) { $session_id = 0 };
1622 if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
1624 if ($session_id == 0) {
1625 daemon_log("$session_id DEBUG: get_ldap_handle invoked without a session_id, create a new ldap_handle", 7);
1626 $ldap_handle = Net::LDAP->new( $ldap_uri );
1627 $ldap_handle->bind($ldap_admin_dn, password => $ldap_admin_password);
1629 } else {
1630 my $session_reference = $global_kernel->ID_id_to_session($session_id);
1631 if( defined $session_reference ) {
1632 $heap = $session_reference->get_heap();
1633 }
1635 if (not defined $heap) {
1636 daemon_log("$session_id DEBUG: cannot get heap for session_id '$session_id'", 7);
1637 return;
1638 }
1640 # TODO: This "if" is nonsense, because it doesn't prove that the
1641 # used handle is still valid - or if we've to reconnect...
1642 #if (not exists $heap->{ldap_handle}) {
1643 $ldap_handle = Net::LDAP->new( $ldap_uri );
1644 $ldap_handle->bind($ldap_admin_dn, password => $ldap_admin_password);
1645 $heap->{ldap_handle} = $ldap_handle;
1646 #}
1647 }
1648 return $ldap_handle;
1649 }
1652 sub change_fai_state {
1653 my ($st, $targets, $session_id) = @_;
1654 $session_id = 0 if not defined $session_id;
1655 # Set FAI state to localboot
1656 my %mapActions= (
1657 reboot => '',
1658 update => 'softupdate',
1659 localboot => 'localboot',
1660 reinstall => 'install',
1661 rescan => '',
1662 wake => '',
1663 memcheck => 'memcheck',
1664 sysinfo => 'sysinfo',
1665 install => 'install',
1666 );
1668 # Return if this is unknown
1669 if (!exists $mapActions{ $st }){
1670 daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1);
1671 return;
1672 }
1674 my $state= $mapActions{ $st };
1676 my $ldap_handle = &get_ldap_handle($session_id);
1677 if( defined($ldap_handle) ) {
1679 # Build search filter for hosts
1680 my $search= "(&(objectClass=GOhard)";
1681 foreach (@{$targets}){
1682 $search.= "(macAddress=$_)";
1683 }
1684 $search.= ")";
1686 # If there's any host inside of the search string, procress them
1687 if (!($search =~ /macAddress/)){
1688 daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);
1689 return;
1690 }
1692 # Perform search for Unit Tag
1693 my $mesg = $ldap_handle->search(
1694 base => $ldap_base,
1695 scope => 'sub',
1696 attrs => ['dn', 'FAIstate', 'objectClass'],
1697 filter => "$search"
1698 );
1700 if ($mesg->count) {
1701 my @entries = $mesg->entries;
1702 foreach my $entry (@entries) {
1703 # Only modify entry if it is not set to '$state'
1704 if ($entry->get_value("FAIstate") ne "$state"){
1705 daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
1706 my $result;
1707 my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
1708 if (exists $tmp{'FAIobject'}){
1709 if ($state eq ''){
1710 $result= $ldap_handle->modify($entry->dn, changes => [
1711 delete => [ FAIstate => [] ] ]);
1712 } else {
1713 $result= $ldap_handle->modify($entry->dn, changes => [
1714 replace => [ FAIstate => $state ] ]);
1715 }
1716 } elsif ($state ne ''){
1717 $result= $ldap_handle->modify($entry->dn, changes => [
1718 add => [ objectClass => 'FAIobject' ],
1719 add => [ FAIstate => $state ] ]);
1720 }
1722 # Errors?
1723 if ($result->code){
1724 daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
1725 }
1726 } else {
1727 daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 7);
1728 }
1729 }
1730 }
1731 # if no ldap handle defined
1732 } else {
1733 daemon_log("$session_id ERROR: no LDAP handle defined for update FAIstate", 1);
1734 }
1736 }
1739 sub change_goto_state {
1740 my ($st, $targets, $session_id) = @_;
1741 $session_id = 0 if not defined $session_id;
1743 # Switch on or off?
1744 my $state= $st eq 'active' ? 'active': 'locked';
1746 my $ldap_handle = &get_ldap_handle($session_id);
1747 if( defined($ldap_handle) ) {
1749 # Build search filter for hosts
1750 my $search= "(&(objectClass=GOhard)";
1751 foreach (@{$targets}){
1752 $search.= "(macAddress=$_)";
1753 }
1754 $search.= ")";
1756 # If there's any host inside of the search string, procress them
1757 if (!($search =~ /macAddress/)){
1758 return;
1759 }
1761 # Perform search for Unit Tag
1762 my $mesg = $ldap_handle->search(
1763 base => $ldap_base,
1764 scope => 'sub',
1765 attrs => ['dn', 'gotoMode'],
1766 filter => "$search"
1767 );
1769 if ($mesg->count) {
1770 my @entries = $mesg->entries;
1771 foreach my $entry (@entries) {
1773 # Only modify entry if it is not set to '$state'
1774 if ($entry->get_value("gotoMode") ne $state){
1776 daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
1777 my $result;
1778 $result= $ldap_handle->modify($entry->dn, changes => [
1779 replace => [ gotoMode => $state ] ]);
1781 # Errors?
1782 if ($result->code){
1783 &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
1784 }
1786 }
1787 }
1788 }
1790 }
1791 }
1794 sub run_create_fai_server_db {
1795 my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
1796 my $session_id = $session->ID;
1797 my $task = POE::Wheel::Run->new(
1798 Program => sub { &create_fai_server_db($table_name,$kernel, undef, $session_id) },
1799 StdoutEvent => "session_run_result",
1800 StderrEvent => "session_run_debug",
1801 CloseEvent => "session_run_done",
1802 );
1804 $heap->{task}->{ $task->ID } = $task;
1805 return;
1806 }
1809 sub create_fai_server_db {
1810 my ($table_name, $kernel, $dont_create_packages_list, $session_id) = @_;
1811 my $result;
1813 if (not defined $session_id) { $session_id = 0; }
1814 my $ldap_handle = &get_ldap_handle();
1815 if(defined($ldap_handle)) {
1816 daemon_log("$session_id INFO: create_fai_server_db: start", 5);
1817 my $mesg= $ldap_handle->search(
1818 base => $ldap_base,
1819 scope => 'sub',
1820 attrs => ['FAIrepository', 'gosaUnitTag'],
1821 filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
1822 );
1823 if($mesg->{'resultCode'} == 0 &&
1824 $mesg->count != 0) {
1825 foreach my $entry (@{$mesg->{entries}}) {
1826 if($entry->exists('FAIrepository')) {
1827 # Add an entry for each Repository configured for server
1828 foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
1829 my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
1830 my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
1831 $result= $fai_server_db->add_dbentry( {
1832 table => $table_name,
1833 primkey => ['server', 'release', 'tag'],
1834 server => $tmp_url,
1835 release => $tmp_release,
1836 sections => $tmp_sections,
1837 tag => (length($tmp_tag)>0)?$tmp_tag:"",
1838 } );
1839 }
1840 }
1841 }
1842 }
1843 daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
1845 # TODO: Find a way to post the 'create_packages_list_db' event
1846 if(not defined($dont_create_packages_list)) {
1847 &create_packages_list_db(undef, undef, $session_id);
1848 }
1849 }
1851 $ldap_handle->disconnect;
1852 return $result;
1853 }
1856 sub run_create_fai_release_db {
1857 my ($session, $heap, $table_name) = @_[SESSION, HEAP, ARG0];
1858 my $session_id = $session->ID;
1859 my $task = POE::Wheel::Run->new(
1860 Program => sub { &create_fai_release_db($table_name, $session_id) },
1861 StdoutEvent => "session_run_result",
1862 StderrEvent => "session_run_debug",
1863 CloseEvent => "session_run_done",
1864 );
1866 $heap->{task}->{ $task->ID } = $task;
1867 return;
1868 }
1871 sub create_fai_release_db {
1872 my ($table_name, $session_id) = @_;
1873 my $result;
1875 # used for logging
1876 if (not defined $session_id) { $session_id = 0; }
1878 my $ldap_handle = &get_ldap_handle();
1879 if(defined($ldap_handle)) {
1880 daemon_log("$session_id INFO: create_fai_release_db: start",5);
1881 my $mesg= $ldap_handle->search(
1882 base => $ldap_base,
1883 scope => 'sub',
1884 attrs => [],
1885 filter => "(&(objectClass=organizationalUnit)(ou=fai))",
1886 );
1887 if($mesg->{'resultCode'} == 0 &&
1888 $mesg->count != 0) {
1889 # Walk through all possible FAI container ou's
1890 my @sql_list;
1891 my $timestamp= &get_time();
1892 foreach my $ou (@{$mesg->{entries}}) {
1893 my $tmp_classes= resolve_fai_classes($ou->dn, $ldap_handle, $session_id);
1894 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
1895 my @tmp_array=get_fai_release_entries($tmp_classes);
1896 if(@tmp_array) {
1897 foreach my $entry (@tmp_array) {
1898 if(defined($entry) && ref($entry) eq 'HASH') {
1899 my $sql=
1900 "INSERT INTO $table_name "
1901 ."(timestamp, release, class, type, state) VALUES ("
1902 .$timestamp.","
1903 ."'".$entry->{'release'}."',"
1904 ."'".$entry->{'class'}."',"
1905 ."'".$entry->{'type'}."',"
1906 ."'".$entry->{'state'}."')";
1907 push @sql_list, $sql;
1908 }
1909 }
1910 }
1911 }
1912 }
1914 daemon_log("$session_id DEBUG: Inserting ".scalar @sql_list." entries to DB",8);
1915 if(@sql_list) {
1916 unshift @sql_list, "VACUUM";
1917 unshift @sql_list, "DELETE FROM $table_name";
1918 $fai_release_db->exec_statementlist(\@sql_list);
1919 }
1920 daemon_log("$session_id DEBUG: Done with inserting",7);
1921 }
1922 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
1923 }
1924 $ldap_handle->disconnect;
1925 return $result;
1926 }
1928 sub get_fai_types {
1929 my $tmp_classes = shift || return undef;
1930 my @result;
1932 foreach my $type(keys %{$tmp_classes}) {
1933 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
1934 my $entry = {
1935 type => $type,
1936 state => $tmp_classes->{$type}[0],
1937 };
1938 push @result, $entry;
1939 }
1940 }
1942 return @result;
1943 }
1945 sub get_fai_state {
1946 my $result = "";
1947 my $tmp_classes = shift || return $result;
1949 foreach my $type(keys %{$tmp_classes}) {
1950 if(defined($tmp_classes->{$type}[0])) {
1951 $result = $tmp_classes->{$type}[0];
1953 # State is equal for all types in class
1954 last;
1955 }
1956 }
1958 return $result;
1959 }
1961 sub resolve_fai_classes {
1962 my ($fai_base, $ldap_handle, $session_id) = @_;
1963 if (not defined $session_id) { $session_id = 0; }
1964 my $result;
1965 my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
1966 my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
1967 my $fai_classes;
1969 daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base",7);
1970 my $mesg= $ldap_handle->search(
1971 base => $fai_base,
1972 scope => 'sub',
1973 attrs => ['cn','objectClass','FAIstate'],
1974 filter => $fai_filter,
1975 );
1976 daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries",7);
1978 if($mesg->{'resultCode'} == 0 &&
1979 $mesg->count != 0) {
1980 foreach my $entry (@{$mesg->{entries}}) {
1981 if($entry->exists('cn')) {
1982 my $tmp_dn= $entry->dn();
1984 # Skip classname and ou dn parts for class
1985 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?),$fai_base$/;
1987 # Skip classes without releases
1988 if((!defined($tmp_release)) || length($tmp_release)==0) {
1989 next;
1990 }
1992 my $tmp_cn= $entry->get_value('cn');
1993 my $tmp_state= $entry->get_value('FAIstate');
1995 my $tmp_type;
1996 # Get FAI type
1997 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
1998 if(grep $_ eq $oclass, @possible_fai_classes) {
1999 $tmp_type= $oclass;
2000 last;
2001 }
2002 }
2004 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2005 # A Subrelease
2006 my @sub_releases = split(/,/, $tmp_release);
2008 # Walk through subreleases and build hash tree
2009 my $hash;
2010 while(my $tmp_sub_release = pop @sub_releases) {
2011 $hash .= "\{'$tmp_sub_release'\}->";
2012 }
2013 eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
2014 } else {
2015 # A branch, no subrelease
2016 push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
2017 }
2018 } elsif (!$entry->exists('cn')) {
2019 my $tmp_dn= $entry->dn();
2020 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?),$fai_base$/;
2022 # Skip classes without releases
2023 if((!defined($tmp_release)) || length($tmp_release)==0) {
2024 next;
2025 }
2027 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2028 # A Subrelease
2029 my @sub_releases= split(/,/, $tmp_release);
2031 # Walk through subreleases and build hash tree
2032 my $hash;
2033 while(my $tmp_sub_release = pop @sub_releases) {
2034 $hash .= "\{'$tmp_sub_release'\}->";
2035 }
2036 # Remove the last two characters
2037 chop($hash);
2038 chop($hash);
2040 eval('$fai_classes->'.$hash.'= {}');
2041 } else {
2042 # A branch, no subrelease
2043 if(!exists($fai_classes->{$tmp_release})) {
2044 $fai_classes->{$tmp_release} = {};
2045 }
2046 }
2047 }
2048 }
2050 # The hash is complete, now we can honor the copy-on-write based missing entries
2051 foreach my $release (keys %$fai_classes) {
2052 $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
2053 }
2054 }
2055 return $result;
2056 }
2058 sub apply_fai_inheritance {
2059 my $fai_classes = shift || return {};
2060 my $tmp_classes;
2062 # Get the classes from the branch
2063 foreach my $class (keys %{$fai_classes}) {
2064 # Skip subreleases
2065 if($class =~ /^ou=.*$/) {
2066 next;
2067 } else {
2068 $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
2069 }
2070 }
2072 # Apply to each subrelease
2073 foreach my $subrelease (keys %{$fai_classes}) {
2074 if($subrelease =~ /ou=/) {
2075 foreach my $tmp_class (keys %{$tmp_classes}) {
2076 if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
2077 $fai_classes->{$subrelease}->{$tmp_class} =
2078 deep_copy($tmp_classes->{$tmp_class});
2079 } else {
2080 foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
2081 if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
2082 $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
2083 deep_copy($tmp_classes->{$tmp_class}->{$type});
2084 }
2085 }
2086 }
2087 }
2088 }
2089 }
2091 # Find subreleases in deeper levels
2092 foreach my $subrelease (keys %{$fai_classes}) {
2093 if($subrelease =~ /ou=/) {
2094 foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
2095 if($subsubrelease =~ /ou=/) {
2096 apply_fai_inheritance($fai_classes->{$subrelease});
2097 }
2098 }
2099 }
2100 }
2102 return $fai_classes;
2103 }
2105 sub get_fai_release_entries {
2106 my $tmp_classes = shift || return;
2107 my $parent = shift || "";
2108 my @result = shift || ();
2110 foreach my $entry (keys %{$tmp_classes}) {
2111 if(defined($entry)) {
2112 if($entry =~ /^ou=.*$/) {
2113 my $release_name = $entry;
2114 $release_name =~ s/ou=//g;
2115 if(length($parent)>0) {
2116 $release_name = $parent."/".$release_name;
2117 }
2118 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2119 foreach my $bufentry(@bufentries) {
2120 push @result, $bufentry;
2121 }
2122 } else {
2123 my @types = get_fai_types($tmp_classes->{$entry});
2124 foreach my $type (@types) {
2125 push @result,
2126 {
2127 'class' => $entry,
2128 'type' => $type->{'type'},
2129 'release' => $parent,
2130 'state' => $type->{'state'},
2131 };
2132 }
2133 }
2134 }
2135 }
2137 return @result;
2138 }
2140 sub deep_copy {
2141 my $this = shift;
2142 if (not ref $this) {
2143 $this;
2144 } elsif (ref $this eq "ARRAY") {
2145 [map deep_copy($_), @$this];
2146 } elsif (ref $this eq "HASH") {
2147 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2148 } else { die "what type is $_?" }
2149 }
2152 sub session_run_result {
2153 my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];
2154 $kernel->sig(CHLD => "child_reap");
2155 }
2157 sub session_run_debug {
2158 my $result = $_[ARG0];
2159 print STDERR "$result\n";
2160 }
2162 sub session_run_done {
2163 my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2164 delete $heap->{task}->{$task_id};
2165 }
2168 sub create_sources_list {
2169 my $session_id = shift;
2170 my $ldap_handle = &main::get_ldap_handle;
2171 my $result="/tmp/gosa_si_tmp_sources_list";
2173 # Remove old file
2174 if(stat($result)) {
2175 unlink($result);
2176 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7);
2177 }
2179 my $fh;
2180 open($fh, ">$result");
2181 if (not defined $fh) {
2182 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7);
2183 return undef;
2184 }
2185 if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2186 my $mesg=$ldap_handle->search(
2187 base => $main::ldap_server_dn,
2188 scope => 'base',
2189 attrs => 'FAIrepository',
2190 filter => 'objectClass=FAIrepositoryServer'
2191 );
2192 if($mesg->count) {
2193 foreach my $entry(@{$mesg->{'entries'}}) {
2194 foreach my $value(@{$entry->get_value('FAIrepository', asref => 1)}) {
2195 my ($server, $tag, $release, $sections)= split /\|/, $value;
2196 my $line = "deb $server $release";
2197 $sections =~ s/,/ /g;
2198 $line.= " $sections";
2199 print $fh $line."\n";
2200 }
2201 }
2202 }
2203 } else {
2204 if (defined $main::ldap_server_dn){
2205 &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1);
2206 } else {
2207 &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2208 }
2209 }
2210 close($fh);
2212 return $result;
2213 }
2216 sub run_create_packages_list_db {
2217 my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2218 my $session_id = $session->ID;
2220 my $task = POE::Wheel::Run->new(
2221 Priority => +20,
2222 Program => sub {&create_packages_list_db(undef, undef, $session_id)},
2223 StdoutEvent => "session_run_result",
2224 StderrEvent => "session_run_debug",
2225 CloseEvent => "session_run_done",
2226 );
2227 $heap->{task}->{ $task->ID } = $task;
2228 }
2231 sub create_packages_list_db {
2232 my ($ldap_handle, $sources_file, $session_id) = @_;
2234 # it should not be possible to trigger a recreation of packages_list_db
2235 # while packages_list_db is under construction, so set flag packages_list_under_construction
2236 # which is tested befor recreation can be started
2237 if (-r $packages_list_under_construction) {
2238 daemon_log("$session_id WARNING: packages_list_db is right now under construction, please wait until this process is finished", 3);
2239 return;
2240 } else {
2241 daemon_log("$session_id INFO: create_packages_list_db: start", 5);
2242 # set packages_list_under_construction to true
2243 system("touch $packages_list_under_construction");
2244 @packages_list_statements=();
2245 }
2247 if (not defined $session_id) { $session_id = 0; }
2248 if (not defined $ldap_handle) {
2249 $ldap_handle= &get_ldap_handle();
2251 if (not defined $ldap_handle) {
2252 daemon_log("$session_id ERROR: no ldap_handle available to create_packages_list_db", 1);
2253 unlink($packages_list_under_construction);
2254 return;
2255 }
2256 }
2257 if (not defined $sources_file) {
2258 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5);
2259 $sources_file = &create_sources_list($session_id);
2260 }
2262 if (not defined $sources_file) {
2263 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1);
2264 unlink($packages_list_under_construction);
2265 return;
2266 }
2268 my $line;
2270 open(CONFIG, "<$sources_file") or do {
2271 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2272 unlink($packages_list_under_construction);
2273 return;
2274 };
2276 # Read lines
2277 while ($line = <CONFIG>){
2278 # Unify
2279 chop($line);
2280 $line =~ s/^\s+//;
2281 $line =~ s/^\s+/ /;
2283 # Strip comments
2284 $line =~ s/#.*$//g;
2286 # Skip empty lines
2287 if ($line =~ /^\s*$/){
2288 next;
2289 }
2291 # Interpret deb line
2292 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2293 my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2294 my $section;
2295 foreach $section (split(' ', $sections)){
2296 &parse_package_info( $baseurl, $dist, $section, $session_id );
2297 }
2298 }
2299 }
2301 close (CONFIG);
2303 find(\&cleanup_and_extract, keys( %repo_dirs ));
2304 &main::strip_packages_list_statements();
2305 unshift @packages_list_statements, "VACUUM";
2306 $packages_list_db->exec_statementlist(\@packages_list_statements);
2307 unlink($packages_list_under_construction);
2308 daemon_log("$session_id INFO: create_packages_list_db: finished", 5);
2309 return;
2310 }
2312 # This function should do some intensive task to minimize the db-traffic
2313 sub strip_packages_list_statements {
2314 my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2315 my @new_statement_list=();
2316 my $hash;
2317 my $insert_hash;
2318 my $update_hash;
2319 my $delete_hash;
2320 my $local_timestamp=get_time();
2322 foreach my $existing_entry (@existing_entries) {
2323 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
2324 }
2326 foreach my $statement (@packages_list_statements) {
2327 if($statement =~ /^INSERT/i) {
2328 # Assign the values from the insert statement
2329 my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~
2330 /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
2331 if(exists($hash->{$distribution}->{$package}->{$version})) {
2332 # If section or description has changed, update the DB
2333 if(
2334 (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or
2335 (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
2336 ) {
2337 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
2338 }
2339 } else {
2340 # Insert a non-existing entry to db
2341 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2342 }
2343 } elsif ($statement =~ /^UPDATE/i) {
2344 my ($template,$package,$version) = ($1,$2,$3) if $statement =~
2345 /^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;
2346 foreach my $distribution (keys %{$hash}) {
2347 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
2348 # update the insertion hash to execute only one query per package (insert instead insert+update)
2349 @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
2350 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
2351 if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
2352 my $section;
2353 my $description;
2354 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
2355 length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
2356 $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
2357 }
2358 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2359 $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
2360 }
2361 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2362 }
2363 }
2364 }
2365 }
2366 }
2368 # TODO: Check for orphaned entries
2370 # unroll the insert_hash
2371 foreach my $distribution (keys %{$insert_hash}) {
2372 foreach my $package (keys %{$insert_hash->{$distribution}}) {
2373 foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
2374 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
2375 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
2376 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
2377 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
2378 ."'$local_timestamp')";
2379 }
2380 }
2381 }
2383 # unroll the update hash
2384 foreach my $distribution (keys %{$update_hash}) {
2385 foreach my $package (keys %{$update_hash->{$distribution}}) {
2386 foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
2387 my $set = "";
2388 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
2389 $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
2390 }
2391 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2392 $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
2393 }
2394 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
2395 $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
2396 }
2397 if(defined($set) and length($set) > 0) {
2398 $set .= "timestamp = '$local_timestamp'";
2399 } else {
2400 next;
2401 }
2402 push @new_statement_list,
2403 "UPDATE $main::packages_list_tn SET $set WHERE"
2404 ." distribution = '$distribution'"
2405 ." AND package = '$package'"
2406 ." AND version = '$version'";
2407 }
2408 }
2409 }
2411 @packages_list_statements = @new_statement_list;
2412 }
2415 sub parse_package_info {
2416 my ($baseurl, $dist, $section, $session_id)= @_;
2417 my ($package);
2418 if (not defined $session_id) { $session_id = 0; }
2419 my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
2420 $repo_dirs{ "${repo_path}/pool" } = 1;
2422 foreach $package ("Packages.gz"){
2423 daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
2424 get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
2425 parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
2426 }
2428 }
2431 sub get_package {
2432 my ($url, $dest, $session_id)= @_;
2433 if (not defined $session_id) { $session_id = 0; }
2435 my $tpath = dirname($dest);
2436 -d "$tpath" || mkpath "$tpath";
2438 # This is ugly, but I've no time to take a look at "how it works in perl"
2439 if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
2440 system("gunzip -cd '$dest' > '$dest.in'");
2441 daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 5);
2442 unlink($dest);
2443 daemon_log("$session_id DEBUG: delete file '$dest'", 5);
2444 } else {
2445 daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' failed!", 1);
2446 }
2447 return 0;
2448 }
2451 sub parse_package {
2452 my ($path, $dist, $srv_path, $session_id)= @_;
2453 if (not defined $session_id) { $session_id = 0;}
2454 my ($package, $version, $section, $description);
2455 my $PACKAGES;
2456 my $timestamp = &get_time();
2458 if(not stat("$path.in")) {
2459 daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
2460 return;
2461 }
2463 open($PACKAGES, "<$path.in");
2464 if(not defined($PACKAGES)) {
2465 daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1);
2466 return;
2467 }
2469 # Read lines
2470 while (<$PACKAGES>){
2471 my $line = $_;
2472 # Unify
2473 chop($line);
2475 # Use empty lines as a trigger
2476 if ($line =~ /^\s*$/){
2477 my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
2478 push(@packages_list_statements, $sql);
2479 $package = "none";
2480 $version = "none";
2481 $section = "none";
2482 $description = "none";
2483 next;
2484 }
2486 # Trigger for package name
2487 if ($line =~ /^Package:\s/){
2488 ($package)= ($line =~ /^Package: (.*)$/);
2489 next;
2490 }
2492 # Trigger for version
2493 if ($line =~ /^Version:\s/){
2494 ($version)= ($line =~ /^Version: (.*)$/);
2495 next;
2496 }
2498 # Trigger for description
2499 if ($line =~ /^Description:\s/){
2500 ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
2501 next;
2502 }
2504 # Trigger for section
2505 if ($line =~ /^Section:\s/){
2506 ($section)= ($line =~ /^Section: (.*)$/);
2507 next;
2508 }
2510 # Trigger for filename
2511 if ($line =~ /^Filename:\s/){
2512 my ($filename) = ($line =~ /^Filename: (.*)$/);
2513 store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
2514 next;
2515 }
2516 }
2518 close( $PACKAGES );
2519 unlink( "$path.in" );
2520 &main::daemon_log("$session_id DEBUG: unlink '$path.in'", 1);
2521 }
2524 sub store_fileinfo {
2525 my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
2527 my %fileinfo = (
2528 'package' => $package,
2529 'dist' => $dist,
2530 'version' => $vers,
2531 );
2533 $repo_files{ "${srvdir}/$file" } = \%fileinfo;
2534 }
2537 sub cleanup_and_extract {
2538 my $fileinfo = $repo_files{ $File::Find::name };
2540 if( defined $fileinfo ) {
2542 my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
2543 my $sql;
2544 my $package = $fileinfo->{ 'package' };
2545 my $newver = $fileinfo->{ 'version' };
2547 mkpath($dir);
2548 system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
2550 if( -f "$dir/DEBIAN/templates" ) {
2552 daemon_log("DEBUG: Found debconf templates in '$package' - $newver", 5);
2554 my $tmpl= "";
2555 {
2556 local $/=undef;
2557 open FILE, "$dir/DEBIAN/templates";
2558 $tmpl = &encode_base64(<FILE>);
2559 close FILE;
2560 }
2561 rmtree("$dir/DEBIAN/templates");
2563 $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
2564 push @packages_list_statements, $sql;
2565 }
2566 }
2568 return;
2569 }
2572 #==== MAIN = main ==============================================================
2573 # parse commandline options
2574 Getopt::Long::Configure( "bundling" );
2575 GetOptions("h|help" => \&usage,
2576 "c|config=s" => \$cfg_file,
2577 "f|foreground" => \$foreground,
2578 "v|verbose+" => \$verbose,
2579 "no-bus+" => \$no_bus,
2580 "no-arp+" => \$no_arp,
2581 );
2583 # read and set config parameters
2584 &check_cmdline_param ;
2585 &read_configfile;
2586 &check_pid;
2588 $SIG{CHLD} = 'IGNORE';
2590 # forward error messages to logfile
2591 if( ! $foreground ) {
2592 open( STDIN, '+>/dev/null' );
2593 open( STDOUT, '+>&STDIN' );
2594 open( STDERR, '+>&STDIN' );
2595 }
2597 # Just fork, if we are not in foreground mode
2598 if( ! $foreground ) {
2599 chdir '/' or die "Can't chdir to /: $!";
2600 $pid = fork;
2601 setsid or die "Can't start a new session: $!";
2602 umask 0;
2603 } else {
2604 $pid = $$;
2605 }
2607 # Do something useful - put our PID into the pid_file
2608 if( 0 != $pid ) {
2609 open( LOCK_FILE, ">$pid_file" );
2610 print LOCK_FILE "$pid\n";
2611 close( LOCK_FILE );
2612 if( !$foreground ) {
2613 exit( 0 )
2614 };
2615 }
2617 # parse head url and revision from svn
2618 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
2619 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
2620 $server_headURL = defined $1 ? $1 : 'unknown' ;
2621 $server_revision = defined $2 ? $2 : 'unknown' ;
2622 if ($server_headURL =~ /\/tag\// ||
2623 $server_headURL =~ /\/branches\// ) {
2624 $server_status = "stable";
2625 } else {
2626 $server_status = "developmental" ;
2627 }
2630 daemon_log(" ", 1);
2631 daemon_log("$0 started!", 1);
2632 daemon_log("status: $server_status", 1);
2633 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1);
2635 if ($no_bus > 0) {
2636 $bus_activ = "false"
2637 }
2639 # connect to incoming_db
2640 unlink($incoming_file_name);
2641 $incoming_db = GOSA::DBsqlite->new($incoming_file_name);
2642 $incoming_db->create_table($incoming_tn, \@incoming_col_names);
2644 # connect to gosa-si job queue
2645 $job_db = GOSA::DBsqlite->new($job_queue_file_name);
2646 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
2648 # connect to known_clients_db
2649 $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
2650 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
2652 # connect to known_server_db
2653 $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
2654 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
2656 # connect to login_usr_db
2657 $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
2658 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
2660 # connect to fai_server_db and fai_release_db
2661 unlink($fai_server_file_name);
2662 $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
2663 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
2665 unlink($fai_release_file_name);
2666 $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
2667 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
2669 # connect to packages_list_db
2670 #unlink($packages_list_file_name);
2671 unlink($packages_list_under_construction);
2672 $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
2673 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
2675 # connect to messaging_db
2676 $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
2677 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
2680 # create xml object used for en/decrypting
2681 $xml = new XML::Simple();
2683 # create socket for incoming xml messages
2685 POE::Component::Server::TCP->new(
2686 Port => $server_port,
2687 ClientInput => sub {
2688 my ($kernel, $input) = @_[KERNEL, ARG0];
2689 push(@tasks, $input);
2690 push(@msgs_to_decrypt, $input);
2691 $kernel->yield("msg_to_decrypt");
2692 $kernel->yield("next_task");
2693 },
2694 InlineStates => {
2695 next_task => \&next_task,
2696 msg_to_decrypt => \&msg_to_decrypt,
2697 task_result => \&handle_task_result,
2698 task_done => \&handle_task_done,
2699 task_debug => \&handle_task_debug,
2700 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!" },
2701 }
2702 );
2704 daemon_log("start socket for incoming xml messages at port '$server_port' ", 1);
2706 # create session for repeatedly checking the job queue for jobs
2707 POE::Session->create(
2708 inline_states => {
2709 _start => \&_start,
2710 sig_handler => \&sig_handler,
2711 watch_for_new_messages => \&watch_for_new_messages,
2712 watch_for_delivery_messages => \&watch_for_delivery_messages,
2713 watch_for_done_messages => \&watch_for_done_messages,
2714 watch_for_new_jobs => \&watch_for_new_jobs,
2715 watch_for_done_jobs => \&watch_for_done_jobs,
2716 create_packages_list_db => \&run_create_packages_list_db,
2717 create_fai_server_db => \&run_create_fai_server_db,
2718 create_fai_release_db => \&run_create_fai_release_db,
2719 session_run_result => \&session_run_result,
2720 session_run_debug => \&session_run_debug,
2721 session_run_done => \&session_run_done,
2722 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!" },
2723 }
2724 );
2727 # import all modules
2728 &import_modules;
2730 # check wether all modules are gosa-si valid passwd check
2732 POE::Kernel->run();
2733 exit;