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, $ClientPackages_key,
86 $arp_activ, $gosa_unit_tag,
87 $GosaPackages_key, $gosa_ip, $gosa_port, $gosa_timeout,
88 $foreign_server_string, $server_domain, $ServerPackages_key, $foreign_servers_register_delay,
89 );
91 # additional variable which should be globaly accessable
92 our $server_address;
93 our $server_mac_address;
94 our $bus_address;
95 our $gosa_address;
96 our $no_bus;
97 our $no_arp;
98 our $verbose;
99 our $forground;
100 our $cfg_file;
101 our ($ldap_uri, $ldap_base, $ldap_admin_dn, $ldap_admin_password, $ldap_server_dn);
104 # specifies the verbosity of the daemon_log
105 $verbose = 0 ;
107 # if foreground is not null, script will be not forked to background
108 $foreground = 0 ;
110 # specifies the timeout seconds while checking the online status of a registrating client
111 $ping_timeout = 5;
113 $no_bus = 0;
114 $bus_activ = "true";
115 $no_arp = 0;
116 my $packages_list_under_construction = "/tmp/packages_list_creation_in_progress";
117 my @packages_list_statements;
118 my $watch_for_new_jobs_in_progress = 0;
120 # holds all incoming decrypted messages
121 our $incoming_db;
122 our $incoming_tn = 'incoming';
123 my $incoming_file_name;
124 my @incoming_col_names = ("id INTEGER PRIMARY KEY",
125 "timestamp DEFAULT 'none'",
126 "headertag DEFAULT 'none'",
127 "targettag DEFAULT 'none'",
128 "xmlmessage DEFAULT 'none'",
129 "module DEFAULT 'none'",
130 );
132 # holds all gosa jobs
133 our $job_db;
134 our $job_queue_tn = 'jobs';
135 my $job_queue_file_name;
136 my @job_queue_col_names = ("id INTEGER PRIMARY KEY",
137 "timestamp DEFAULT 'none'",
138 "status DEFAULT 'none'",
139 "result DEFAULT 'none'",
140 "progress DEFAULT 'none'",
141 "headertag DEFAULT 'none'",
142 "targettag DEFAULT 'none'",
143 "xmlmessage DEFAULT 'none'",
144 "macaddress DEFAULT 'none'",
145 "plainname DEFAULT 'none'",
146 );
148 # holds all other gosa-sd as well as the gosa-sd-bus
149 our $known_server_db;
150 our $known_server_tn = "known_server";
151 my $known_server_file_name;
152 my @known_server_col_names = ("hostname", "status", "hostkey", "timestamp");
154 # holds all registrated clients
155 our $known_clients_db;
156 our $known_clients_tn = "known_clients";
157 my $known_clients_file_name;
158 my @known_clients_col_names = ("hostname", "status", "hostkey", "timestamp", "macaddress", "events");
160 # holds all logged in user at each client
161 our $login_users_db;
162 our $login_users_tn = "login_users";
163 my $login_users_file_name;
164 my @login_users_col_names = ("client", "user", "timestamp");
166 # holds all fai server, the debian release and tag
167 our $fai_server_db;
168 our $fai_server_tn = "fai_server";
169 my $fai_server_file_name;
170 our @fai_server_col_names = ("timestamp", "server", "release", "sections", "tag");
172 our $fai_release_db;
173 our $fai_release_tn = "fai_release";
174 my $fai_release_file_name;
175 our @fai_release_col_names = ("timestamp", "release", "class", "type", "state");
177 # holds all packages available from different repositories
178 our $packages_list_db;
179 our $packages_list_tn = "packages_list";
180 my $packages_list_file_name;
181 our @packages_list_col_names = ("distribution", "package", "version", "section", "description", "template", "timestamp");
182 my $outdir = "/tmp/packages_list_db";
183 my $arch = "i386";
185 # holds all messages which should be delivered to a user
186 our $messaging_db;
187 our $messaging_tn = "messaging";
188 our @messaging_col_names = ("id INTEGER", "subject", "message_from", "message_to",
189 "flag", "direction", "delivery_time", "message", "timestamp" );
190 my $messaging_file_name;
192 # path to directory to store client install log files
193 our $client_fai_log_dir = "/var/log/fai";
195 # queue which stores taskes until one of the $max_children children are ready to process the task
196 my @tasks = qw();
197 my @msgs_to_decrypt = qw();
198 my $max_children = 2;
201 %cfg_defaults = (
202 "general" => {
203 "log-file" => [\$log_file, "/var/run/".$prg.".log"],
204 "pid-file" => [\$pid_file, "/var/run/".$prg.".pid"],
205 },
206 "bus" => {
207 "activ" => [\$bus_activ, "true"],
208 },
209 "server" => {
210 "port" => [\$server_port, "20081"],
211 "known-clients" => [\$known_clients_file_name, '/var/lib/gosa-si/clients.db' ],
212 "known-servers" => [\$known_server_file_name, '/var/lib/gosa-si/servers.db'],
213 "incoming" => [\$incoming_file_name, '/var/lib/gosa-si/incoming.db'],
214 "login-users" => [\$login_users_file_name, '/var/lib/gosa-si/users.db'],
215 "fai-server" => [\$fai_server_file_name, '/var/lib/gosa-si/fai_server.db'],
216 "fai-release" => [\$fai_release_file_name, '/var/lib/gosa-si/fai_release.db'],
217 "packages-list" => [\$packages_list_file_name, '/var/lib/gosa-si/packages.db'],
218 "messaging" => [\$messaging_file_name, '/var/lib/gosa-si/messaging.db'],
219 "source-list" => [\$sources_list, '/etc/apt/sources.list'],
220 "repo-path" => [\$repo_path, '/srv/www/repository'],
221 "ldap-uri" => [\$ldap_uri, ""],
222 "ldap-base" => [\$ldap_base, ""],
223 "ldap-admin-dn" => [\$ldap_admin_dn, ""],
224 "ldap-admin-password" => [\$ldap_admin_password, ""],
225 "gosa-unit-tag" => [\$gosa_unit_tag, ""],
226 "max-clients" => [\$max_clients, 10],
227 },
228 "GOsaPackages" => {
229 "ip" => [\$gosa_ip, "0.0.0.0"],
230 "port" => [\$gosa_port, "20082"],
231 "job-queue" => [\$job_queue_file_name, '/var/lib/gosa-si/jobs.db'],
232 "job-queue-loop-delay" => [\$job_queue_loop_delay, 3],
233 "messaging-db-loop-delay" => [\$messaging_db_loop_delay, 3],
234 "key" => [\$GosaPackages_key, "none"],
235 },
236 "ClientPackages" => {
237 "key" => [\$ClientPackages_key, "none"],
238 },
239 "ServerPackages"=> {
240 "address" => [\$foreign_server_string, ""],
241 "domain" => [\$server_domain, ""],
242 "key" => [\$ServerPackages_key, "none"],
243 "key-lifetime" => [\$foreign_servers_register_delay, 600],
244 }
245 );
248 #=== FUNCTION ================================================================
249 # NAME: usage
250 # PARAMETERS: nothing
251 # RETURNS: nothing
252 # DESCRIPTION: print out usage text to STDERR
253 #===============================================================================
254 sub usage {
255 print STDERR << "EOF" ;
256 usage: $prg [-hvf] [-c config]
258 -h : this (help) message
259 -c <file> : config file
260 -f : foreground, process will not be forked to background
261 -v : be verbose (multiple to increase verbosity)
262 -no-bus : starts $prg without connection to bus
263 -no-arp : starts $prg without connection to arp module
265 EOF
266 print "\n" ;
267 }
270 #=== FUNCTION ================================================================
271 # NAME: read_configfile
272 # PARAMETERS: cfg_file - string -
273 # RETURNS: nothing
274 # DESCRIPTION: read cfg_file and set variables
275 #===============================================================================
276 sub read_configfile {
277 my $cfg;
278 if( defined( $cfg_file) && ( (-s $cfg_file) > 0 )) {
279 if( -r $cfg_file ) {
280 $cfg = Config::IniFiles->new( -file => $cfg_file );
281 } else {
282 print STDERR "Couldn't read config file!\n";
283 }
284 } else {
285 $cfg = Config::IniFiles->new() ;
286 }
287 foreach my $section (keys %cfg_defaults) {
288 foreach my $param (keys %{$cfg_defaults{ $section }}) {
289 my $pinfo = $cfg_defaults{ $section }{ $param };
290 ${@$pinfo[ 0 ]} = $cfg->val( $section, $param, @$pinfo[ 1 ] );
291 }
292 }
293 }
296 #=== FUNCTION ================================================================
297 # NAME: logging
298 # PARAMETERS: level - string - default 'info'
299 # msg - string -
300 # facility - string - default 'LOG_DAEMON'
301 # RETURNS: nothing
302 # DESCRIPTION: function for logging
303 #===============================================================================
304 sub daemon_log {
305 # log into log_file
306 my( $msg, $level ) = @_;
307 if(not defined $msg) { return }
308 if(not defined $level) { $level = 1 }
309 if(defined $log_file){
310 open(LOG_HANDLE, ">>$log_file");
311 if(not defined open( LOG_HANDLE, ">>$log_file" )) {
312 print STDERR "cannot open $log_file: $!";
313 return }
314 chomp($msg);
315 $msg =~s/\n//g; # no newlines are allowed in log messages, this is important for later log parsing
316 if($level <= $verbose){
317 my ($seconds, $minutes, $hours, $monthday, $month,
318 $year, $weekday, $yearday, $sommertime) = localtime(time);
319 $hours = $hours < 10 ? $hours = "0".$hours : $hours;
320 $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
321 $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
322 my @monthnames = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
323 $month = $monthnames[$month];
324 $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
325 $year+=1900;
326 my $name = $prg;
328 my $log_msg = "$month $monthday $hours:$minutes:$seconds $name $msg\n";
329 print LOG_HANDLE $log_msg;
330 if( $foreground ) {
331 print STDERR $log_msg;
332 }
333 }
334 close( LOG_HANDLE );
335 }
336 }
339 #=== FUNCTION ================================================================
340 # NAME: check_cmdline_param
341 # PARAMETERS: nothing
342 # RETURNS: nothing
343 # DESCRIPTION: validates commandline parameter
344 #===============================================================================
345 sub check_cmdline_param () {
346 my $err_config;
347 my $err_counter = 0;
348 if(not defined($cfg_file)) {
349 $cfg_file = "/etc/gosa-si/server.conf";
350 if(! -r $cfg_file) {
351 $err_config = "please specify a config file";
352 $err_counter += 1;
353 }
354 }
355 if( $err_counter > 0 ) {
356 &usage( "", 1 );
357 if( defined( $err_config)) { print STDERR "$err_config\n"}
358 print STDERR "\n";
359 exit( -1 );
360 }
361 }
364 #=== FUNCTION ================================================================
365 # NAME: check_pid
366 # PARAMETERS: nothing
367 # RETURNS: nothing
368 # DESCRIPTION: handels pid processing
369 #===============================================================================
370 sub check_pid {
371 $pid = -1;
372 # Check, if we are already running
373 if( open(LOCK_FILE, "<$pid_file") ) {
374 $pid = <LOCK_FILE>;
375 if( defined $pid ) {
376 chomp( $pid );
377 if( -f "/proc/$pid/stat" ) {
378 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
379 if( $stat ) {
380 daemon_log("ERROR: Already running",1);
381 close( LOCK_FILE );
382 exit -1;
383 }
384 }
385 }
386 close( LOCK_FILE );
387 unlink( $pid_file );
388 }
390 # create a syslog msg if it is not to possible to open PID file
391 if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
392 my($msg) = "Couldn't obtain lockfile '$pid_file' ";
393 if (open(LOCK_FILE, '<', $pid_file)
394 && ($pid = <LOCK_FILE>))
395 {
396 chomp($pid);
397 $msg .= "(PID $pid)\n";
398 } else {
399 $msg .= "(unable to read PID)\n";
400 }
401 if( ! ($foreground) ) {
402 openlog( $0, "cons,pid", "daemon" );
403 syslog( "warning", $msg );
404 closelog();
405 }
406 else {
407 print( STDERR " $msg " );
408 }
409 exit( -1 );
410 }
411 }
413 #=== FUNCTION ================================================================
414 # NAME: import_modules
415 # PARAMETERS: module_path - string - abs. path to the directory the modules
416 # are stored
417 # RETURNS: nothing
418 # DESCRIPTION: each file in module_path which ends with '.pm' and activation
419 # state is on is imported by "require 'file';"
420 #===============================================================================
421 sub import_modules {
422 daemon_log(" ", 1);
424 if (not -e $modules_path) {
425 daemon_log("0 ERROR: cannot find directory or directory is not readable: $modules_path", 1);
426 }
428 opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
429 while (defined (my $file = readdir (DIR))) {
430 if (not $file =~ /(\S*?).pm$/) {
431 next;
432 }
433 my $mod_name = $1;
435 if( $file =~ /ArpHandler.pm/ ) {
436 if( $no_arp > 0 ) {
437 next;
438 }
439 }
441 eval { require $file; };
442 if ($@) {
443 daemon_log("0 ERROR: gosa-si-server could not load module $file", 1);
444 daemon_log("$@", 5);
445 } else {
446 my $info = eval($mod_name.'::get_module_info()');
447 # Only load module if get_module_info() returns a non-null object
448 if( $info ) {
449 my ($input_address, $input_key, $input, $input_active, $input_type) = @{$info};
450 $known_modules->{$mod_name} = $info;
451 daemon_log("0 INFO: module $mod_name loaded", 5);
452 }
453 }
454 }
455 close (DIR);
456 }
459 #=== FUNCTION ================================================================
460 # NAME: sig_int_handler
461 # PARAMETERS: signal - string - signal arose from system
462 # RETURNS: noting
463 # DESCRIPTION: handels tasks to be done befor signal becomes active
464 #===============================================================================
465 sub sig_int_handler {
466 my ($signal) = @_;
468 # if (defined($ldap_handle)) {
469 # $ldap_handle->disconnect;
470 # }
471 # TODO alle verbliebenden ldap verbindungen aus allen heaps beenden
474 daemon_log("shutting down gosa-si-server", 1);
475 system("kill `ps -C gosa-si-server-nobus -o pid=`");
476 }
477 $SIG{INT} = \&sig_int_handler;
480 sub check_key_and_xml_validity {
481 my ($crypted_msg, $module_key, $session_id) = @_;
482 my $msg;
483 my $msg_hash;
484 my $error_string;
485 eval{
486 $msg = &decrypt_msg($crypted_msg, $module_key);
488 if ($msg =~ /<xml>/i){
489 $msg =~ s/\s+/ /g; # just for better daemon_log
490 daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 8);
491 $msg_hash = $xml->XMLin($msg, ForceArray=>1);
493 ##############
494 # check header
495 if( not exists $msg_hash->{'header'} ) { die "no header specified"; }
496 my $header_l = $msg_hash->{'header'};
497 if( 1 > @{$header_l} ) { die 'empty header tag'; }
498 if( 1 < @{$header_l} ) { die 'more than one header specified'; }
499 my $header = @{$header_l}[0];
500 if( 0 == length $header) { die 'empty string in header tag'; }
502 ##############
503 # check source
504 if( not exists $msg_hash->{'source'} ) { die "no source specified"; }
505 my $source_l = $msg_hash->{'source'};
506 if( 1 > @{$source_l} ) { die 'empty source tag'; }
507 if( 1 < @{$source_l} ) { die 'more than one source specified'; }
508 my $source = @{$source_l}[0];
509 if( 0 == length $source) { die 'source error'; }
511 ##############
512 # check target
513 if( not exists $msg_hash->{'target'} ) { die "no target specified"; }
514 my $target_l = $msg_hash->{'target'};
515 if( 1 > @{$target_l} ) { die 'empty target tag'; }
516 }
517 };
518 if($@) {
519 daemon_log("$session_id DEBUG: do not understand the message: $@", 7);
520 $msg = undef;
521 $msg_hash = undef;
522 }
524 return ($msg, $msg_hash);
525 }
528 sub check_outgoing_xml_validity {
529 my ($msg) = @_;
531 my $msg_hash;
532 eval{
533 $msg_hash = $xml->XMLin($msg, ForceArray=>1);
535 ##############
536 # check header
537 my $header_l = $msg_hash->{'header'};
538 if( 1 != @{$header_l} ) {
539 die 'no or more than one headers specified';
540 }
541 my $header = @{$header_l}[0];
542 if( 0 == length $header) {
543 die 'header has length 0';
544 }
546 ##############
547 # check source
548 my $source_l = $msg_hash->{'source'};
549 if( 1 != @{$source_l} ) {
550 die 'no or more than 1 sources specified';
551 }
552 my $source = @{$source_l}[0];
553 if( 0 == length $source) {
554 die 'source has length 0';
555 }
556 unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
557 $source =~ /^GOSA$/i ) {
558 die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
559 }
561 ##############
562 # check target
563 my $target_l = $msg_hash->{'target'};
564 if( 0 == @{$target_l} ) {
565 die "no targets specified";
566 }
567 foreach my $target (@$target_l) {
568 if( 0 == length $target) {
569 die "target has length 0";
570 }
571 unless( $target =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
572 $target =~ /^GOSA$/i ||
573 $target =~ /^\*$/ ||
574 $target =~ /KNOWN_SERVER/i ||
575 $target =~ /JOBDB/i ||
576 $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 ){
577 die "target '$target' is not a complete ip-address with port or a valid target name or a mac-address";
578 }
579 }
580 };
581 if($@) {
582 daemon_log("WARNING: outgoing msg is not gosa-si envelope conform", 5);
583 daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 8);
584 $msg_hash = undef;
585 }
587 return ($msg_hash);
588 }
591 sub input_from_known_server {
592 my ($input, $remote_ip, $session_id) = @_ ;
593 my ($msg, $msg_hash, $module);
595 my $sql_statement= "SELECT * FROM known_server";
596 my $query_res = $known_server_db->select_dbentry( $sql_statement );
598 while( my ($hit_num, $hit) = each %{ $query_res } ) {
599 my $host_name = $hit->{hostname};
600 if( not $host_name =~ "^$remote_ip") {
601 next;
602 }
603 my $host_key = $hit->{hostkey};
604 daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 7);
605 daemon_log("$session_id DEBUG: input_from_known_server: host_key: $host_key", 7);
607 # check if module can open msg envelope with module key
608 my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
609 if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
610 daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 7);
611 daemon_log("$@", 8);
612 next;
613 }
614 else {
615 $msg = $tmp_msg;
616 $msg_hash = $tmp_msg_hash;
617 $module = "ClientPackages";
618 last;
619 }
620 }
622 if( (!$msg) || (!$msg_hash) || (!$module) ) {
623 daemon_log("$session_id DEBUG: Incoming message is not from a known server", 7);
624 }
626 return ($msg, $msg_hash, $module);
627 }
630 sub input_from_known_client {
631 my ($input, $remote_ip, $session_id) = @_ ;
632 my ($msg, $msg_hash, $module);
634 my $sql_statement= "SELECT * FROM known_clients";
635 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
636 while( my ($hit_num, $hit) = each %{ $query_res } ) {
637 my $host_name = $hit->{hostname};
638 if( not $host_name =~ /^$remote_ip:\d*$/) {
639 next;
640 }
641 my $host_key = $hit->{hostkey};
642 &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 7);
643 &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 7);
645 # check if module can open msg envelope with module key
646 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
648 if( (!$msg) || (!$msg_hash) ) {
649 &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 7);
650 &daemon_log("$@", 8);
651 next;
652 }
653 else {
654 $module = "ClientPackages";
655 last;
656 }
657 }
659 if( (!$msg) || (!$msg_hash) || (!$module) ) {
660 &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 7);
661 }
663 return ($msg, $msg_hash, $module);
664 }
667 sub input_from_unknown_host {
668 no strict "refs";
669 my ($input, $session_id) = @_ ;
670 my ($msg, $msg_hash, $module);
671 my $error_string;
673 my %act_modules = %$known_modules;
675 while( my ($mod, $info) = each(%act_modules)) {
677 # check a key exists for this module
678 my $module_key = ${$mod."_key"};
679 if( not defined $module_key ) {
680 if( $mod eq 'ArpHandler' ) {
681 next;
682 }
683 daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
684 next;
685 }
686 daemon_log("$session_id DEBUG: $mod: $module_key", 7);
688 # check if module can open msg envelope with module key
689 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
690 if( (not defined $msg) || (not defined $msg_hash) ) {
691 next;
692 }
693 else {
694 $module = $mod;
695 last;
696 }
697 }
699 if( (!$msg) || (!$msg_hash) || (!$module)) {
700 daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 7);
701 }
703 return ($msg, $msg_hash, $module);
704 }
707 sub create_ciphering {
708 my ($passwd) = @_;
709 if((!defined($passwd)) || length($passwd)==0) {
710 $passwd = "";
711 }
712 $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
713 my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
714 my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
715 $my_cipher->set_iv($iv);
716 return $my_cipher;
717 }
720 sub encrypt_msg {
721 my ($msg, $key) = @_;
722 my $my_cipher = &create_ciphering($key);
723 my $len;
724 {
725 use bytes;
726 $len= 16-length($msg)%16;
727 }
728 $msg = "\0"x($len).$msg;
729 $msg = $my_cipher->encrypt($msg);
730 chomp($msg = &encode_base64($msg));
731 # there are no newlines allowed inside msg
732 $msg=~ s/\n//g;
733 return $msg;
734 }
737 sub decrypt_msg {
739 my ($msg, $key) = @_ ;
740 $msg = &decode_base64($msg);
741 my $my_cipher = &create_ciphering($key);
742 $msg = $my_cipher->decrypt($msg);
743 $msg =~ s/\0*//g;
744 return $msg;
745 }
748 sub get_encrypt_key {
749 my ($target) = @_ ;
750 my $encrypt_key;
751 my $error = 0;
753 # target can be in known_server
754 if( not defined $encrypt_key ) {
755 my $sql_statement= "SELECT * FROM known_server WHERE hostname='$target'";
756 my $query_res = $known_server_db->select_dbentry( $sql_statement );
757 while( my ($hit_num, $hit) = each %{ $query_res } ) {
758 my $host_name = $hit->{hostname};
759 if( $host_name ne $target ) {
760 next;
761 }
762 $encrypt_key = $hit->{hostkey};
763 last;
764 }
765 }
767 # target can be in known_client
768 if( not defined $encrypt_key ) {
769 my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$target'";
770 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
771 while( my ($hit_num, $hit) = each %{ $query_res } ) {
772 my $host_name = $hit->{hostname};
773 if( $host_name ne $target ) {
774 next;
775 }
776 $encrypt_key = $hit->{hostkey};
777 last;
778 }
779 }
781 return $encrypt_key;
782 }
785 #=== FUNCTION ================================================================
786 # NAME: open_socket
787 # PARAMETERS: PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
788 # [PeerPort] string necessary if port not appended by PeerAddr
789 # RETURNS: socket IO::Socket::INET
790 # DESCRIPTION: open a socket to PeerAddr
791 #===============================================================================
792 sub open_socket {
793 my ($PeerAddr, $PeerPort) = @_ ;
794 if(defined($PeerPort)){
795 $PeerAddr = $PeerAddr.":".$PeerPort;
796 }
797 my $socket;
798 $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
799 Porto => "tcp",
800 Type => SOCK_STREAM,
801 Timeout => 5,
802 );
803 if(not defined $socket) {
804 return;
805 }
806 # &daemon_log("DEBUG: open_socket: $PeerAddr", 7);
807 return $socket;
808 }
811 #=== FUNCTION ================================================================
812 # NAME: get_ip
813 # PARAMETERS: interface name (i.e. eth0)
814 # RETURNS: (ip address)
815 # DESCRIPTION: Uses ioctl to get ip address directly from system.
816 #===============================================================================
817 sub get_ip {
818 my $ifreq= shift;
819 my $result= "";
820 my $SIOCGIFADDR= 0x8915; # man 2 ioctl_list
821 my $proto= getprotobyname('ip');
823 socket SOCKET, PF_INET, SOCK_DGRAM, $proto
824 or die "socket: $!";
826 if(ioctl SOCKET, $SIOCGIFADDR, $ifreq) {
827 my ($if, $sin) = unpack 'a16 a16', $ifreq;
828 my ($port, $addr) = sockaddr_in $sin;
829 my $ip = inet_ntoa $addr;
831 if ($ip && length($ip) > 0) {
832 $result = $ip;
833 }
834 }
836 return $result;
837 }
840 sub get_local_ip_for_remote_ip {
841 my $remote_ip= shift;
842 my $result="0.0.0.0";
844 if($remote_ip =~ /^(\d\d?\d?\.){3}\d\d?\d?$/) {
845 if($remote_ip eq "127.0.0.1") {
846 $result = "127.0.0.1";
847 } else {
848 my $PROC_NET_ROUTE= ('/proc/net/route');
850 open(PROC_NET_ROUTE, "<$PROC_NET_ROUTE")
851 or die "Could not open $PROC_NET_ROUTE";
853 my @ifs = <PROC_NET_ROUTE>;
855 close(PROC_NET_ROUTE);
857 # Eat header line
858 shift @ifs;
859 chomp @ifs;
860 foreach my $line(@ifs) {
861 my ($Iface,$Destination,$Gateway,$Flags,$RefCnt,$Use,$Metric,$Mask,$MTU,$Window,$IRTT)=split(/\s/, $line);
862 my $destination;
863 my $mask;
864 my ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Destination);
865 $destination= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
866 ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Mask);
867 $mask= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
868 if(new NetAddr::IP($remote_ip)->within(new NetAddr::IP($destination, $mask))) {
869 # destination matches route, save mac and exit
870 $result= &get_ip($Iface);
871 last;
872 }
873 }
874 }
875 } else {
876 daemon_log("get_local_ip_for_remote_ip was called with a non-ip parameter: $remote_ip", 1);
877 }
878 return $result;
879 }
882 sub send_msg_to_target {
883 my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
884 my $error = 0;
885 my $header;
886 my $new_status;
887 my $act_status;
888 my ($sql_statement, $res);
890 if( $msg_header ) {
891 $header = "'$msg_header'-";
892 } else {
893 $header = "";
894 }
896 # Patch the source ip
897 if($msg =~ /<source>0\.0\.0\.0:\d*?<\/source>/) {
898 my $remote_ip = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
899 $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$remote_ip:$2<\/source>/s;
900 }
902 # encrypt xml msg
903 my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
905 # opensocket
906 my $socket = &open_socket($address);
907 if( !$socket ) {
908 daemon_log("$session_id ERROR: cannot send ".$header."msg to $address , host not reachable", 1);
909 $error++;
910 }
912 if( $error == 0 ) {
913 # send xml msg
914 print $socket $crypted_msg."\n";
916 daemon_log("$session_id INFO: send ".$header."msg to $address", 5);
917 #daemon_log("DEBUG: message:\n$msg", 9);
919 }
921 # close socket in any case
922 if( $socket ) {
923 close $socket;
924 }
926 if( $error > 0 ) { $new_status = "down"; }
927 else { $new_status = $msg_header; }
930 # known_clients
931 $sql_statement = "SELECT * FROM known_clients WHERE hostname='$address'";
932 $res = $known_clients_db->select_dbentry($sql_statement);
933 if( keys(%$res) > 0) {
934 $act_status = $res->{1}->{'status'};
935 if( $act_status eq "down" ) {
936 $sql_statement = "DELETE FROM known_clients WHERE hostname='$address'";
937 $res = $known_clients_db->del_dbentry($sql_statement);
938 daemon_log("$session_id WARNING: failed 2x to send msg to host '$address', delete host from known_clients", 3);
939 } else {
940 $sql_statement = "UPDATE known_clients SET status='$new_status' WHERE hostname='$address'";
941 $res = $known_clients_db->update_dbentry($sql_statement);
942 if($new_status eq "down"){
943 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
944 } else {
945 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
946 }
947 }
948 }
950 # known_server
951 $sql_statement = "SELECT * FROM known_server WHERE hostname='$address'";
952 $res = $known_server_db->select_dbentry($sql_statement);
953 if( keys(%$res) > 0 ) {
954 $act_status = $res->{1}->{'status'};
955 if( $act_status eq "down" ) {
956 $sql_statement = "DELETE FROM known_server WHERE hostname='$address'";
957 $res = $known_server_db->del_dbentry($sql_statement);
958 daemon_log("$session_id WARNING: failed 2x to send a message to host '$address', delete host from known_server", 3);
959 }
960 else {
961 $sql_statement = "UPDATE known_server SET status='$new_status' WHERE hostname='$address'";
962 $res = $known_server_db->update_dbentry($sql_statement);
963 if($new_status eq "down"){
964 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
965 }
966 else {
967 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
968 }
969 }
970 }
971 return $error;
972 }
975 sub update_jobdb_status_for_send_msgs {
976 my ($answer, $error) = @_;
977 if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
978 my $jobdb_id = $1;
980 # sending msg faild
981 if( $error ) {
982 if (not $answer =~ /<header>trigger_action_reinstall<\/header>/) {
983 my $sql_statement = "UPDATE $job_queue_tn ".
984 "SET status='error', result='can not deliver msg, please consult log file' ".
985 "WHERE id=$jobdb_id";
986 my $res = $job_db->update_dbentry($sql_statement);
987 }
989 # sending msg was successful
990 } else {
991 my $sql_statement = "UPDATE $job_queue_tn ".
992 "SET status='done' ".
993 "WHERE id=$jobdb_id AND status='processed'";
994 my $res = $job_db->update_dbentry($sql_statement);
995 }
996 }
997 }
999 sub _start {
1000 my ($kernel) = $_[KERNEL];
1001 &trigger_db_loop($kernel);
1002 $global_kernel = $kernel;
1003 $kernel->yield('register_at_foreign_servers');
1004 $kernel->yield('create_fai_server_db', $fai_server_tn );
1005 $kernel->yield('create_fai_release_db', $fai_release_tn );
1006 $kernel->sig(USR1 => "sig_handler");
1007 $kernel->sig(USR2 => "create_packages_list_db");
1008 }
1010 sub sig_handler {
1011 my ($kernel, $signal) = @_[KERNEL, ARG0] ;
1012 daemon_log("0 INFO got signal '$signal'", 1);
1013 $kernel->sig_handled();
1014 return;
1015 }
1018 sub msg_to_decrypt {
1019 my ($session, $heap) = @_[SESSION, HEAP];
1020 my $session_id = $session->ID;
1021 my ($msg, $msg_hash, $module);
1022 my $error = 0;
1024 # hole neue msg aus @msgs_to_decrypt
1025 my $next_msg = shift @msgs_to_decrypt;
1027 # entschlüssle sie
1029 # msg is from a new client or gosa
1030 ($msg, $msg_hash, $module) = &input_from_unknown_host($next_msg, $session_id);
1031 # msg is from a gosa-si-server or gosa-si-bus
1032 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1033 ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $heap->{'remote_ip'}, $session_id);
1034 }
1035 # msg is from a gosa-si-client
1036 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1037 ($msg, $msg_hash, $module) = &input_from_known_client($next_msg, $heap->{'remote_ip'}, $session_id);
1038 }
1039 # an error occurred
1040 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1041 # if an incoming msg could not be decrypted (maybe a wrong key), send client a ping. If the client
1042 # could not understand a msg from its server the client cause a re-registering process
1043 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);
1044 my $sql_statement = "SELECT * FROM $main::known_clients_tn WHERE (hostname LIKE '".$heap->{'remote_ip'}."%')";
1045 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1046 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1047 my $host_name = $hit->{'hostname'};
1048 my $host_key = $hit->{'hostkey'};
1049 my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source> <target>$host_name</target></xml>";
1050 my $error = &send_msg_to_target($ping_msg, $host_name, $host_key, "gosa_ping", $session_id);
1051 &update_jobdb_status_for_send_msgs($ping_msg, $error);
1052 }
1053 $error++;
1054 }
1056 # add message to incoming_db
1057 if( $error == 0) {
1058 my $header = @{$msg_hash->{'header'}}[0];
1059 my $target = @{$msg_hash->{'target'}}[0];
1060 my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1061 primkey=>[],
1062 headertag=>$header,
1063 targettag=>$target,
1064 xmlmessage=>$msg,
1065 timestamp=>&get_time,
1066 module=>$module,
1067 } );
1068 if ($res != 0) {
1069 # TODO ist das mit $! so ok???
1070 #&daemon_log("$session_id ERROR: cannot add message to incoming.db: $!", 1);
1071 }
1072 }
1074 }
1077 sub next_task {
1078 my ($session, $heap) = @_[SESSION, HEAP];
1079 my $task = POE::Wheel::Run->new(
1080 Program => sub { process_task($session, $heap) },
1081 StdioFilter => POE::Filter::Reference->new(),
1082 StdoutEvent => "task_result",
1083 StderrEvent => "task_debug",
1084 CloseEvent => "task_done",
1085 );
1087 $heap->{task}->{ $task->ID } = $task;
1088 }
1090 sub handle_task_result {
1091 my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
1092 my $client_answer = $result->{'answer'};
1093 if( $client_answer =~ s/session_id=(\d+)$// ) {
1094 my $session_id = $1;
1095 if( defined $session_id ) {
1096 my $session_reference = $kernel->ID_id_to_session($session_id);
1097 if( defined $session_reference ) {
1098 $heap = $session_reference->get_heap();
1099 }
1100 }
1102 if(exists $heap->{'client'}) {
1103 $heap->{'client'}->put($client_answer);
1104 }
1105 }
1106 $kernel->sig(CHLD => "child_reap");
1107 }
1109 sub handle_task_debug {
1110 my $result = $_[ARG0];
1111 print STDERR "$result\n";
1112 }
1114 sub handle_task_done {
1115 my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1116 delete $heap->{task}->{$task_id};
1117 }
1119 sub process_task {
1120 no strict "refs";
1121 my ($session, $heap, $input) = @_;
1122 my $session_id = $session->ID;
1123 my $error = 0;
1124 my $answer_l;
1125 my ($answer_header, @answer_target_l, $answer_source);
1126 my $client_answer = "";
1128 ##################################################
1129 # fetch first unprocessed message from incoming_db
1130 # sometimes the program is faster than sqlite, so wait until informations are present at db
1131 my $id_sql;
1132 my $id_res;
1133 my $message_id;
1134 # TODO : das hier ist sehr sehr unschön
1135 # to be tested: speed enhancement with usleep 100000???
1136 while (1) {
1137 $id_sql = "SELECT min(id) FROM $incoming_tn WHERE (NOT headertag LIKE 'answer%')";
1138 $id_res = $incoming_db->exec_statement($id_sql);
1139 $message_id = @{@$id_res[0]}[0];
1140 if (defined $message_id) { last }
1141 usleep(100000);
1142 }
1144 # fetch new message from incoming_db
1145 my $sql = "SELECT * FROM $incoming_tn WHERE id=$message_id";
1146 my $res = $incoming_db->exec_statement($sql);
1148 # prepare all variables needed to process message
1149 my $msg = @{@$res[0]}[4];
1150 my $incoming_id = @{@$res[0]}[0];
1151 my $module = @{@$res[0]}[5];
1152 my $header = @{@$res[0]}[2];
1153 my $msg_hash = $xml->XMLin($msg, ForceArray=>1);
1155 # messages which are an answer to a still running process should not be processed here
1156 if ($header =~ /^answer_(\d+)/) {
1157 return;
1158 }
1160 # delete message from db
1161 my $delete_sql = "DELETE FROM $incoming_tn WHERE id=$incoming_id";
1162 my $delete_res = $incoming_db->exec_statement($delete_sql);
1164 ######################
1165 # process incoming msg
1166 if( $error == 0) {
1167 daemon_log("$session_id INFO: Incoming msg (session_id=$session_id) with header '".@{$msg_hash->{'header'}}[0].
1168 "' from '".$heap->{'remote_ip'}."'", 5);
1169 daemon_log("$session_id DEBUG: Processing module ".$module, 7);
1170 $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1172 if ( 0 < @{$answer_l} ) {
1173 my $answer_str = join("\n", @{$answer_l});
1174 while ($answer_str =~ /<header>(\w+)<\/header>/g) {
1175 daemon_log("$session_id INFO: got answer message with header '$1'", 5);
1176 }
1177 daemon_log("$session_id DEBUG: $module: got answer from module: \n".$answer_str,8);
1178 } else {
1179 daemon_log("$session_id DEBUG: $module: got no answer from module!" ,8);
1180 }
1182 }
1183 if( !$answer_l ) { $error++ };
1185 ########
1186 # answer
1187 if( $error == 0 ) {
1189 foreach my $answer ( @{$answer_l} ) {
1190 # check outgoing msg to xml validity
1191 my $answer_hash = &check_outgoing_xml_validity($answer);
1192 if( not defined $answer_hash ) { next; }
1194 $answer_header = @{$answer_hash->{'header'}}[0];
1195 @answer_target_l = @{$answer_hash->{'target'}};
1196 $answer_source = @{$answer_hash->{'source'}}[0];
1198 # deliver msg to all targets
1199 foreach my $answer_target ( @answer_target_l ) {
1201 # targets of msg are all gosa-si-clients in known_clients_db
1202 if( $answer_target eq "*" ) {
1203 # answer is for all clients
1204 my $sql_statement= "SELECT * FROM known_clients";
1205 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1206 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1207 my $host_name = $hit->{hostname};
1208 my $host_key = $hit->{hostkey};
1209 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1210 &update_jobdb_status_for_send_msgs($answer, $error);
1211 }
1212 }
1214 # targets of msg are all gosa-si-server in known_server_db
1215 elsif( $answer_target eq "KNOWN_SERVER" ) {
1216 # answer is for all server in known_server
1217 my $sql_statement= "SELECT * FROM known_server";
1218 my $query_res = $known_server_db->select_dbentry( $sql_statement );
1219 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1220 my $host_name = $hit->{hostname};
1221 my $host_key = $hit->{hostkey};
1222 $answer =~ s/KNOWN_SERVER/$host_name/g;
1223 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1224 &update_jobdb_status_for_send_msgs($answer, $error);
1225 }
1226 }
1228 # target of msg is GOsa
1229 elsif( $answer_target eq "GOSA" ) {
1230 my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1231 my $add_on = "";
1232 if( defined $session_id ) {
1233 $add_on = ".session_id=$session_id";
1234 }
1235 # answer is for GOSA and has to returned to connected client
1236 my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1237 $client_answer = $gosa_answer.$add_on;
1238 }
1240 # target of msg is job queue at this host
1241 elsif( $answer_target eq "JOBDB") {
1242 $answer =~ /<header>(\S+)<\/header>/;
1243 my $header;
1244 if( defined $1 ) { $header = $1; }
1245 my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1246 &update_jobdb_status_for_send_msgs($answer, $error);
1247 }
1249 # target of msg is a mac address
1250 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 ) {
1251 daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients", 5);
1252 my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1253 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1254 my $found_ip_flag = 0;
1255 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1256 my $host_name = $hit->{hostname};
1257 my $host_key = $hit->{hostkey};
1258 $answer =~ s/$answer_target/$host_name/g;
1259 daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1260 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1261 &update_jobdb_status_for_send_msgs($answer, $error);
1262 $found_ip_flag++ ;
1263 }
1264 if( $found_ip_flag == 0) {
1265 daemon_log("$session_id WARNING: no host found in known_clients with mac address '$answer_target'", 3);
1266 if( $bus_activ eq "true" ) {
1267 daemon_log("$session_id INFO: try to forward msg '$answer_header' to bus '$bus_address'", 5);
1268 my $sql_statement = "SELECT * FROM known_server WHERE hostname='$bus_address'";
1269 my $query_res = $known_server_db->select_dbentry( $sql_statement );
1270 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1271 my $bus_address = $hit->{hostname};
1272 my $bus_key = $hit->{hostkey};
1273 my $error = &send_msg_to_target($answer, $bus_address, $bus_key, $answer_header, $session_id);
1274 &update_jobdb_status_for_send_msgs($answer, $error);
1275 last;
1276 }
1277 }
1279 }
1281 # answer is for one specific host
1282 } else {
1283 # get encrypt_key
1284 my $encrypt_key = &get_encrypt_key($answer_target);
1285 if( not defined $encrypt_key ) {
1286 # unknown target, forward msg to bus
1287 daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1288 if( $bus_activ eq "true" ) {
1289 daemon_log("$session_id INFO: try to forward msg '$answer_header' to bus '$bus_address'", 5);
1290 my $sql_statement = "SELECT * FROM known_server WHERE hostname='$bus_address'";
1291 my $query_res = $known_server_db->select_dbentry( $sql_statement );
1292 my $res_length = keys( %{$query_res} );
1293 if( $res_length == 0 ){
1294 daemon_log("$session_id WARNING: send '$answer_header' to '$bus_address' failed, ".
1295 "no bus found in known_server", 3);
1296 }
1297 else {
1298 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1299 my $bus_key = $hit->{hostkey};
1300 my $error = &send_msg_to_target($answer, $bus_address, $bus_key, $answer_header,$session_id );
1301 &update_jobdb_status_for_send_msgs($answer, $error);
1302 }
1303 }
1304 }
1305 next;
1306 }
1307 my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1308 &update_jobdb_status_for_send_msgs($answer, $error);
1309 }
1310 }
1311 }
1312 }
1314 my $filter = POE::Filter::Reference->new();
1315 my %result = (
1316 status => "seems ok to me",
1317 answer => $client_answer,
1318 );
1320 my $output = $filter->put( [ \%result ] );
1321 print @$output;
1324 }
1327 sub trigger_db_loop {
1328 my ($kernel) = @_ ;
1329 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1330 $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay);
1331 $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1332 $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1333 $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1334 }
1337 sub watch_for_done_jobs {
1338 my ($kernel,$heap) = @_[KERNEL, HEAP];
1340 my $sql_statement = "SELECT * FROM ".$job_queue_tn.
1341 " WHERE status='done'";
1342 my $res = $job_db->select_dbentry( $sql_statement );
1344 while( my ($id, $hit) = each %{$res} ) {
1345 my $jobdb_id = $hit->{id};
1346 my $sql_statement = "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1347 my $res = $job_db->del_dbentry($sql_statement);
1348 }
1350 $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1351 }
1354 sub watch_for_new_jobs {
1355 if($watch_for_new_jobs_in_progress == 0) {
1356 $watch_for_new_jobs_in_progress = 1;
1357 my ($kernel,$heap) = @_[KERNEL, HEAP];
1359 # check gosa job queue for jobs with executable timestamp
1360 my $timestamp = &get_time();
1361 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE status='waiting' AND (CAST (timestamp AS INTEGER)) < $timestamp ORDER BY timestamp";
1362 my $res = $job_db->exec_statement( $sql_statement );
1364 # Merge all new jobs that would do the same actions
1365 my @drops;
1366 my $hits;
1367 foreach my $hit (reverse @{$res} ) {
1368 my $macaddress= lc @{$hit}[8];
1369 my $headertag= @{$hit}[5];
1370 if(
1371 defined($hits->{$macaddress}) &&
1372 defined($hits->{$macaddress}->{$headertag}) &&
1373 defined($hits->{$macaddress}->{$headertag}[0])
1374 ) {
1375 push @drops, "DELETE FROM $job_queue_tn WHERE id = $hits->{$macaddress}->{$headertag}[0]";
1376 }
1377 $hits->{$macaddress}->{$headertag}= $hit;
1378 }
1380 # Delete new jobs with a matching job in state 'processing'
1381 foreach my $macaddress (keys %{$hits}) {
1382 foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1383 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1384 if(defined($jobdb_id)) {
1385 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND headertag='$jobdb_headertag' AND status='processing'";
1386 my $res = $job_db->exec_statement( $sql_statement );
1387 foreach my $hit (@{$res}) {
1388 push @drops, "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1389 }
1390 } else {
1391 daemon_log("J ERROR: Job without id exists for macaddress $macaddress!", 1);
1392 }
1393 }
1394 }
1396 # Commit deletion
1397 $job_db->exec_statementlist(\@drops);
1399 # Look for new jobs that could be executed
1400 foreach my $macaddress (keys %{$hits}) {
1402 # Look if there is an executing job
1403 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='processing'";
1404 my $res = $job_db->exec_statement( $sql_statement );
1406 # Skip new jobs for host if there is a processing job
1407 if(defined($res) and defined @{$res}[0]) {
1408 next;
1409 }
1411 foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1412 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1413 if(defined($jobdb_id)) {
1414 my $job_msg = @{$hits->{$macaddress}->{$jobdb_headertag}}[7];
1416 daemon_log("J DEBUG: its time to execute $job_msg", 7);
1417 my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1418 my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1420 # expect macaddress is unique!!!!!!
1421 my $target = $res_hash->{1}->{hostname};
1423 # change header
1424 $job_msg =~ s/<header>job_/<header>gosa_/;
1426 # add sqlite_id
1427 $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1429 $job_msg =~ /<header>(\S+)<\/header>/;
1430 my $header = $1 ;
1431 my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");
1433 # update status in job queue to 'processing'
1434 $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
1435 my $res = $job_db->update_dbentry($sql_statement);
1436 # TODO: abfangen ob alles in ordnung ist oder nicht, wenn nicht error schmeißen
1438 # We don't want parallel processing
1439 last;
1440 }
1441 }
1442 }
1444 $watch_for_new_jobs_in_progress = 0;
1445 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1446 }
1447 }
1450 sub watch_for_new_messages {
1451 my ($kernel,$heap) = @_[KERNEL, HEAP];
1452 my @coll_user_msg; # collection list of outgoing messages
1454 # check messaging_db for new incoming messages with executable timestamp
1455 my $timestamp = &get_time();
1456 my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS INTEGER))<$timestamp AND flag='n' AND direction='in' )";
1457 my $res = $messaging_db->exec_statement( $sql_statement );
1458 foreach my $hit (@{$res}) {
1460 # create outgoing messages
1461 my $message_to = @{$hit}[3];
1462 # translate message_to to plain login name
1463 my @message_to_l = split(/,/, $message_to);
1464 my %receiver_h;
1465 foreach my $receiver (@message_to_l) {
1466 if ($receiver =~ /^u_([\s\S]*)$/) {
1467 $receiver_h{$1} = 0;
1468 } elsif ($receiver =~ /^g_([\s\S]*)$/) {
1469 my $group_name = $1;
1470 # fetch all group members from ldap and add them to receiver hash
1471 my $ldap_handle = &get_ldap_handle();
1472 if (defined $ldap_handle) {
1473 my $mesg = $ldap_handle->search(
1474 base => $ldap_base,
1475 scope => 'sub',
1476 attrs => ['memberUid'],
1477 filter => "cn=$group_name",
1478 );
1479 if ($mesg->count) {
1480 my @entries = $mesg->entries;
1481 foreach my $entry (@entries) {
1482 my @receivers= $entry->get_value("memberUid");
1483 foreach my $receiver (@receivers) {
1484 $receiver_h{$1} = 0;
1485 }
1486 }
1487 }
1488 # translating errors ?
1489 if ($mesg->code) {
1490 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: $mesg->error", 1);
1491 }
1492 # ldap handle error ?
1493 } else {
1494 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: no ldap handle available", 1);
1495 }
1496 } else {
1497 my $sbjct = &encode_base64(@{$hit}[1]);
1498 my $msg = &encode_base64(@{$hit}[7]);
1499 &daemon_log("M WARNING: unknown receiver '$receiver' for a user-message '$sbjct - $msg'", 3);
1500 }
1501 }
1502 my @receiver_l = keys(%receiver_h);
1504 my $message_id = @{$hit}[0];
1506 #add each outgoing msg to messaging_db
1507 my $receiver;
1508 foreach $receiver (@receiver_l) {
1509 my $sql_statement = "INSERT INTO $messaging_tn (id, subject, message_from, message_to, flag, direction, delivery_time, message, timestamp) ".
1510 "VALUES ('".
1511 $message_id."', '". # id
1512 @{$hit}[1]."', '". # subject
1513 @{$hit}[2]."', '". # message_from
1514 $receiver."', '". # message_to
1515 "none"."', '". # flag
1516 "out"."', '". # direction
1517 @{$hit}[6]."', '". # delivery_time
1518 @{$hit}[7]."', '". # message
1519 $timestamp."'". # timestamp
1520 ")";
1521 &daemon_log("M DEBUG: $sql_statement", 1);
1522 my $res = $messaging_db->exec_statement($sql_statement);
1523 &daemon_log("M INFO: message '".@{$hit}[0]."' is prepared for delivery to receiver '$receiver'", 5);
1524 }
1526 # set incoming message to flag d=deliverd
1527 $sql_statement = "UPDATE $messaging_tn SET flag='p' WHERE id='$message_id'";
1528 &daemon_log("M DEBUG: $sql_statement", 7);
1529 $res = $messaging_db->update_dbentry($sql_statement);
1530 &daemon_log("M INFO: message '$message_id' is set to flag 'p' (processed)", 5);
1531 }
1533 $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1534 return;
1535 }
1537 sub watch_for_delivery_messages {
1538 my ($kernel, $heap) = @_[KERNEL, HEAP];
1540 # select outgoing messages
1541 my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( flag='p' AND direction='out' )";
1542 #&daemon_log("0 DEBUG: $sql", 7);
1543 my $res = $messaging_db->exec_statement( $sql_statement );
1545 # build out msg for each usr
1546 foreach my $hit (@{$res}) {
1547 my $receiver = @{$hit}[3];
1548 my $msg_id = @{$hit}[0];
1549 my $subject = @{$hit}[1];
1550 my $message = @{$hit}[7];
1552 # resolve usr -> host where usr is logged in
1553 my $sql = "SELECT * FROM $login_users_tn WHERE (user='$receiver')";
1554 #&daemon_log("0 DEBUG: $sql", 7);
1555 my $res = $login_users_db->exec_statement($sql);
1557 # reciver is logged in nowhere
1558 if (not ref(@$res[0]) eq "ARRAY") { next; }
1560 my $send_succeed = 0;
1561 foreach my $hit (@$res) {
1562 my $receiver_host = @$hit[0];
1563 &daemon_log("M DEBUG: user '$receiver' is logged in at host '$receiver_host'", 7);
1565 # fetch key to encrypt msg propperly for usr/host
1566 my $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$receiver_host')";
1567 &daemon_log("0 DEBUG: $sql", 7);
1568 my $res = $known_clients_db->exec_statement($sql);
1570 # host is already down
1571 if (not ref(@$res[0]) eq "ARRAY") { next; }
1573 # host is on
1574 my $receiver_key = @{@{$res}[0]}[2];
1575 my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
1576 my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data );
1577 my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0);
1578 if ($error == 0 ) {
1579 $send_succeed++ ;
1580 }
1581 }
1583 if ($send_succeed) {
1584 # set outgoing msg at db to deliverd
1585 my $sql = "UPDATE $messaging_tn SET flag='d' WHERE (id='$msg_id' AND direction='out' AND message_to='$receiver')";
1586 &daemon_log("0 DEBUG: $sql", 7);
1587 my $res = $messaging_db->exec_statement($sql);
1588 }
1589 }
1591 $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1592 return;
1593 }
1596 sub watch_for_done_messages {
1597 my ($kernel,$heap) = @_[KERNEL, HEAP];
1599 my $sql = "SELECT * FROM $messaging_tn WHERE (flag='p' AND direction='in')";
1600 #&daemon_log("0 DEBUG: $sql", 7);
1601 my $res = $messaging_db->exec_statement($sql);
1603 foreach my $hit (@{$res}) {
1604 my $msg_id = @{$hit}[0];
1606 my $sql = "SELECT * FROM $messaging_tn WHERE (id='$msg_id' AND direction='out' AND (NOT flag='s'))";
1607 #&daemon_log("0 DEBUG: $sql", 7);
1608 my $res = $messaging_db->exec_statement($sql);
1610 # not all usr msgs have been seen till now
1611 if ( ref(@$res[0]) eq "ARRAY") { next; }
1613 $sql = "DELETE FROM $messaging_tn WHERE (id='$msg_id')";
1614 #&daemon_log("0 DEBUG: $sql", 7);
1615 $res = $messaging_db->exec_statement($sql);
1617 }
1619 $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1620 return;
1621 }
1624 sub get_ldap_handle {
1625 my ($session_id) = @_;
1626 my $heap;
1627 my $ldap_handle;
1629 if (not defined $session_id ) { $session_id = 0 };
1630 if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
1632 if ($session_id == 0) {
1633 daemon_log("$session_id DEBUG: get_ldap_handle invoked without a session_id, create a new ldap_handle", 7);
1634 $ldap_handle = Net::LDAP->new( $ldap_uri );
1635 $ldap_handle->bind($ldap_admin_dn, password => $ldap_admin_password);
1637 } else {
1638 my $session_reference = $global_kernel->ID_id_to_session($session_id);
1639 if( defined $session_reference ) {
1640 $heap = $session_reference->get_heap();
1641 }
1643 if (not defined $heap) {
1644 daemon_log("$session_id DEBUG: cannot get heap for session_id '$session_id'", 7);
1645 return;
1646 }
1648 # TODO: This "if" is nonsense, because it doesn't prove that the
1649 # used handle is still valid - or if we've to reconnect...
1650 #if (not exists $heap->{ldap_handle}) {
1651 $ldap_handle = Net::LDAP->new( $ldap_uri );
1652 $ldap_handle->bind($ldap_admin_dn, password => $ldap_admin_password);
1653 $heap->{ldap_handle} = $ldap_handle;
1654 #}
1655 }
1656 return $ldap_handle;
1657 }
1660 sub change_fai_state {
1661 my ($st, $targets, $session_id) = @_;
1662 $session_id = 0 if not defined $session_id;
1663 # Set FAI state to localboot
1664 my %mapActions= (
1665 reboot => '',
1666 update => 'softupdate',
1667 localboot => 'localboot',
1668 reinstall => 'install',
1669 rescan => '',
1670 wake => '',
1671 memcheck => 'memcheck',
1672 sysinfo => 'sysinfo',
1673 install => 'install',
1674 );
1676 # Return if this is unknown
1677 if (!exists $mapActions{ $st }){
1678 daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1);
1679 return;
1680 }
1682 my $state= $mapActions{ $st };
1684 my $ldap_handle = &get_ldap_handle($session_id);
1685 if( defined($ldap_handle) ) {
1687 # Build search filter for hosts
1688 my $search= "(&(objectClass=GOhard)";
1689 foreach (@{$targets}){
1690 $search.= "(macAddress=$_)";
1691 }
1692 $search.= ")";
1694 # If there's any host inside of the search string, procress them
1695 if (!($search =~ /macAddress/)){
1696 daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);
1697 return;
1698 }
1700 # Perform search for Unit Tag
1701 my $mesg = $ldap_handle->search(
1702 base => $ldap_base,
1703 scope => 'sub',
1704 attrs => ['dn', 'FAIstate', 'objectClass'],
1705 filter => "$search"
1706 );
1708 if ($mesg->count) {
1709 my @entries = $mesg->entries;
1710 foreach my $entry (@entries) {
1711 # Only modify entry if it is not set to '$state'
1712 if ($entry->get_value("FAIstate") ne "$state"){
1713 daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
1714 my $result;
1715 my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
1716 if (exists $tmp{'FAIobject'}){
1717 if ($state eq ''){
1718 $result= $ldap_handle->modify($entry->dn, changes => [
1719 delete => [ FAIstate => [] ] ]);
1720 } else {
1721 $result= $ldap_handle->modify($entry->dn, changes => [
1722 replace => [ FAIstate => $state ] ]);
1723 }
1724 } elsif ($state ne ''){
1725 $result= $ldap_handle->modify($entry->dn, changes => [
1726 add => [ objectClass => 'FAIobject' ],
1727 add => [ FAIstate => $state ] ]);
1728 }
1730 # Errors?
1731 if ($result->code){
1732 daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
1733 }
1734 } else {
1735 daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 7);
1736 }
1737 }
1738 }
1739 # if no ldap handle defined
1740 } else {
1741 daemon_log("$session_id ERROR: no LDAP handle defined for update FAIstate", 1);
1742 }
1744 }
1747 sub change_goto_state {
1748 my ($st, $targets, $session_id) = @_;
1749 $session_id = 0 if not defined $session_id;
1751 # Switch on or off?
1752 my $state= $st eq 'active' ? 'active': 'locked';
1754 my $ldap_handle = &get_ldap_handle($session_id);
1755 if( defined($ldap_handle) ) {
1757 # Build search filter for hosts
1758 my $search= "(&(objectClass=GOhard)";
1759 foreach (@{$targets}){
1760 $search.= "(macAddress=$_)";
1761 }
1762 $search.= ")";
1764 # If there's any host inside of the search string, procress them
1765 if (!($search =~ /macAddress/)){
1766 return;
1767 }
1769 # Perform search for Unit Tag
1770 my $mesg = $ldap_handle->search(
1771 base => $ldap_base,
1772 scope => 'sub',
1773 attrs => ['dn', 'gotoMode'],
1774 filter => "$search"
1775 );
1777 if ($mesg->count) {
1778 my @entries = $mesg->entries;
1779 foreach my $entry (@entries) {
1781 # Only modify entry if it is not set to '$state'
1782 if ($entry->get_value("gotoMode") ne $state){
1784 daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
1785 my $result;
1786 $result= $ldap_handle->modify($entry->dn, changes => [
1787 replace => [ gotoMode => $state ] ]);
1789 # Errors?
1790 if ($result->code){
1791 &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
1792 }
1794 }
1795 }
1796 }
1798 }
1799 }
1802 sub run_create_fai_server_db {
1803 my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
1804 my $session_id = $session->ID;
1805 my $task = POE::Wheel::Run->new(
1806 Program => sub { &create_fai_server_db($table_name,$kernel, undef, $session_id) },
1807 StdoutEvent => "session_run_result",
1808 StderrEvent => "session_run_debug",
1809 CloseEvent => "session_run_done",
1810 );
1812 $heap->{task}->{ $task->ID } = $task;
1813 return;
1814 }
1817 sub create_fai_server_db {
1818 my ($table_name, $kernel, $dont_create_packages_list, $session_id) = @_;
1819 my $result;
1821 if (not defined $session_id) { $session_id = 0; }
1822 my $ldap_handle = &get_ldap_handle();
1823 if(defined($ldap_handle)) {
1824 daemon_log("$session_id INFO: create_fai_server_db: start", 5);
1825 my $mesg= $ldap_handle->search(
1826 base => $ldap_base,
1827 scope => 'sub',
1828 attrs => ['FAIrepository', 'gosaUnitTag'],
1829 filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
1830 );
1831 if($mesg->{'resultCode'} == 0 &&
1832 $mesg->count != 0) {
1833 foreach my $entry (@{$mesg->{entries}}) {
1834 if($entry->exists('FAIrepository')) {
1835 # Add an entry for each Repository configured for server
1836 foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
1837 my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
1838 my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
1839 $result= $fai_server_db->add_dbentry( {
1840 table => $table_name,
1841 primkey => ['server', 'release', 'tag'],
1842 server => $tmp_url,
1843 release => $tmp_release,
1844 sections => $tmp_sections,
1845 tag => (length($tmp_tag)>0)?$tmp_tag:"",
1846 } );
1847 }
1848 }
1849 }
1850 }
1851 daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
1853 # TODO: Find a way to post the 'create_packages_list_db' event
1854 if(not defined($dont_create_packages_list)) {
1855 &create_packages_list_db(undef, undef, $session_id);
1856 }
1857 }
1859 $ldap_handle->disconnect;
1860 return $result;
1861 }
1864 sub run_create_fai_release_db {
1865 my ($session, $heap, $table_name) = @_[SESSION, HEAP, ARG0];
1866 my $session_id = $session->ID;
1867 my $task = POE::Wheel::Run->new(
1868 Program => sub { &create_fai_release_db($table_name, $session_id) },
1869 StdoutEvent => "session_run_result",
1870 StderrEvent => "session_run_debug",
1871 CloseEvent => "session_run_done",
1872 );
1874 $heap->{task}->{ $task->ID } = $task;
1875 return;
1876 }
1879 sub create_fai_release_db {
1880 my ($table_name, $session_id) = @_;
1881 my $result;
1883 # used for logging
1884 if (not defined $session_id) { $session_id = 0; }
1886 my $ldap_handle = &get_ldap_handle();
1887 if(defined($ldap_handle)) {
1888 daemon_log("$session_id INFO: create_fai_release_db: start",5);
1889 my $mesg= $ldap_handle->search(
1890 base => $ldap_base,
1891 scope => 'sub',
1892 attrs => [],
1893 filter => "(&(objectClass=organizationalUnit)(ou=fai))",
1894 );
1895 if($mesg->{'resultCode'} == 0 &&
1896 $mesg->count != 0) {
1897 # Walk through all possible FAI container ou's
1898 my @sql_list;
1899 my $timestamp= &get_time();
1900 foreach my $ou (@{$mesg->{entries}}) {
1901 my $tmp_classes= resolve_fai_classes($ou->dn, $ldap_handle, $session_id);
1902 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
1903 my @tmp_array=get_fai_release_entries($tmp_classes);
1904 if(@tmp_array) {
1905 foreach my $entry (@tmp_array) {
1906 if(defined($entry) && ref($entry) eq 'HASH') {
1907 my $sql=
1908 "INSERT INTO $table_name "
1909 ."(timestamp, release, class, type, state) VALUES ("
1910 .$timestamp.","
1911 ."'".$entry->{'release'}."',"
1912 ."'".$entry->{'class'}."',"
1913 ."'".$entry->{'type'}."',"
1914 ."'".$entry->{'state'}."')";
1915 push @sql_list, $sql;
1916 }
1917 }
1918 }
1919 }
1920 }
1922 daemon_log("$session_id DEBUG: Inserting ".scalar @sql_list." entries to DB",8);
1923 if(@sql_list) {
1924 unshift @sql_list, "VACUUM";
1925 unshift @sql_list, "DELETE FROM $table_name";
1926 $fai_release_db->exec_statementlist(\@sql_list);
1927 }
1928 daemon_log("$session_id DEBUG: Done with inserting",7);
1929 }
1930 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
1931 }
1932 $ldap_handle->disconnect;
1933 return $result;
1934 }
1936 sub get_fai_types {
1937 my $tmp_classes = shift || return undef;
1938 my @result;
1940 foreach my $type(keys %{$tmp_classes}) {
1941 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
1942 my $entry = {
1943 type => $type,
1944 state => $tmp_classes->{$type}[0],
1945 };
1946 push @result, $entry;
1947 }
1948 }
1950 return @result;
1951 }
1953 sub get_fai_state {
1954 my $result = "";
1955 my $tmp_classes = shift || return $result;
1957 foreach my $type(keys %{$tmp_classes}) {
1958 if(defined($tmp_classes->{$type}[0])) {
1959 $result = $tmp_classes->{$type}[0];
1961 # State is equal for all types in class
1962 last;
1963 }
1964 }
1966 return $result;
1967 }
1969 sub resolve_fai_classes {
1970 my ($fai_base, $ldap_handle, $session_id) = @_;
1971 if (not defined $session_id) { $session_id = 0; }
1972 my $result;
1973 my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
1974 my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
1975 my $fai_classes;
1977 daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base",7);
1978 my $mesg= $ldap_handle->search(
1979 base => $fai_base,
1980 scope => 'sub',
1981 attrs => ['cn','objectClass','FAIstate'],
1982 filter => $fai_filter,
1983 );
1984 daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries",7);
1986 if($mesg->{'resultCode'} == 0 &&
1987 $mesg->count != 0) {
1988 foreach my $entry (@{$mesg->{entries}}) {
1989 if($entry->exists('cn')) {
1990 my $tmp_dn= $entry->dn();
1992 # Skip classname and ou dn parts for class
1993 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?),$fai_base$/;
1995 # Skip classes without releases
1996 if((!defined($tmp_release)) || length($tmp_release)==0) {
1997 next;
1998 }
2000 my $tmp_cn= $entry->get_value('cn');
2001 my $tmp_state= $entry->get_value('FAIstate');
2003 my $tmp_type;
2004 # Get FAI type
2005 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
2006 if(grep $_ eq $oclass, @possible_fai_classes) {
2007 $tmp_type= $oclass;
2008 last;
2009 }
2010 }
2012 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2013 # A Subrelease
2014 my @sub_releases = split(/,/, $tmp_release);
2016 # Walk through subreleases and build hash tree
2017 my $hash;
2018 while(my $tmp_sub_release = pop @sub_releases) {
2019 $hash .= "\{'$tmp_sub_release'\}->";
2020 }
2021 eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
2022 } else {
2023 # A branch, no subrelease
2024 push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
2025 }
2026 } elsif (!$entry->exists('cn')) {
2027 my $tmp_dn= $entry->dn();
2028 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?),$fai_base$/;
2030 # Skip classes without releases
2031 if((!defined($tmp_release)) || length($tmp_release)==0) {
2032 next;
2033 }
2035 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2036 # A Subrelease
2037 my @sub_releases= split(/,/, $tmp_release);
2039 # Walk through subreleases and build hash tree
2040 my $hash;
2041 while(my $tmp_sub_release = pop @sub_releases) {
2042 $hash .= "\{'$tmp_sub_release'\}->";
2043 }
2044 # Remove the last two characters
2045 chop($hash);
2046 chop($hash);
2048 eval('$fai_classes->'.$hash.'= {}');
2049 } else {
2050 # A branch, no subrelease
2051 if(!exists($fai_classes->{$tmp_release})) {
2052 $fai_classes->{$tmp_release} = {};
2053 }
2054 }
2055 }
2056 }
2058 # The hash is complete, now we can honor the copy-on-write based missing entries
2059 foreach my $release (keys %$fai_classes) {
2060 $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
2061 }
2062 }
2063 return $result;
2064 }
2066 sub apply_fai_inheritance {
2067 my $fai_classes = shift || return {};
2068 my $tmp_classes;
2070 # Get the classes from the branch
2071 foreach my $class (keys %{$fai_classes}) {
2072 # Skip subreleases
2073 if($class =~ /^ou=.*$/) {
2074 next;
2075 } else {
2076 $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
2077 }
2078 }
2080 # Apply to each subrelease
2081 foreach my $subrelease (keys %{$fai_classes}) {
2082 if($subrelease =~ /ou=/) {
2083 foreach my $tmp_class (keys %{$tmp_classes}) {
2084 if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
2085 $fai_classes->{$subrelease}->{$tmp_class} =
2086 deep_copy($tmp_classes->{$tmp_class});
2087 } else {
2088 foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
2089 if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
2090 $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
2091 deep_copy($tmp_classes->{$tmp_class}->{$type});
2092 }
2093 }
2094 }
2095 }
2096 }
2097 }
2099 # Find subreleases in deeper levels
2100 foreach my $subrelease (keys %{$fai_classes}) {
2101 if($subrelease =~ /ou=/) {
2102 foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
2103 if($subsubrelease =~ /ou=/) {
2104 apply_fai_inheritance($fai_classes->{$subrelease});
2105 }
2106 }
2107 }
2108 }
2110 return $fai_classes;
2111 }
2113 sub get_fai_release_entries {
2114 my $tmp_classes = shift || return;
2115 my $parent = shift || "";
2116 my @result = shift || ();
2118 foreach my $entry (keys %{$tmp_classes}) {
2119 if(defined($entry)) {
2120 if($entry =~ /^ou=.*$/) {
2121 my $release_name = $entry;
2122 $release_name =~ s/ou=//g;
2123 if(length($parent)>0) {
2124 $release_name = $parent."/".$release_name;
2125 }
2126 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2127 foreach my $bufentry(@bufentries) {
2128 push @result, $bufentry;
2129 }
2130 } else {
2131 my @types = get_fai_types($tmp_classes->{$entry});
2132 foreach my $type (@types) {
2133 push @result,
2134 {
2135 'class' => $entry,
2136 'type' => $type->{'type'},
2137 'release' => $parent,
2138 'state' => $type->{'state'},
2139 };
2140 }
2141 }
2142 }
2143 }
2145 return @result;
2146 }
2148 sub deep_copy {
2149 my $this = shift;
2150 if (not ref $this) {
2151 $this;
2152 } elsif (ref $this eq "ARRAY") {
2153 [map deep_copy($_), @$this];
2154 } elsif (ref $this eq "HASH") {
2155 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2156 } else { die "what type is $_?" }
2157 }
2160 sub session_run_result {
2161 my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];
2162 $kernel->sig(CHLD => "child_reap");
2163 }
2165 sub session_run_debug {
2166 my $result = $_[ARG0];
2167 print STDERR "$result\n";
2168 }
2170 sub session_run_done {
2171 my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2172 delete $heap->{task}->{$task_id};
2173 }
2176 sub create_sources_list {
2177 my $session_id = shift;
2178 my $ldap_handle = &main::get_ldap_handle;
2179 my $result="/tmp/gosa_si_tmp_sources_list";
2181 # Remove old file
2182 if(stat($result)) {
2183 unlink($result);
2184 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7);
2185 }
2187 my $fh;
2188 open($fh, ">$result");
2189 if (not defined $fh) {
2190 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7);
2191 return undef;
2192 }
2193 if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2194 my $mesg=$ldap_handle->search(
2195 base => $main::ldap_server_dn,
2196 scope => 'base',
2197 attrs => 'FAIrepository',
2198 filter => 'objectClass=FAIrepositoryServer'
2199 );
2200 if($mesg->count) {
2201 foreach my $entry(@{$mesg->{'entries'}}) {
2202 foreach my $value(@{$entry->get_value('FAIrepository', asref => 1)}) {
2203 my ($server, $tag, $release, $sections)= split /\|/, $value;
2204 my $line = "deb $server $release";
2205 $sections =~ s/,/ /g;
2206 $line.= " $sections";
2207 print $fh $line."\n";
2208 }
2209 }
2210 }
2211 } else {
2212 if (defined $main::ldap_server_dn){
2213 &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1);
2214 } else {
2215 &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2216 }
2217 }
2218 close($fh);
2220 return $result;
2221 }
2224 sub run_create_packages_list_db {
2225 my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2226 my $session_id = $session->ID;
2228 my $task = POE::Wheel::Run->new(
2229 Priority => +20,
2230 Program => sub {&create_packages_list_db(undef, undef, $session_id)},
2231 StdoutEvent => "session_run_result",
2232 StderrEvent => "session_run_debug",
2233 CloseEvent => "session_run_done",
2234 );
2235 $heap->{task}->{ $task->ID } = $task;
2236 }
2239 sub create_packages_list_db {
2240 my ($ldap_handle, $sources_file, $session_id) = @_;
2242 # it should not be possible to trigger a recreation of packages_list_db
2243 # while packages_list_db is under construction, so set flag packages_list_under_construction
2244 # which is tested befor recreation can be started
2245 if (-r $packages_list_under_construction) {
2246 daemon_log("$session_id WARNING: packages_list_db is right now under construction, please wait until this process is finished", 3);
2247 return;
2248 } else {
2249 daemon_log("$session_id INFO: create_packages_list_db: start", 5);
2250 # set packages_list_under_construction to true
2251 system("touch $packages_list_under_construction");
2252 @packages_list_statements=();
2253 }
2255 if (not defined $session_id) { $session_id = 0; }
2256 if (not defined $ldap_handle) {
2257 $ldap_handle= &get_ldap_handle();
2259 if (not defined $ldap_handle) {
2260 daemon_log("$session_id ERROR: no ldap_handle available to create_packages_list_db", 1);
2261 unlink($packages_list_under_construction);
2262 return;
2263 }
2264 }
2265 if (not defined $sources_file) {
2266 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5);
2267 $sources_file = &create_sources_list($session_id);
2268 }
2270 if (not defined $sources_file) {
2271 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1);
2272 unlink($packages_list_under_construction);
2273 return;
2274 }
2276 my $line;
2278 open(CONFIG, "<$sources_file") or do {
2279 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2280 unlink($packages_list_under_construction);
2281 return;
2282 };
2284 # Read lines
2285 while ($line = <CONFIG>){
2286 # Unify
2287 chop($line);
2288 $line =~ s/^\s+//;
2289 $line =~ s/^\s+/ /;
2291 # Strip comments
2292 $line =~ s/#.*$//g;
2294 # Skip empty lines
2295 if ($line =~ /^\s*$/){
2296 next;
2297 }
2299 # Interpret deb line
2300 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2301 my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2302 my $section;
2303 foreach $section (split(' ', $sections)){
2304 &parse_package_info( $baseurl, $dist, $section, $session_id );
2305 }
2306 }
2307 }
2309 close (CONFIG);
2311 find(\&cleanup_and_extract, keys( %repo_dirs ));
2312 &main::strip_packages_list_statements();
2313 unshift @packages_list_statements, "VACUUM";
2314 $packages_list_db->exec_statementlist(\@packages_list_statements);
2315 unlink($packages_list_under_construction);
2316 daemon_log("$session_id INFO: create_packages_list_db: finished", 5);
2317 return;
2318 }
2320 # This function should do some intensive task to minimize the db-traffic
2321 sub strip_packages_list_statements {
2322 my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2323 my @new_statement_list=();
2324 my $hash;
2325 my $insert_hash;
2326 my $update_hash;
2327 my $delete_hash;
2328 my $local_timestamp=get_time();
2330 foreach my $existing_entry (@existing_entries) {
2331 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
2332 }
2334 foreach my $statement (@packages_list_statements) {
2335 if($statement =~ /^INSERT/i) {
2336 # Assign the values from the insert statement
2337 my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~
2338 /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
2339 if(exists($hash->{$distribution}->{$package}->{$version})) {
2340 # If section or description has changed, update the DB
2341 if(
2342 (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or
2343 (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
2344 ) {
2345 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
2346 }
2347 } else {
2348 # Insert a non-existing entry to db
2349 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2350 }
2351 } elsif ($statement =~ /^UPDATE/i) {
2352 my ($template,$package,$version) = ($1,$2,$3) if $statement =~
2353 /^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;
2354 foreach my $distribution (keys %{$hash}) {
2355 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
2356 # update the insertion hash to execute only one query per package (insert instead insert+update)
2357 @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
2358 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
2359 if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
2360 my $section;
2361 my $description;
2362 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
2363 length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
2364 $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
2365 }
2366 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2367 $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
2368 }
2369 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2370 }
2371 }
2372 }
2373 }
2374 }
2376 # TODO: Check for orphaned entries
2378 # unroll the insert_hash
2379 foreach my $distribution (keys %{$insert_hash}) {
2380 foreach my $package (keys %{$insert_hash->{$distribution}}) {
2381 foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
2382 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
2383 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
2384 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
2385 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
2386 ."'$local_timestamp')";
2387 }
2388 }
2389 }
2391 # unroll the update hash
2392 foreach my $distribution (keys %{$update_hash}) {
2393 foreach my $package (keys %{$update_hash->{$distribution}}) {
2394 foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
2395 my $set = "";
2396 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
2397 $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
2398 }
2399 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2400 $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
2401 }
2402 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
2403 $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
2404 }
2405 if(defined($set) and length($set) > 0) {
2406 $set .= "timestamp = '$local_timestamp'";
2407 } else {
2408 next;
2409 }
2410 push @new_statement_list,
2411 "UPDATE $main::packages_list_tn SET $set WHERE"
2412 ." distribution = '$distribution'"
2413 ." AND package = '$package'"
2414 ." AND version = '$version'";
2415 }
2416 }
2417 }
2419 @packages_list_statements = @new_statement_list;
2420 }
2423 sub parse_package_info {
2424 my ($baseurl, $dist, $section, $session_id)= @_;
2425 my ($package);
2426 if (not defined $session_id) { $session_id = 0; }
2427 my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
2428 $repo_dirs{ "${repo_path}/pool" } = 1;
2430 foreach $package ("Packages.gz"){
2431 daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
2432 get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
2433 parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
2434 }
2436 }
2439 sub get_package {
2440 my ($url, $dest, $session_id)= @_;
2441 if (not defined $session_id) { $session_id = 0; }
2443 my $tpath = dirname($dest);
2444 -d "$tpath" || mkpath "$tpath";
2446 # This is ugly, but I've no time to take a look at "how it works in perl"
2447 if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
2448 system("gunzip -cd '$dest' > '$dest.in'");
2449 daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 5);
2450 unlink($dest);
2451 daemon_log("$session_id DEBUG: delete file '$dest'", 5);
2452 } else {
2453 daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' failed!", 1);
2454 }
2455 return 0;
2456 }
2459 sub parse_package {
2460 my ($path, $dist, $srv_path, $session_id)= @_;
2461 if (not defined $session_id) { $session_id = 0;}
2462 my ($package, $version, $section, $description);
2463 my $PACKAGES;
2464 my $timestamp = &get_time();
2466 if(not stat("$path.in")) {
2467 daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
2468 return;
2469 }
2471 open($PACKAGES, "<$path.in");
2472 if(not defined($PACKAGES)) {
2473 daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1);
2474 return;
2475 }
2477 # Read lines
2478 while (<$PACKAGES>){
2479 my $line = $_;
2480 # Unify
2481 chop($line);
2483 # Use empty lines as a trigger
2484 if ($line =~ /^\s*$/){
2485 my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
2486 push(@packages_list_statements, $sql);
2487 $package = "none";
2488 $version = "none";
2489 $section = "none";
2490 $description = "none";
2491 next;
2492 }
2494 # Trigger for package name
2495 if ($line =~ /^Package:\s/){
2496 ($package)= ($line =~ /^Package: (.*)$/);
2497 next;
2498 }
2500 # Trigger for version
2501 if ($line =~ /^Version:\s/){
2502 ($version)= ($line =~ /^Version: (.*)$/);
2503 next;
2504 }
2506 # Trigger for description
2507 if ($line =~ /^Description:\s/){
2508 ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
2509 next;
2510 }
2512 # Trigger for section
2513 if ($line =~ /^Section:\s/){
2514 ($section)= ($line =~ /^Section: (.*)$/);
2515 next;
2516 }
2518 # Trigger for filename
2519 if ($line =~ /^Filename:\s/){
2520 my ($filename) = ($line =~ /^Filename: (.*)$/);
2521 store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
2522 next;
2523 }
2524 }
2526 close( $PACKAGES );
2527 unlink( "$path.in" );
2528 &main::daemon_log("$session_id DEBUG: unlink '$path.in'", 1);
2529 }
2532 sub store_fileinfo {
2533 my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
2535 my %fileinfo = (
2536 'package' => $package,
2537 'dist' => $dist,
2538 'version' => $vers,
2539 );
2541 $repo_files{ "${srvdir}/$file" } = \%fileinfo;
2542 }
2545 sub cleanup_and_extract {
2546 my $fileinfo = $repo_files{ $File::Find::name };
2548 if( defined $fileinfo ) {
2550 my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
2551 my $sql;
2552 my $package = $fileinfo->{ 'package' };
2553 my $newver = $fileinfo->{ 'version' };
2555 mkpath($dir);
2556 system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
2558 if( -f "$dir/DEBIAN/templates" ) {
2560 daemon_log("DEBUG: Found debconf templates in '$package' - $newver", 5);
2562 my $tmpl= "";
2563 {
2564 local $/=undef;
2565 open FILE, "$dir/DEBIAN/templates";
2566 $tmpl = &encode_base64(<FILE>);
2567 close FILE;
2568 }
2569 rmtree("$dir/DEBIAN/templates");
2571 $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
2572 push @packages_list_statements, $sql;
2573 }
2574 }
2576 return;
2577 }
2580 sub register_at_foreign_servers {
2581 my ($kernel) = $_[KERNEL];
2583 # hole alle bekannten server aus known_server_db
2584 my $sql = "SELECT * FROM $known_server_tn";
2585 my $res = $known_server_db->exec_statement($sql);
2587 # no entries in known_server_db
2588 if (not ref(@$res[0]) eq "ARRAY") {
2589 # TODO
2590 }
2592 foreach my $hit (@$res) {
2593 my $hostname = @$hit[0];
2594 my $hostkey = &create_passwd;
2596 my %data= ('known_clients' => "",
2597 'key' => $hostkey,
2598 );
2599 my $foreign_server_msg = &build_msg('new_server', $server_address, $hostname, \%data);
2600 my $error = &send_msg_to_target($foreign_server_msg, $hostname, $ServerPackages_key, "new_server", 0);
2603 }
2605 $kernel->delay_set("register_at_foreign_servers", $foreign_servers_register_delay);
2606 return;
2607 }
2610 #==== MAIN = main ==============================================================
2611 # parse commandline options
2612 Getopt::Long::Configure( "bundling" );
2613 GetOptions("h|help" => \&usage,
2614 "c|config=s" => \$cfg_file,
2615 "f|foreground" => \$foreground,
2616 "v|verbose+" => \$verbose,
2617 "no-bus+" => \$no_bus,
2618 "no-arp+" => \$no_arp,
2619 );
2621 # read and set config parameters
2622 &check_cmdline_param ;
2623 &read_configfile;
2624 &check_pid;
2626 $SIG{CHLD} = 'IGNORE';
2628 # forward error messages to logfile
2629 if( ! $foreground ) {
2630 open( STDIN, '+>/dev/null' );
2631 open( STDOUT, '+>&STDIN' );
2632 open( STDERR, '+>&STDIN' );
2633 }
2635 # Just fork, if we are not in foreground mode
2636 if( ! $foreground ) {
2637 chdir '/' or die "Can't chdir to /: $!";
2638 $pid = fork;
2639 setsid or die "Can't start a new session: $!";
2640 umask 0;
2641 } else {
2642 $pid = $$;
2643 }
2645 # Do something useful - put our PID into the pid_file
2646 if( 0 != $pid ) {
2647 open( LOCK_FILE, ">$pid_file" );
2648 print LOCK_FILE "$pid\n";
2649 close( LOCK_FILE );
2650 if( !$foreground ) {
2651 exit( 0 )
2652 };
2653 }
2655 # parse head url and revision from svn
2656 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
2657 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
2658 $server_headURL = defined $1 ? $1 : 'unknown' ;
2659 $server_revision = defined $2 ? $2 : 'unknown' ;
2660 if ($server_headURL =~ /\/tag\// ||
2661 $server_headURL =~ /\/branches\// ) {
2662 $server_status = "stable";
2663 } else {
2664 $server_status = "developmental" ;
2665 }
2668 daemon_log(" ", 1);
2669 daemon_log("$0 started!", 1);
2670 daemon_log("status: $server_status", 1);
2671 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1);
2673 if ($no_bus > 0) {
2674 $bus_activ = "false"
2675 }
2677 # connect to incoming_db
2678 unlink($incoming_file_name);
2679 $incoming_db = GOSA::DBsqlite->new($incoming_file_name);
2680 $incoming_db->create_table($incoming_tn, \@incoming_col_names);
2682 # connect to gosa-si job queue
2683 $job_db = GOSA::DBsqlite->new($job_queue_file_name);
2684 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
2686 # connect to known_clients_db
2687 $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
2688 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
2690 # connect to known_server_db
2691 unlink($known_server_file_name);
2692 $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
2693 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
2695 # connect to login_usr_db
2696 $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
2697 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
2699 # connect to fai_server_db and fai_release_db
2700 unlink($fai_server_file_name);
2701 $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
2702 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
2704 unlink($fai_release_file_name);
2705 $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
2706 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
2708 # connect to packages_list_db
2709 #unlink($packages_list_file_name);
2710 unlink($packages_list_under_construction);
2711 $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
2712 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
2714 # connect to messaging_db
2715 $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
2716 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
2719 # create xml object used for en/decrypting
2720 $xml = new XML::Simple();
2723 # foreign servers
2724 my @foreign_server_list;
2726 # add foreign server from cfg file
2727 if ($foreign_server_string ne "") {
2728 my @cfg_foreign_server_list = split(",", $foreign_server_string);
2729 foreach my $foreign_server (@cfg_foreign_server_list) {
2730 push(@foreign_server_list, $foreign_server);
2731 }
2732 }
2734 # add foreign server from dns
2735 my @tmp_servers;
2736 if ( !$server_domain) {
2737 # Try our DNS Searchlist
2738 for my $domain(get_dns_domains()) {
2739 chomp($domain);
2740 my @tmp_domains= &get_server_addresses($domain);
2741 if(@tmp_domains) {
2742 for my $tmp_server(@tmp_domains) {
2743 push @tmp_servers, $tmp_server;
2744 }
2745 }
2746 }
2747 if(@tmp_servers && length(@tmp_servers)==0) {
2748 daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
2749 }
2750 } else {
2751 @tmp_servers = &get_server_addresses($server_domain);
2752 if( 0 == @tmp_servers ) {
2753 daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
2754 }
2755 }
2756 foreach my $server (@tmp_servers) {
2757 unshift(@foreign_server_list, $server);
2758 }
2759 # eliminate duplicate entries
2760 @foreign_server_list = &del_doubles(@foreign_server_list);
2761 my $all_foreign_server = join(", ", @foreign_server_list);
2762 daemon_log("0 INFO: found foreign server in config file and DNS: $all_foreign_server", 5);
2764 # add all found foreign servers to known_server
2765 my $act_timestamp = &get_time();
2766 foreach my $foreign_server (@foreign_server_list) {
2767 my $res = $known_server_db->add_dbentry( {table=>$known_server_tn,
2768 primkey=>['hostname'],
2769 hostname=>$foreign_server,
2770 status=>'not_jet_registered',
2771 hostkey=>"none",
2772 timestamp=>$act_timestamp,
2773 } );
2774 }
2777 POE::Component::Server::TCP->new(
2778 Port => $server_port,
2779 ClientInput => sub {
2780 my ($kernel, $input) = @_[KERNEL, ARG0];
2781 push(@tasks, $input);
2782 push(@msgs_to_decrypt, $input);
2783 $kernel->yield("msg_to_decrypt");
2784 $kernel->yield("next_task");
2785 },
2786 InlineStates => {
2787 next_task => \&next_task,
2788 msg_to_decrypt => \&msg_to_decrypt,
2789 task_result => \&handle_task_result,
2790 task_done => \&handle_task_done,
2791 task_debug => \&handle_task_debug,
2792 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!" },
2793 }
2794 );
2796 daemon_log("start socket for incoming xml messages at port '$server_port' ", 1);
2798 # create session for repeatedly checking the job queue for jobs
2799 POE::Session->create(
2800 inline_states => {
2801 _start => \&_start,
2802 register_at_foreign_servers => \®ister_at_foreign_servers,
2803 sig_handler => \&sig_handler,
2804 watch_for_new_messages => \&watch_for_new_messages,
2805 watch_for_delivery_messages => \&watch_for_delivery_messages,
2806 watch_for_done_messages => \&watch_for_done_messages,
2807 watch_for_new_jobs => \&watch_for_new_jobs,
2808 watch_for_done_jobs => \&watch_for_done_jobs,
2809 create_packages_list_db => \&run_create_packages_list_db,
2810 create_fai_server_db => \&run_create_fai_server_db,
2811 create_fai_release_db => \&run_create_fai_release_db,
2812 session_run_result => \&session_run_result,
2813 session_run_debug => \&session_run_debug,
2814 session_run_done => \&session_run_done,
2815 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!" },
2816 }
2817 );
2820 # import all modules
2821 &import_modules;
2823 # TODO
2824 # check wether all modules are gosa-si valid passwd check
2828 POE::Kernel->run();
2829 exit;