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);
57 use DateTime;
59 my $modules_path = "/usr/lib/gosa-si/modules";
60 use lib "/usr/lib/gosa-si/modules";
62 # revision number of server and program name
63 my $server_version = '$HeadURL: https://oss.gonicus.de/repositories/gosa/trunk/gosa-si/gosa-si-server $:$Rev: 10826 $';
64 my $server_headURL;
65 my $server_revision;
66 my $server_status;
67 our $prg= basename($0);
69 our $global_kernel;
70 my ($foreground, $ping_timeout);
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 ($procid, $pid);
76 my ($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_timeout,
88 $foreign_server_string, $server_domain, $ServerPackages_key, $foreign_servers_register_delay,
89 $wake_on_lan_passwd, $job_synchronization, $modified_jobs_loop_delay,
90 $arp_enabled, $arp_interface,
91 $opsi_enabled, $opsi_server, $opsi_admin, $opsi_password,
92 );
94 # additional variable which should be globaly accessable
95 our $server_address;
96 our $server_mac_address;
97 our $gosa_address;
98 our $no_arp;
99 our $verbose;
100 our $forground;
101 our $cfg_file;
102 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_arp = 0;
114 my $packages_list_under_construction = "/tmp/packages_list_creation_in_progress";
115 my @packages_list_statements;
116 my $watch_for_new_jobs_in_progress = 0;
118 # holds all incoming decrypted messages
119 our $incoming_db;
120 our $incoming_tn = 'incoming';
121 my $incoming_file_name;
122 my @incoming_col_names = ("id INTEGER PRIMARY KEY",
123 "timestamp DEFAULT 'none'",
124 "headertag DEFAULT 'none'",
125 "targettag DEFAULT 'none'",
126 "xmlmessage DEFAULT 'none'",
127 "module DEFAULT 'none'",
128 "sessionid DEFAULT '0'",
129 );
131 # holds all gosa jobs
132 our $job_db;
133 our $job_queue_tn = 'jobs';
134 my $job_queue_file_name;
135 my @job_queue_col_names = ("id INTEGER PRIMARY KEY",
136 "timestamp DEFAULT 'none'",
137 "status DEFAULT 'none'",
138 "result DEFAULT 'none'",
139 "progress DEFAULT 'none'",
140 "headertag DEFAULT 'none'",
141 "targettag DEFAULT 'none'",
142 "xmlmessage DEFAULT 'none'",
143 "macaddress DEFAULT 'none'",
144 "plainname DEFAULT 'none'",
145 "siserver DEFAULT 'none'",
146 "modified DEFAULT '0'",
147 );
149 # holds all other gosa-si-server
150 our $known_server_db;
151 our $known_server_tn = "known_server";
152 my $known_server_file_name;
153 my @known_server_col_names = ("hostname", "status", "hostkey", "timestamp");
155 # holds all registrated clients
156 our $known_clients_db;
157 our $known_clients_tn = "known_clients";
158 my $known_clients_file_name;
159 my @known_clients_col_names = ("hostname", "status", "hostkey", "timestamp", "macaddress", "events", "keylifetime");
161 # holds all registered clients at a foreign server
162 our $foreign_clients_db;
163 our $foreign_clients_tn = "foreign_clients";
164 my $foreign_clients_file_name;
165 my @foreign_clients_col_names = ("hostname", "macaddress", "regserver", "timestamp");
167 # holds all logged in user at each client
168 our $login_users_db;
169 our $login_users_tn = "login_users";
170 my $login_users_file_name;
171 my @login_users_col_names = ("client", "user", "timestamp");
173 # holds all fai server, the debian release and tag
174 our $fai_server_db;
175 our $fai_server_tn = "fai_server";
176 my $fai_server_file_name;
177 our @fai_server_col_names = ("timestamp", "server", "release", "sections", "tag");
179 our $fai_release_db;
180 our $fai_release_tn = "fai_release";
181 my $fai_release_file_name;
182 our @fai_release_col_names = ("timestamp", "release", "class", "type", "state");
184 # holds all packages available from different repositories
185 our $packages_list_db;
186 our $packages_list_tn = "packages_list";
187 my $packages_list_file_name;
188 our @packages_list_col_names = ("distribution", "package", "version", "section", "description", "template", "timestamp");
189 my $outdir = "/tmp/packages_list_db";
190 my $arch = "i386";
192 # holds all messages which should be delivered to a user
193 our $messaging_db;
194 our $messaging_tn = "messaging";
195 our @messaging_col_names = ("id INTEGER", "subject", "message_from", "message_to",
196 "flag", "direction", "delivery_time", "message", "timestamp" );
197 my $messaging_file_name;
199 # path to directory to store client install log files
200 our $client_fai_log_dir = "/var/log/fai";
202 # queue which stores taskes until one of the $max_children children are ready to process the task
203 my @tasks = qw();
204 my @msgs_to_decrypt = qw();
205 my $max_children = 2;
208 # loop delay for job queue to look for opsi jobs
209 my $job_queue_opsi_delay = 10;
210 our $opsi_client;
211 our $opsi_url;
215 %cfg_defaults = (
216 "general" => {
217 "log-file" => [\$log_file, "/var/run/".$prg.".log"],
218 "pid-file" => [\$pid_file, "/var/run/".$prg.".pid"],
219 },
220 "server" => {
221 "ip" => [\$server_ip, "0.0.0.0"],
222 "port" => [\$server_port, "20081"],
223 "known-clients" => [\$known_clients_file_name, '/var/lib/gosa-si/clients.db' ],
224 "known-servers" => [\$known_server_file_name, '/var/lib/gosa-si/servers.db'],
225 "incoming" => [\$incoming_file_name, '/var/lib/gosa-si/incoming.db'],
226 "login-users" => [\$login_users_file_name, '/var/lib/gosa-si/users.db'],
227 "fai-server" => [\$fai_server_file_name, '/var/lib/gosa-si/fai_server.db'],
228 "fai-release" => [\$fai_release_file_name, '/var/lib/gosa-si/fai_release.db'],
229 "packages-list" => [\$packages_list_file_name, '/var/lib/gosa-si/packages.db'],
230 "messaging" => [\$messaging_file_name, '/var/lib/gosa-si/messaging.db'],
231 "foreign-clients" => [\$foreign_clients_file_name, '/var/lib/gosa-si/foreign_clients.db'],
232 "source-list" => [\$sources_list, '/etc/apt/sources.list'],
233 "repo-path" => [\$repo_path, '/srv/www/repository'],
234 "ldap-uri" => [\$ldap_uri, ""],
235 "ldap-base" => [\$ldap_base, ""],
236 "ldap-admin-dn" => [\$ldap_admin_dn, ""],
237 "ldap-admin-password" => [\$ldap_admin_password, ""],
238 "gosa-unit-tag" => [\$gosa_unit_tag, ""],
239 "max-clients" => [\$max_clients, 10],
240 "wol-password" => [\$wake_on_lan_passwd, ""],
241 },
242 "GOsaPackages" => {
243 "job-queue" => [\$job_queue_file_name, '/var/lib/gosa-si/jobs.db'],
244 "job-queue-loop-delay" => [\$job_queue_loop_delay, 3],
245 "messaging-db-loop-delay" => [\$messaging_db_loop_delay, 3],
246 "key" => [\$GosaPackages_key, "none"],
247 },
248 "ClientPackages" => {
249 "key" => [\$ClientPackages_key, "none"],
250 },
251 "ServerPackages"=> {
252 "address" => [\$foreign_server_string, ""],
253 "domain" => [\$server_domain, ""],
254 "key" => [\$ServerPackages_key, "none"],
255 "key-lifetime" => [\$foreign_servers_register_delay, 120],
256 "job-synchronization-enabled" => [\$job_synchronization, "true"],
257 "synchronization-loop" => [\$modified_jobs_loop_delay, 5],
258 },
259 "ArpHandler" => {
260 "enabled" => [\$arp_enabled, "true"],
261 "interface" => [\$arp_interface, "all"],
262 },
263 "Opsi" => {
264 "enabled" => [\$opsi_enabled, "false"],
265 "server" => [\$opsi_server, "localhost"],
266 "admin" => [\$opsi_admin, "opsi-admin"],
267 "password" => [\$opsi_password, "secret"],
268 },
270 );
273 #=== FUNCTION ================================================================
274 # NAME: usage
275 # PARAMETERS: nothing
276 # RETURNS: nothing
277 # DESCRIPTION: print out usage text to STDERR
278 #===============================================================================
279 sub usage {
280 print STDERR << "EOF" ;
281 usage: $prg [-hvf] [-c config]
283 -h : this (help) message
284 -c <file> : config file
285 -f : foreground, process will not be forked to background
286 -v : be verbose (multiple to increase verbosity)
287 -no-arp : starts $prg without connection to arp module
289 EOF
290 print "\n" ;
291 }
294 #=== FUNCTION ================================================================
295 # NAME: logging
296 # PARAMETERS: level - string - default 'info'
297 # msg - string -
298 # facility - string - default 'LOG_DAEMON'
299 # RETURNS: nothing
300 # DESCRIPTION: function for logging
301 #===============================================================================
302 sub daemon_log {
303 # log into log_file
304 my( $msg, $level ) = @_;
305 if(not defined $msg) { return }
306 if(not defined $level) { $level = 1 }
307 if(defined $log_file){
308 open(LOG_HANDLE, ">>$log_file");
309 chmod 0600, $log_file;
310 if(not defined open( LOG_HANDLE, ">>$log_file" )) {
311 print STDERR "cannot open $log_file: $!";
312 return
313 }
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 # ArpHandler switch
436 if( $file =~ /ArpHandler.pm/ ) {
437 if( $arp_enabled eq "false" ) { next; }
438 }
440 eval { require $file; };
441 if ($@) {
442 daemon_log("0 ERROR: gosa-si-server could not load module $file", 1);
443 daemon_log("$@", 5);
444 } else {
445 my $info = eval($mod_name.'::get_module_info()');
446 # Only load module if get_module_info() returns a non-null object
447 if( $info ) {
448 my ($input_address, $input_key, $input, $input_active, $input_type) = @{$info};
449 $known_modules->{$mod_name} = $info;
450 daemon_log("0 INFO: module $mod_name loaded", 5);
451 }
452 }
453 }
455 close (DIR);
456 }
458 #=== FUNCTION ================================================================
459 # NAME: password_check
460 # PARAMETERS: nothing
461 # RETURNS: nothing
462 # DESCRIPTION: escalates an critical error if two modules exist which are avaialable by
463 # the same password
464 #===============================================================================
465 sub password_check {
466 my $passwd_hash = {};
467 while (my ($mod_name, $mod_info) = each %$known_modules) {
468 my $mod_passwd = @$mod_info[1];
469 if (not defined $mod_passwd) { next; }
470 if (not exists $passwd_hash->{$mod_passwd}) {
471 $passwd_hash->{$mod_passwd} = $mod_name;
473 # escalates critical error
474 } else {
475 &daemon_log("0 ERROR: two loaded modules do have the same password. Please modify the 'key'-parameter in config file");
476 &daemon_log("0 ERROR: module='$mod_name' and module='".$passwd_hash->{$mod_passwd}."'");
477 exit( -1 );
478 }
479 }
481 }
484 #=== FUNCTION ================================================================
485 # NAME: sig_int_handler
486 # PARAMETERS: signal - string - signal arose from system
487 # RETURNS: nothing
488 # DESCRIPTION: handels tasks to be done befor signal becomes active
489 #===============================================================================
490 sub sig_int_handler {
491 my ($signal) = @_;
493 # if (defined($ldap_handle)) {
494 # $ldap_handle->disconnect;
495 # }
496 # TODO alle verbliebenden ldap verbindungen aus allen heaps beenden
499 daemon_log("shutting down gosa-si-server", 1);
500 system("kill `ps -C gosa-si-server -o pid=`");
501 }
502 $SIG{INT} = \&sig_int_handler;
505 sub check_key_and_xml_validity {
506 my ($crypted_msg, $module_key, $session_id) = @_;
507 my $msg;
508 my $msg_hash;
509 my $error_string;
510 eval{
511 $msg = &decrypt_msg($crypted_msg, $module_key);
513 if ($msg =~ /<xml>/i){
514 $msg =~ s/\s+/ /g; # just for better daemon_log
515 daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 8);
516 $msg_hash = $xml->XMLin($msg, ForceArray=>1);
518 ##############
519 # check header
520 if( not exists $msg_hash->{'header'} ) { die "no header specified"; }
521 my $header_l = $msg_hash->{'header'};
522 if( 1 > @{$header_l} ) { die 'empty header tag'; }
523 if( 1 < @{$header_l} ) { die 'more than one header specified'; }
524 my $header = @{$header_l}[0];
525 if( 0 == length $header) { die 'empty string in header tag'; }
527 ##############
528 # check source
529 if( not exists $msg_hash->{'source'} ) { die "no source specified"; }
530 my $source_l = $msg_hash->{'source'};
531 if( 1 > @{$source_l} ) { die 'empty source tag'; }
532 if( 1 < @{$source_l} ) { die 'more than one source specified'; }
533 my $source = @{$source_l}[0];
534 if( 0 == length $source) { die 'source error'; }
536 ##############
537 # check target
538 if( not exists $msg_hash->{'target'} ) { die "no target specified"; }
539 my $target_l = $msg_hash->{'target'};
540 if( 1 > @{$target_l} ) { die 'empty target tag'; }
541 }
542 };
543 if($@) {
544 daemon_log("$session_id ERROR: do not understand the message: $@", 1);
545 $msg = undef;
546 $msg_hash = undef;
547 }
549 return ($msg, $msg_hash);
550 }
553 sub check_outgoing_xml_validity {
554 my ($msg, $session_id) = @_;
556 my $msg_hash;
557 eval{
558 $msg_hash = $xml->XMLin($msg, ForceArray=>1);
560 ##############
561 # check header
562 my $header_l = $msg_hash->{'header'};
563 if( 1 != @{$header_l} ) {
564 die 'no or more than one headers specified';
565 }
566 my $header = @{$header_l}[0];
567 if( 0 == length $header) {
568 die 'header has length 0';
569 }
571 ##############
572 # check source
573 my $source_l = $msg_hash->{'source'};
574 if( 1 != @{$source_l} ) {
575 die 'no or more than 1 sources specified';
576 }
577 my $source = @{$source_l}[0];
578 if( 0 == length $source) {
579 die 'source has length 0';
580 }
581 unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
582 $source =~ /^GOSA$/i ) {
583 die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
584 }
586 ##############
587 # check target
588 my $target_l = $msg_hash->{'target'};
589 if( 0 == @{$target_l} ) {
590 die "no targets specified";
591 }
592 foreach my $target (@$target_l) {
593 if( 0 == length $target) {
594 die "target has length 0";
595 }
596 unless( $target =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
597 $target =~ /^GOSA$/i ||
598 $target =~ /^\*$/ ||
599 $target =~ /KNOWN_SERVER/i ||
600 $target =~ /JOBDB/i ||
601 $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 ){
602 die "target '$target' is not a complete ip-address with port or a valid target name or a mac-address";
603 }
604 }
605 };
606 if($@) {
607 daemon_log("$session_id ERROR: outgoing msg is not gosa-si envelope conform: $@", 1);
608 daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 1);
609 $msg_hash = undef;
610 }
612 return ($msg_hash);
613 }
616 sub input_from_known_server {
617 my ($input, $remote_ip, $session_id) = @_ ;
618 my ($msg, $msg_hash, $module);
620 my $sql_statement= "SELECT * FROM known_server";
621 my $query_res = $known_server_db->select_dbentry( $sql_statement );
623 while( my ($hit_num, $hit) = each %{ $query_res } ) {
624 my $host_name = $hit->{hostname};
625 if( not $host_name =~ "^$remote_ip") {
626 next;
627 }
628 my $host_key = $hit->{hostkey};
629 daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 7);
630 daemon_log("$session_id DEBUG: input_from_known_server: host_key: $host_key", 7);
632 # check if module can open msg envelope with module key
633 my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
634 if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
635 daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 7);
636 daemon_log("$@", 8);
637 next;
638 }
639 else {
640 $msg = $tmp_msg;
641 $msg_hash = $tmp_msg_hash;
642 $module = "ServerPackages";
643 last;
644 }
645 }
647 if( (!$msg) || (!$msg_hash) || (!$module) ) {
648 daemon_log("$session_id DEBUG: Incoming message is not from a known server", 7);
649 }
651 return ($msg, $msg_hash, $module);
652 }
655 sub input_from_known_client {
656 my ($input, $remote_ip, $session_id) = @_ ;
657 my ($msg, $msg_hash, $module);
659 my $sql_statement= "SELECT * FROM known_clients";
660 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
661 while( my ($hit_num, $hit) = each %{ $query_res } ) {
662 my $host_name = $hit->{hostname};
663 if( not $host_name =~ /^$remote_ip:\d*$/) {
664 next;
665 }
666 my $host_key = $hit->{hostkey};
667 &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 7);
668 &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 7);
670 # check if module can open msg envelope with module key
671 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
673 if( (!$msg) || (!$msg_hash) ) {
674 &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 7);
675 &daemon_log("$@", 8);
676 next;
677 }
678 else {
679 $module = "ClientPackages";
680 last;
681 }
682 }
684 if( (!$msg) || (!$msg_hash) || (!$module) ) {
685 &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 7);
686 }
688 return ($msg, $msg_hash, $module);
689 }
692 sub input_from_unknown_host {
693 no strict "refs";
694 my ($input, $session_id) = @_ ;
695 my ($msg, $msg_hash, $module);
696 my $error_string;
698 my %act_modules = %$known_modules;
700 while( my ($mod, $info) = each(%act_modules)) {
702 # check a key exists for this module
703 my $module_key = ${$mod."_key"};
704 if( not defined $module_key ) {
705 if( $mod eq 'ArpHandler' ) {
706 next;
707 }
708 daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
709 next;
710 }
711 daemon_log("$session_id DEBUG: $mod: $module_key", 7);
713 # check if module can open msg envelope with module key
714 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
715 if( (not defined $msg) || (not defined $msg_hash) ) {
716 next;
717 }
718 else {
719 $module = $mod;
720 last;
721 }
722 }
724 if( (!$msg) || (!$msg_hash) || (!$module)) {
725 daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 7);
726 }
728 return ($msg, $msg_hash, $module);
729 }
732 sub create_ciphering {
733 my ($passwd) = @_;
734 if((!defined($passwd)) || length($passwd)==0) {
735 $passwd = "";
736 }
737 $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
738 my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
739 my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
740 $my_cipher->set_iv($iv);
741 return $my_cipher;
742 }
745 sub encrypt_msg {
746 my ($msg, $key) = @_;
747 my $my_cipher = &create_ciphering($key);
748 my $len;
749 {
750 use bytes;
751 $len= 16-length($msg)%16;
752 }
753 $msg = "\0"x($len).$msg;
754 $msg = $my_cipher->encrypt($msg);
755 chomp($msg = &encode_base64($msg));
756 # there are no newlines allowed inside msg
757 $msg=~ s/\n//g;
758 return $msg;
759 }
762 sub decrypt_msg {
764 my ($msg, $key) = @_ ;
765 $msg = &decode_base64($msg);
766 my $my_cipher = &create_ciphering($key);
767 $msg = $my_cipher->decrypt($msg);
768 $msg =~ s/\0*//g;
769 return $msg;
770 }
773 sub get_encrypt_key {
774 my ($target) = @_ ;
775 my $encrypt_key;
776 my $error = 0;
778 # target can be in known_server
779 if( not defined $encrypt_key ) {
780 my $sql_statement= "SELECT * FROM known_server WHERE hostname='$target'";
781 my $query_res = $known_server_db->select_dbentry( $sql_statement );
782 while( my ($hit_num, $hit) = each %{ $query_res } ) {
783 my $host_name = $hit->{hostname};
784 if( $host_name ne $target ) {
785 next;
786 }
787 $encrypt_key = $hit->{hostkey};
788 last;
789 }
790 }
792 # target can be in known_client
793 if( not defined $encrypt_key ) {
794 my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$target'";
795 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
796 while( my ($hit_num, $hit) = each %{ $query_res } ) {
797 my $host_name = $hit->{hostname};
798 if( $host_name ne $target ) {
799 next;
800 }
801 $encrypt_key = $hit->{hostkey};
802 last;
803 }
804 }
806 return $encrypt_key;
807 }
810 #=== FUNCTION ================================================================
811 # NAME: open_socket
812 # PARAMETERS: PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
813 # [PeerPort] string necessary if port not appended by PeerAddr
814 # RETURNS: socket IO::Socket::INET
815 # DESCRIPTION: open a socket to PeerAddr
816 #===============================================================================
817 sub open_socket {
818 my ($PeerAddr, $PeerPort) = @_ ;
819 if(defined($PeerPort)){
820 $PeerAddr = $PeerAddr.":".$PeerPort;
821 }
822 my $socket;
823 $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
824 Porto => "tcp",
825 Type => SOCK_STREAM,
826 Timeout => 5,
827 );
828 if(not defined $socket) {
829 return;
830 }
831 # &daemon_log("DEBUG: open_socket: $PeerAddr", 7);
832 return $socket;
833 }
836 sub get_local_ip_for_remote_ip {
837 my $remote_ip= shift;
838 my $result="0.0.0.0";
840 if($remote_ip =~ /^(\d\d?\d?\.){3}\d\d?\d?$/) {
841 if($remote_ip eq "127.0.0.1") {
842 $result = "127.0.0.1";
843 } else {
844 my $PROC_NET_ROUTE= ('/proc/net/route');
846 open(PROC_NET_ROUTE, "<$PROC_NET_ROUTE")
847 or die "Could not open $PROC_NET_ROUTE";
849 my @ifs = <PROC_NET_ROUTE>;
851 close(PROC_NET_ROUTE);
853 # Eat header line
854 shift @ifs;
855 chomp @ifs;
856 foreach my $line(@ifs) {
857 my ($Iface,$Destination,$Gateway,$Flags,$RefCnt,$Use,$Metric,$Mask,$MTU,$Window,$IRTT)=split(/\s/, $line);
858 my $destination;
859 my $mask;
860 my ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Destination);
861 $destination= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
862 ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Mask);
863 $mask= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
864 if(new NetAddr::IP($remote_ip)->within(new NetAddr::IP($destination, $mask))) {
865 # destination matches route, save mac and exit
866 $result= &get_ip($Iface);
867 last;
868 }
869 }
870 }
871 } else {
872 daemon_log("0 WARNING: get_local_ip_for_remote_ip() was called with a non-ip parameter: '$remote_ip'", 1);
873 }
874 return $result;
875 }
878 sub send_msg_to_target {
879 my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
880 my $error = 0;
881 my $header;
882 my $timestamp = &get_time();
883 my $new_status;
884 my $act_status;
885 my ($sql_statement, $res);
887 if( $msg_header ) {
888 $header = "'$msg_header'-";
889 } else {
890 $header = "";
891 }
893 # Patch the source ip
894 if($msg =~ /<source>0\.0\.0\.0:\d*?<\/source>/) {
895 my $remote_ip = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
896 $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$remote_ip:$2<\/source>/s;
897 }
899 # encrypt xml msg
900 my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
902 # opensocket
903 my $socket = &open_socket($address);
904 if( !$socket ) {
905 daemon_log("$session_id WARNING: cannot send ".$header."msg to $address , host not reachable", 3);
906 $error++;
907 }
909 if( $error == 0 ) {
910 # send xml msg
911 print $socket $crypted_msg."\n";
913 daemon_log("$session_id INFO: send ".$header."msg to $address", 5);
914 daemon_log("$session_id DEBUG: message:\n$msg", 9);
916 }
918 # close socket in any case
919 if( $socket ) {
920 close $socket;
921 }
923 if( $error > 0 ) { $new_status = "down"; }
924 else { $new_status = $msg_header; }
927 # known_clients
928 $sql_statement = "SELECT * FROM $known_clients_tn WHERE hostname='$address'";
929 $res = $known_clients_db->select_dbentry($sql_statement);
930 if( keys(%$res) == 1) {
931 $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
932 if ($act_status eq "down" && $new_status eq "down") {
933 $sql_statement = "DELETE FROM known_clients WHERE hostname='$address'";
934 $res = $known_clients_db->del_dbentry($sql_statement);
935 daemon_log("$session_id WARNING: failed 2x to send msg to host '$address', delete host from known_clients", 3);
936 } else {
937 $sql_statement = "UPDATE known_clients SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
938 $res = $known_clients_db->update_dbentry($sql_statement);
939 if($new_status eq "down"){
940 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
941 } else {
942 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
943 }
944 }
945 }
947 # known_server
948 $sql_statement = "SELECT * FROM $known_server_tn WHERE hostname='$address'";
949 $res = $known_server_db->select_dbentry($sql_statement);
950 if( keys(%$res) == 1) {
951 $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
952 if ($act_status eq "down" && $new_status eq "down") {
953 $sql_statement = "DELETE FROM known_server WHERE hostname='$address'";
954 $res = $known_server_db->del_dbentry($sql_statement);
955 daemon_log("$session_id WARNING: failed 2x to send a message to host '$address', delete host from known_server", 3);
956 }
957 else {
958 $sql_statement = "UPDATE known_server SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
959 $res = $known_server_db->update_dbentry($sql_statement);
960 if($new_status eq "down"){
961 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
962 } else {
963 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
964 }
965 }
966 }
967 return $error;
968 }
971 sub update_jobdb_status_for_send_msgs {
972 my ($answer, $error) = @_;
973 if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
974 my $jobdb_id = $1;
976 # sending msg faild
977 if( $error ) {
978 if (not $answer =~ /<header>trigger_action_reinstall<\/header>/) {
979 my $sql_statement = "UPDATE $job_queue_tn ".
980 "SET status='error', result='can not deliver msg, please consult log file' ".
981 "WHERE id=$jobdb_id";
982 my $res = $job_db->update_dbentry($sql_statement);
983 }
985 # sending msg was successful
986 } else {
987 my $sql_statement = "UPDATE $job_queue_tn ".
988 "SET status='done' ".
989 "WHERE id=$jobdb_id AND status='processed'";
990 my $res = $job_db->update_dbentry($sql_statement);
991 }
992 }
993 }
996 sub sig_handler {
997 my ($kernel, $signal) = @_[KERNEL, ARG0] ;
998 daemon_log("0 INFO got signal '$signal'", 1);
999 $kernel->sig_handled();
1000 return;
1001 }
1004 sub msg_to_decrypt {
1005 my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
1006 my $session_id = $session->ID;
1007 my ($msg, $msg_hash, $module);
1008 my $error = 0;
1010 # hole neue msg aus @msgs_to_decrypt
1011 my $next_msg = shift @msgs_to_decrypt;
1013 # entschlüssle sie
1015 # msg is from a new client or gosa
1016 ($msg, $msg_hash, $module) = &input_from_unknown_host($next_msg, $session_id);
1017 # msg is from a gosa-si-server
1018 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1019 ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $heap->{'remote_ip'}, $session_id);
1020 }
1021 # msg is from a gosa-si-client
1022 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1023 ($msg, $msg_hash, $module) = &input_from_known_client($next_msg, $heap->{'remote_ip'}, $session_id);
1024 }
1025 # an error occurred
1026 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1027 # if an incoming msg could not be decrypted (maybe a wrong key), send client a ping. If the client
1028 # could not understand a msg from its server the client cause a re-registering process
1029 daemon_log("$session_id WARNING cannot understand incoming msg, send 'ping'-msg to all host with ip '".$heap->{remote_ip}.
1030 "' to cause a re-registering of the client if necessary", 3);
1031 my $sql_statement = "SELECT * FROM $main::known_clients_tn WHERE (hostname LIKE '".$heap->{'remote_ip'}."%')";
1032 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1033 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1034 my $host_name = $hit->{'hostname'};
1035 my $host_key = $hit->{'hostkey'};
1036 my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source> <target>$host_name</target></xml>";
1037 my $error = &send_msg_to_target($ping_msg, $host_name, $host_key, "gosa_ping", $session_id);
1038 &update_jobdb_status_for_send_msgs($ping_msg, $error);
1039 }
1040 $error++;
1041 }
1044 my $header;
1045 my $target;
1046 my $source;
1047 my $done = 0;
1048 my $sql;
1049 my $res;
1051 # check whether this message should be processed here
1052 if ($error == 0) {
1053 $header = @{$msg_hash->{'header'}}[0];
1054 $target = @{$msg_hash->{'target'}}[0];
1055 $source = @{$msg_hash->{'source'}}[0];
1056 my $not_found_in_known_clients_db = 0;
1057 my $not_found_in_known_server_db = 0;
1058 my $not_found_in_foreign_clients_db = 0;
1059 my $local_address;
1060 my ($target_ip, $target_port) = split(':', $target);
1061 if ($target =~ /^\d+\.\d+\.\d+\.\d+:\d+$/) {
1062 $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1063 } else {
1064 $local_address = $server_address;
1065 }
1067 # target and source is equal to GOSA -> process here
1068 if (not $done) {
1069 if ($target eq "GOSA" && $source eq "GOSA") {
1070 $done = 1;
1071 }
1072 }
1074 # target is own address without forward_to_gosa-tag -> process here
1075 if (not $done) {
1076 if (($target eq $local_address) && (not exists $msg_hash->{'forward_to_gosa'})) {
1077 $done = 1;
1078 if ($source eq "GOSA") {
1079 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1080 }
1081 #print STDERR "target is own address without forward_to_gosa-tag -> process here\n";
1082 }
1083 }
1085 # target is a client address in known_clients -> process here
1086 if (not $done) {
1087 $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1088 $res = $known_clients_db->select_dbentry($sql);
1089 if (keys(%$res) > 0) {
1090 $done = 1;
1091 my $hostname = $res->{1}->{'hostname'};
1092 $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1093 #print STDERR "target is a client address in known_clients -> process here\n";
1094 my $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1095 if ($source eq "GOSA") {
1096 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1097 }
1099 } else {
1100 $not_found_in_known_clients_db = 1;
1101 }
1102 }
1104 # target ist own address with forward_to_gosa-tag not pointing to myself -> process here
1105 if (not $done) {
1106 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1107 my $gosa_at;
1108 my $gosa_session_id;
1109 if (($target eq $local_address) && (defined $forward_to_gosa)){
1110 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1111 if ($gosa_at ne $local_address) {
1112 $done = 1;
1113 #print STDERR "target is own address with forward_to_gosa-tag not pointing to myself -> process here\n";
1114 }
1115 }
1116 }
1118 # if message should be processed here -> add message to incoming_db
1119 if ($done) {
1120 # if a job or a gosa message comes from a foreign server, fake module to GosaPackages
1121 # so gosa-si-server knows how to process this kind of messages
1122 if ($header =~ /^gosa_/ || $header =~ /^job_/) {
1123 $module = "GosaPackages";
1124 }
1126 my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1127 primkey=>[],
1128 headertag=>$header,
1129 targettag=>$target,
1130 xmlmessage=>&encode_base64($msg),
1131 timestamp=>&get_time,
1132 module=>$module,
1133 sessionid=>$session_id,
1134 } );
1135 }
1137 # target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa
1138 if (not $done) {
1139 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1140 my $gosa_at;
1141 my $gosa_session_id;
1142 if (($target eq $local_address) && (defined $forward_to_gosa)){
1143 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1144 if ($gosa_at eq $local_address) {
1145 my $session_reference = $kernel->ID_id_to_session($gosa_session_id);
1146 if( defined $session_reference ) {
1147 $heap = $session_reference->get_heap();
1148 }
1149 if(exists $heap->{'client'}) {
1150 $msg = &encrypt_msg($msg, $GosaPackages_key);
1151 $heap->{'client'}->put($msg);
1152 &daemon_log("$session_id INFO: incoming '$header' message forwarded to GOsa", 5);
1153 }
1154 $done = 1;
1155 #print STDERR "target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa\n";
1156 }
1157 }
1159 }
1161 # target is a client address in foreign_clients -> forward to registration server
1162 if (not $done) {
1163 $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1164 $res = $foreign_clients_db->select_dbentry($sql);
1165 if (keys(%$res) > 0) {
1166 my $hostname = $res->{1}->{'hostname'};
1167 my ($host_ip, $host_port) = split(/:/, $hostname);
1168 my $local_address = &get_local_ip_for_remote_ip($host_ip).":$server_port";
1169 my $regserver = $res->{1}->{'regserver'};
1170 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$regserver'";
1171 my $res = $known_server_db->select_dbentry($sql);
1172 if (keys(%$res) > 0) {
1173 my $regserver_key = $res->{1}->{'hostkey'};
1174 $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1175 $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1176 if ($source eq "GOSA") {
1177 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1178 }
1179 &send_msg_to_target($msg, $regserver, $regserver_key, $header, $session_id);
1180 }
1181 $done = 1;
1182 #print STDERR "target is a client address in foreign_clients -> forward to registration server\n";
1183 } else {
1184 $not_found_in_foreign_clients_db = 1;
1185 }
1186 }
1188 # target is a server address -> forward to server
1189 if (not $done) {
1190 $sql = "SELECT * FROM $known_server_tn WHERE hostname='$target'";
1191 $res = $known_server_db->select_dbentry($sql);
1192 if (keys(%$res) > 0) {
1193 my $hostkey = $res->{1}->{'hostkey'};
1195 if ($source eq "GOSA") {
1196 $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1197 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1199 }
1201 &send_msg_to_target($msg, $target, $hostkey, $header, $session_id);
1202 $done = 1;
1203 #print STDERR "target is a server address -> forward to server\n";
1204 } else {
1205 $not_found_in_known_server_db = 1;
1206 }
1207 }
1210 # target is not in foreign_clients_db, known_server_db or known_clients_db, maybe it is a complete new one -> process here
1211 if ( $not_found_in_foreign_clients_db
1212 && $not_found_in_known_server_db
1213 && $not_found_in_known_clients_db) {
1214 my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1215 primkey=>[],
1216 headertag=>$header,
1217 targettag=>$target,
1218 xmlmessage=>&encode_base64($msg),
1219 timestamp=>&get_time,
1220 module=>$module,
1221 sessionid=>$session_id,
1222 } );
1223 $done = 1;
1224 }
1227 if (not $done) {
1228 daemon_log("$session_id ERROR: do not know what to do with this message: $msg", 1);
1229 if ($source eq "GOSA") {
1230 my %data = ('error_msg' => &encode_base64($msg), 'error_string' => "Do not know what to do with this message!");
1231 my $error_msg = &build_msg("error", $local_address, "GOSA", \%data );
1233 my $session_reference = $kernel->ID_id_to_session($session_id);
1234 if( defined $session_reference ) {
1235 $heap = $session_reference->get_heap();
1236 }
1237 if(exists $heap->{'client'}) {
1238 $error_msg = &encrypt_msg($error_msg, $GosaPackages_key);
1239 $heap->{'client'}->put($error_msg);
1240 }
1241 }
1242 }
1244 }
1246 return;
1247 }
1250 sub next_task {
1251 my ($session, $heap, $task) = @_[SESSION, HEAP, ARG0];
1252 my $running_task = POE::Wheel::Run->new(
1253 Program => sub { process_task($session, $heap, $task) },
1254 StdioFilter => POE::Filter::Reference->new(),
1255 StdoutEvent => "task_result",
1256 StderrEvent => "task_debug",
1257 CloseEvent => "task_done",
1258 );
1259 $heap->{task}->{ $running_task->ID } = $running_task;
1260 }
1262 sub handle_task_result {
1263 my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
1264 my $client_answer = $result->{'answer'};
1265 if( $client_answer =~ s/session_id=(\d+)$// ) {
1266 my $session_id = $1;
1267 if( defined $session_id ) {
1268 my $session_reference = $kernel->ID_id_to_session($session_id);
1269 if( defined $session_reference ) {
1270 $heap = $session_reference->get_heap();
1271 }
1272 }
1274 if(exists $heap->{'client'}) {
1275 $heap->{'client'}->put($client_answer);
1276 }
1277 }
1278 $kernel->sig(CHLD => "child_reap");
1279 }
1281 sub handle_task_debug {
1282 my $result = $_[ARG0];
1283 print STDERR "$result\n";
1284 }
1286 sub handle_task_done {
1287 my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1288 delete $heap->{task}->{$task_id};
1289 }
1291 sub process_task {
1292 no strict "refs";
1293 #CHECK: Not @_[...]?
1294 my ($session, $heap, $task) = @_;
1295 my $error = 0;
1296 my $answer_l;
1297 my ($answer_header, @answer_target_l, $answer_source);
1298 my $client_answer = "";
1300 # prepare all variables needed to process message
1301 #my $msg = $task->{'xmlmessage'};
1302 my $msg = &decode_base64($task->{'xmlmessage'});
1303 my $incoming_id = $task->{'id'};
1304 my $module = $task->{'module'};
1305 my $header = $task->{'headertag'};
1306 my $session_id = $task->{'sessionid'};
1307 my $msg_hash = $xml->XMLin($msg, ForceArray=>1);
1308 my $source = @{$msg_hash->{'source'}}[0];
1310 # set timestamp of incoming client uptodate, so client will not
1311 # be deleted from known_clients because of expiration
1312 my $act_time = &get_time();
1313 my $sql = "UPDATE $known_clients_tn SET timestamp='$act_time' WHERE hostname='$source'";
1314 my $res = $known_clients_db->exec_statement($sql);
1316 ######################
1317 # process incoming msg
1318 if( $error == 0) {
1319 daemon_log("$session_id INFO: Incoming msg (session_id=$session_id) with header '".@{$msg_hash->{'header'}}[0]."'", 5);
1320 daemon_log("$session_id DEBUG: Processing module ".$module, 7);
1321 $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1323 if ( 0 < @{$answer_l} ) {
1324 my $answer_str = join("\n", @{$answer_l});
1325 while ($answer_str =~ /<header>(\w+)<\/header>/g) {
1326 daemon_log("$session_id INFO: got answer message with header '$1'", 5);
1327 }
1328 daemon_log("$session_id DEBUG: $module: got answer from module: \n".$answer_str,8);
1329 } else {
1330 daemon_log("$session_id DEBUG: $module: got no answer from module!" ,8);
1331 }
1333 }
1334 if( !$answer_l ) { $error++ };
1336 ########
1337 # answer
1338 if( $error == 0 ) {
1340 foreach my $answer ( @{$answer_l} ) {
1341 # check outgoing msg to xml validity
1342 my $answer_hash = &check_outgoing_xml_validity($answer, $session_id);
1343 if( not defined $answer_hash ) { next; }
1345 $answer_header = @{$answer_hash->{'header'}}[0];
1346 @answer_target_l = @{$answer_hash->{'target'}};
1347 $answer_source = @{$answer_hash->{'source'}}[0];
1349 # deliver msg to all targets
1350 foreach my $answer_target ( @answer_target_l ) {
1352 # targets of msg are all gosa-si-clients in known_clients_db
1353 if( $answer_target eq "*" ) {
1354 # answer is for all clients
1355 my $sql_statement= "SELECT * FROM known_clients";
1356 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1357 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1358 my $host_name = $hit->{hostname};
1359 my $host_key = $hit->{hostkey};
1360 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1361 &update_jobdb_status_for_send_msgs($answer, $error);
1362 }
1363 }
1365 # targets of msg are all gosa-si-server in known_server_db
1366 elsif( $answer_target eq "KNOWN_SERVER" ) {
1367 # answer is for all server in known_server
1368 my $sql_statement= "SELECT * FROM $known_server_tn";
1369 my $query_res = $known_server_db->select_dbentry( $sql_statement );
1370 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1371 my $host_name = $hit->{hostname};
1372 my $host_key = $hit->{hostkey};
1373 $answer =~ s/<target>\S+<\/target>/<target>$host_name<\/target>/g;
1374 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1375 &update_jobdb_status_for_send_msgs($answer, $error);
1376 }
1377 }
1379 # target of msg is GOsa
1380 elsif( $answer_target eq "GOSA" ) {
1381 my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1382 my $add_on = "";
1383 if( defined $session_id ) {
1384 $add_on = ".session_id=$session_id";
1385 }
1386 # answer is for GOSA and has to returned to connected client
1387 my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1388 $client_answer = $gosa_answer.$add_on;
1389 }
1391 # target of msg is job queue at this host
1392 elsif( $answer_target eq "JOBDB") {
1393 $answer =~ /<header>(\S+)<\/header>/;
1394 my $header;
1395 if( defined $1 ) { $header = $1; }
1396 my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1397 &update_jobdb_status_for_send_msgs($answer, $error);
1398 }
1400 # Target of msg is a mac address
1401 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 ) {
1402 daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients and foreign_clients", 5);
1403 my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1404 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1405 my $found_ip_flag = 0;
1406 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1407 my $host_name = $hit->{hostname};
1408 my $host_key = $hit->{hostkey};
1409 $answer =~ s/$answer_target/$host_name/g;
1410 daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1411 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1412 &update_jobdb_status_for_send_msgs($answer, $error);
1413 $found_ip_flag++ ;
1414 }
1415 if ($found_ip_flag == 0) {
1416 my $sql = "SELECT * FROM $foreign_clients_tn WHERE macaddress LIKE '$answer_target'";
1417 my $res = $foreign_clients_db->select_dbentry($sql);
1418 while( my ($hit_num, $hit) = each %{ $res } ) {
1419 my $host_name = $hit->{hostname};
1420 my $reg_server = $hit->{regserver};
1421 daemon_log("$session_id INFO: found host '$host_name' with mac '$answer_target', registered at '$reg_server'", 5);
1423 # Fetch key for reg_server
1424 my $reg_server_key;
1425 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$reg_server'";
1426 my $res = $known_server_db->select_dbentry($sql);
1427 if (exists $res->{1}) {
1428 $reg_server_key = $res->{1}->{'hostkey'};
1429 } else {
1430 daemon_log("$session_id ERROR: cannot find hostkey for '$host_name' in '$known_server_tn'", 1);
1431 daemon_log("$session_id ERROR: unable to forward answer to correct registration server, processing is aborted!", 1);
1432 $reg_server_key = undef;
1433 }
1435 # Send answer to server where client is registered
1436 if (defined $reg_server_key) {
1437 $answer =~ s/$answer_target/$host_name/g;
1438 my $error = &send_msg_to_target($answer, $reg_server, $reg_server_key, $answer_header, $session_id);
1439 &update_jobdb_status_for_send_msgs($answer, $error);
1440 $found_ip_flag++ ;
1441 }
1442 }
1443 }
1444 if( $found_ip_flag == 0) {
1445 daemon_log("$session_id WARNING: no host found in known_clients with mac address '$answer_target'", 3);
1446 }
1448 # Answer is for one specific host
1449 } else {
1450 # get encrypt_key
1451 my $encrypt_key = &get_encrypt_key($answer_target);
1452 if( not defined $encrypt_key ) {
1453 # unknown target
1454 daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1455 next;
1456 }
1457 my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1458 &update_jobdb_status_for_send_msgs($answer, $error);
1459 }
1460 }
1461 }
1462 }
1464 my $filter = POE::Filter::Reference->new();
1465 my %result = (
1466 status => "seems ok to me",
1467 answer => $client_answer,
1468 );
1470 my $output = $filter->put( [ \%result ] );
1471 print @$output;
1474 }
1476 sub session_start {
1477 my ($kernel) = $_[KERNEL];
1478 $global_kernel = $kernel;
1479 $kernel->yield('register_at_foreign_servers');
1480 $kernel->yield('create_fai_server_db', $fai_server_tn );
1481 $kernel->yield('create_fai_release_db', $fai_release_tn );
1482 $kernel->yield('watch_for_next_tasks');
1483 $kernel->sig(USR1 => "sig_handler");
1484 $kernel->sig(USR2 => "recreate_packages_db");
1485 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1486 $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay);
1487 $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay);
1488 $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1489 $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1490 $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1491 $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1493 # Start opsi check
1494 if ($opsi_enabled eq "true") {
1495 $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay);
1496 }
1498 }
1501 sub watch_for_done_jobs {
1502 #CHECK: $heap for what?
1503 my ($kernel,$heap) = @_[KERNEL, HEAP];
1505 my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((status='done') AND (modified='0'))";
1506 my $res = $job_db->select_dbentry( $sql_statement );
1508 while( my ($id, $hit) = each %{$res} ) {
1509 my $jobdb_id = $hit->{id};
1510 my $sql_statement = "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1511 my $res = $job_db->del_dbentry($sql_statement);
1512 }
1514 $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1515 }
1518 sub watch_for_opsi_jobs {
1519 my ($kernel) = $_[KERNEL];
1521 # This is not very nice to look for opsi install jobs, but headertag has to be trigger_action_reinstall. The only way to identify a
1522 # opsi install job is to parse the xml message. There is still the correct header.
1523 my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((xmlmessage LIKE '%opsi_install_client</header>%') AND (status='processing') AND (siserver='localhost'))";
1524 my $res = $job_db->select_dbentry( $sql_statement );
1526 # Ask OPSI for an update of the running jobs
1527 while (my ($id, $hit) = each %$res ) {
1528 # Determine current parameters of the job
1529 my $hostId = $hit->{'plainname'};
1530 my $macaddress = $hit->{'macaddress'};
1531 my $progress = $hit->{'progress'};
1533 my $result= {};
1535 # For hosts, only return the products that are or get installed
1536 my $callobj;
1537 $callobj = {
1538 method => 'getProductStates_hash',
1539 params => [ $hostId ],
1540 id => 1,
1541 };
1543 my $hres = $opsi_client->call($opsi_url, $callobj);
1544 #my ($hres_err, $hres_err_string) = &check_opsi_res($hres);
1545 if (not &check_opsi_res($hres)) {
1546 my $htmp= $hres->result->{$hostId};
1548 # Check state != not_installed or action == setup -> load and add
1549 my $products= 0;
1550 my $installed= 0;
1551 my $installing = 0;
1552 my $error= 0;
1553 my @installed_list;
1554 my @error_list;
1555 my $act_status = "none";
1556 foreach my $product (@{$htmp}){
1558 if ($product->{'installationStatus'} ne "not_installed" or
1559 $product->{'actionRequest'} eq "setup"){
1561 # Increase number of products for this host
1562 $products++;
1564 if ($product->{'installationStatus'} eq "failed"){
1565 $result->{$product->{'productId'}}= "error";
1566 unshift(@error_list, $product->{'productId'});
1567 $error++;
1568 }
1569 if ($product->{'installationStatus'} eq "installed" && $product->{'actionRequest'} eq "none"){
1570 $result->{$product->{'productId'}}= "installed";
1571 unshift(@installed_list, $product->{'productId'});
1572 $installed++;
1573 }
1574 if ($product->{'installationStatus'} eq "installing"){
1575 $result->{$product->{'productId'}}= "installing";
1576 $installing++;
1577 $act_status = "installing - ".$product->{'productId'};
1578 }
1579 }
1580 }
1582 # Estimate "rough" progress, avoid division by zero
1583 if ($products == 0) {
1584 $result->{'progress'}= 0;
1585 } else {
1586 $result->{'progress'}= int($installed * 100 / $products);
1587 }
1589 # Set updates in job queue
1590 if ((not $error) && (not $installing) && ($installed)) {
1591 $act_status = "installed - ".join(", ", @installed_list);
1592 }
1593 if ($error) {
1594 $act_status = "error - ".join(", ", @error_list);
1595 }
1596 if ($progress ne $result->{'progress'} ) {
1597 # Updating progress and result
1598 my $update_statement = "UPDATE $job_queue_tn SET modified='1', progress='".$result->{'progress'}."', result='$act_status' WHERE macaddress='$macaddress' AND siserver='localhost'";
1599 my $update_res = $job_db->update_dbentry($update_statement);
1600 }
1601 if ($progress eq 100) {
1602 # Updateing status
1603 my $done_statement = "UPDATE $job_queue_tn SET modified='1', ";
1604 if ($error) {
1605 $done_statement .= "status='error'";
1606 } else {
1607 $done_statement .= "status='done'";
1608 }
1609 $done_statement .= " WHERE macaddress='$macaddress' AND siserver='localhost'";
1610 my $done_res = $job_db->update_dbentry($done_statement);
1611 }
1614 }
1615 }
1617 $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay);
1618 }
1621 # If a job got an update or was modified anyway, send to all other si-server an update message of this jobs.
1622 sub watch_for_modified_jobs {
1623 my ($kernel,$heap) = @_[KERNEL, HEAP];
1625 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE ((siserver='localhost') AND (modified='1'))";
1626 my $res = $job_db->select_dbentry( $sql_statement );
1628 # if db contains no jobs which should be update, do nothing
1629 if (keys %$res != 0) {
1631 if ($job_synchronization eq "true") {
1632 # make out of the db result a gosa-si message
1633 my $update_msg = &db_res2si_msg ($res, "foreign_job_updates", "KNOWN_SERVER", "MY_LOCAL_ADDRESS");
1635 # update all other SI-server
1636 &inform_all_other_si_server($update_msg);
1637 }
1639 # set jobs all jobs to modified = 0, wait until the next modification for updates of other si-server
1640 $sql_statement = "UPDATE $job_queue_tn SET modified='0' ";
1641 $res = $job_db->update_dbentry($sql_statement);
1642 }
1644 $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay);
1645 }
1648 sub watch_for_new_jobs {
1649 if($watch_for_new_jobs_in_progress == 0) {
1650 $watch_for_new_jobs_in_progress = 1;
1651 my ($kernel,$heap) = @_[KERNEL, HEAP];
1653 # check gosa job quaeue for jobs with executable timestamp
1654 my $timestamp = &get_time();
1655 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE status='waiting' AND (CAST (timestamp AS INTEGER)) < $timestamp ORDER BY timestamp";
1656 my $res = $job_db->exec_statement( $sql_statement );
1658 # Merge all new jobs that would do the same actions
1659 my @drops;
1660 my $hits;
1661 foreach my $hit (reverse @{$res} ) {
1662 my $macaddress= lc @{$hit}[8];
1663 my $headertag= @{$hit}[5];
1664 if(
1665 defined($hits->{$macaddress}) &&
1666 defined($hits->{$macaddress}->{$headertag}) &&
1667 defined($hits->{$macaddress}->{$headertag}[0])
1668 ) {
1669 push @drops, "DELETE FROM $job_queue_tn WHERE id = $hits->{$macaddress}->{$headertag}[0]";
1670 }
1671 $hits->{$macaddress}->{$headertag}= $hit;
1672 }
1674 # Delete new jobs with a matching job in state 'processing'
1675 foreach my $macaddress (keys %{$hits}) {
1676 foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1677 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1678 if(defined($jobdb_id)) {
1679 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND headertag='$jobdb_headertag' AND status='processing'";
1680 my $res = $job_db->exec_statement( $sql_statement );
1681 foreach my $hit (@{$res}) {
1682 push @drops, "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1683 }
1684 } else {
1685 daemon_log("J ERROR: Job without id exists for macaddress $macaddress!", 1);
1686 }
1687 }
1688 }
1690 # Commit deletion
1691 $job_db->exec_statementlist(\@drops);
1693 # Look for new jobs that could be executed
1694 foreach my $macaddress (keys %{$hits}) {
1696 # Look if there is an executing job
1697 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='processing'";
1698 my $res = $job_db->exec_statement( $sql_statement );
1700 # Skip new jobs for host if there is a processing job
1701 if(defined($res) and defined @{$res}[0]) {
1702 next;
1703 }
1705 foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1706 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1707 if(defined($jobdb_id)) {
1708 my $job_msg = @{$hits->{$macaddress}->{$jobdb_headertag}}[7];
1710 daemon_log("J DEBUG: its time to execute $job_msg", 7);
1711 my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1712 my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1714 # expect macaddress is unique!!!!!!
1715 my $target = $res_hash->{1}->{hostname};
1717 # change header
1718 $job_msg =~ s/<header>job_/<header>gosa_/;
1720 # add sqlite_id
1721 $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1723 $job_msg =~ /<header>(\S+)<\/header>/;
1724 my $header = $1 ;
1725 my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");
1727 # update status in job queue to 'processing'
1728 $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
1729 my $res = $job_db->update_dbentry($sql_statement);
1730 # TODO: abfangen ob alles in ordnung ist oder nicht, wenn nicht error schmeißen
1732 # We don't want parallel processing
1733 last;
1734 }
1735 }
1736 }
1738 $watch_for_new_jobs_in_progress = 0;
1739 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1740 }
1741 }
1744 sub watch_for_new_messages {
1745 my ($kernel,$heap) = @_[KERNEL, HEAP];
1746 my @coll_user_msg; # collection list of outgoing messages
1748 # check messaging_db for new incoming messages with executable timestamp
1749 my $timestamp = &get_time();
1750 my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS INTEGER))<$timestamp AND flag='n' AND direction='in' )";
1751 my $res = $messaging_db->exec_statement( $sql_statement );
1752 foreach my $hit (@{$res}) {
1754 # create outgoing messages
1755 my $message_to = @{$hit}[3];
1756 # translate message_to to plain login name
1757 my @message_to_l = split(/,/, $message_to);
1758 my %receiver_h;
1759 foreach my $receiver (@message_to_l) {
1760 if ($receiver =~ /^u_([\s\S]*)$/) {
1761 $receiver_h{$1} = 0;
1762 } elsif ($receiver =~ /^g_([\s\S]*)$/) {
1763 my $group_name = $1;
1764 # fetch all group members from ldap and add them to receiver hash
1765 my $ldap_handle = &get_ldap_handle();
1766 if (defined $ldap_handle) {
1767 my $mesg = $ldap_handle->search(
1768 base => $ldap_base,
1769 scope => 'sub',
1770 attrs => ['memberUid'],
1771 filter => "cn=$group_name",
1772 );
1773 if ($mesg->count) {
1774 my @entries = $mesg->entries;
1775 foreach my $entry (@entries) {
1776 my @receivers= $entry->get_value("memberUid");
1777 foreach my $receiver (@receivers) {
1778 $receiver_h{$1} = 0;
1779 }
1780 }
1781 }
1782 # translating errors ?
1783 if ($mesg->code) {
1784 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: $mesg->error", 1);
1785 }
1786 # ldap handle error ?
1787 } else {
1788 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: no ldap handle available", 1);
1789 }
1790 } else {
1791 my $sbjct = &encode_base64(@{$hit}[1]);
1792 my $msg = &encode_base64(@{$hit}[7]);
1793 &daemon_log("M WARNING: unknown receiver '$receiver' for a user-message '$sbjct - $msg'", 3);
1794 }
1795 }
1796 my @receiver_l = keys(%receiver_h);
1798 my $message_id = @{$hit}[0];
1800 #add each outgoing msg to messaging_db
1801 my $receiver;
1802 foreach $receiver (@receiver_l) {
1803 my $sql_statement = "INSERT INTO $messaging_tn (id, subject, message_from, message_to, flag, direction, delivery_time, message, timestamp) ".
1804 "VALUES ('".
1805 $message_id."', '". # id
1806 @{$hit}[1]."', '". # subject
1807 @{$hit}[2]."', '". # message_from
1808 $receiver."', '". # message_to
1809 "none"."', '". # flag
1810 "out"."', '". # direction
1811 @{$hit}[6]."', '". # delivery_time
1812 @{$hit}[7]."', '". # message
1813 $timestamp."'". # timestamp
1814 ")";
1815 &daemon_log("M DEBUG: $sql_statement", 1);
1816 my $res = $messaging_db->exec_statement($sql_statement);
1817 &daemon_log("M INFO: message '".@{$hit}[0]."' is prepared for delivery to receiver '$receiver'", 5);
1818 }
1820 # set incoming message to flag d=deliverd
1821 $sql_statement = "UPDATE $messaging_tn SET flag='p' WHERE id='$message_id'";
1822 &daemon_log("M DEBUG: $sql_statement", 7);
1823 $res = $messaging_db->update_dbentry($sql_statement);
1824 &daemon_log("M INFO: message '$message_id' is set to flag 'p' (processed)", 5);
1825 }
1827 $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1828 return;
1829 }
1831 sub watch_for_delivery_messages {
1832 my ($kernel, $heap) = @_[KERNEL, HEAP];
1834 # select outgoing messages
1835 my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( flag='p' AND direction='out' )";
1836 #&daemon_log("0 DEBUG: $sql", 7);
1837 my $res = $messaging_db->exec_statement( $sql_statement );
1839 # build out msg for each usr
1840 foreach my $hit (@{$res}) {
1841 my $receiver = @{$hit}[3];
1842 my $msg_id = @{$hit}[0];
1843 my $subject = @{$hit}[1];
1844 my $message = @{$hit}[7];
1846 # resolve usr -> host where usr is logged in
1847 my $sql = "SELECT * FROM $login_users_tn WHERE (user='$receiver')";
1848 #&daemon_log("0 DEBUG: $sql", 7);
1849 my $res = $login_users_db->exec_statement($sql);
1851 # reciver is logged in nowhere
1852 if (not ref(@$res[0]) eq "ARRAY") { next; }
1854 my $send_succeed = 0;
1855 foreach my $hit (@$res) {
1856 my $receiver_host = @$hit[0];
1857 &daemon_log("M DEBUG: user '$receiver' is logged in at host '$receiver_host'", 7);
1859 # fetch key to encrypt msg propperly for usr/host
1860 my $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$receiver_host')";
1861 &daemon_log("0 DEBUG: $sql", 7);
1862 my $res = $known_clients_db->exec_statement($sql);
1864 # host is already down
1865 if (not ref(@$res[0]) eq "ARRAY") { next; }
1867 # host is on
1868 my $receiver_key = @{@{$res}[0]}[2];
1869 my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
1870 my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data );
1871 my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0);
1872 if ($error == 0 ) {
1873 $send_succeed++ ;
1874 }
1875 }
1877 if ($send_succeed) {
1878 # set outgoing msg at db to deliverd
1879 my $sql = "UPDATE $messaging_tn SET flag='d' WHERE (id='$msg_id' AND direction='out' AND message_to='$receiver')";
1880 &daemon_log("0 DEBUG: $sql", 7);
1881 my $res = $messaging_db->exec_statement($sql);
1882 }
1883 }
1885 $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1886 return;
1887 }
1890 sub watch_for_done_messages {
1891 my ($kernel,$heap) = @_[KERNEL, HEAP];
1893 my $sql = "SELECT * FROM $messaging_tn WHERE (flag='p' AND direction='in')";
1894 #&daemon_log("0 DEBUG: $sql", 7);
1895 my $res = $messaging_db->exec_statement($sql);
1897 foreach my $hit (@{$res}) {
1898 my $msg_id = @{$hit}[0];
1900 my $sql = "SELECT * FROM $messaging_tn WHERE (id='$msg_id' AND direction='out' AND (NOT flag='s'))";
1901 #&daemon_log("0 DEBUG: $sql", 7);
1902 my $res = $messaging_db->exec_statement($sql);
1904 # not all usr msgs have been seen till now
1905 if ( ref(@$res[0]) eq "ARRAY") { next; }
1907 $sql = "DELETE FROM $messaging_tn WHERE (id='$msg_id')";
1908 #&daemon_log("0 DEBUG: $sql", 7);
1909 $res = $messaging_db->exec_statement($sql);
1911 }
1913 $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1914 return;
1915 }
1918 sub watch_for_old_known_clients {
1919 my ($kernel,$heap) = @_[KERNEL, HEAP];
1921 my $sql_statement = "SELECT * FROM $known_clients_tn";
1922 my $res = $known_clients_db->select_dbentry( $sql_statement );
1924 my $act_time = int(&get_time());
1926 while ( my ($hit_num, $hit) = each %$res) {
1927 my $expired_timestamp = int($hit->{'timestamp'});
1928 $expired_timestamp =~ /(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/;
1929 my $dt = DateTime->new( year => $1,
1930 month => $2,
1931 day => $3,
1932 hour => $4,
1933 minute => $5,
1934 second => $6,
1935 );
1937 $dt->add( seconds => 2 * int($hit->{'keylifetime'}) );
1938 $expired_timestamp = $dt->ymd('').$dt->hms('');
1939 if ($act_time > $expired_timestamp) {
1940 my $hostname = $hit->{'hostname'};
1941 my $del_sql = "DELETE FROM $known_clients_tn WHERE hostname='$hostname'";
1942 my $del_res = $known_clients_db->exec_statement($del_sql);
1944 &main::daemon_log("0 INFO: timestamp '".$hit->{'timestamp'}."' of client '$hostname' is expired('$expired_timestamp'), client will be deleted from known_clients_db", 5);
1945 }
1947 }
1949 $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1950 }
1953 sub watch_for_next_tasks {
1954 my ($kernel,$heap) = @_[KERNEL, HEAP];
1956 my $sql = "SELECT * FROM $incoming_tn";
1957 my $res = $incoming_db->select_dbentry($sql);
1959 while ( my ($hit_num, $hit) = each %$res) {
1960 my $headertag = $hit->{'headertag'};
1961 if ($headertag =~ /^answer_(\d+)/) {
1962 # do not start processing, this message is for a still running POE::Wheel
1963 next;
1964 }
1965 my $message_id = $hit->{'id'};
1966 $kernel->yield('next_task', $hit);
1968 my $sql = "DELETE FROM $incoming_tn WHERE id=$message_id";
1969 my $res = $incoming_db->exec_statement($sql);
1970 }
1972 $kernel->delay_set('watch_for_next_tasks', 0.1);
1973 }
1976 sub get_ldap_handle {
1977 my ($session_id) = @_;
1978 my $heap;
1979 my $ldap_handle;
1981 if (not defined $session_id ) { $session_id = 0 };
1982 if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
1984 if ($session_id == 0) {
1985 daemon_log("$session_id DEBUG: get_ldap_handle invoked without a session_id, create a new ldap_handle", 7);
1986 $ldap_handle = Net::LDAP->new( $ldap_uri );
1987 $ldap_handle->bind($ldap_admin_dn, password => $ldap_admin_password) or daemon_log("$session_id ERROR: Bind to LDAP $ldap_uri as $ldap_admin_dn failed!");
1989 } else {
1990 my $session_reference = $global_kernel->ID_id_to_session($session_id);
1991 if( defined $session_reference ) {
1992 $heap = $session_reference->get_heap();
1993 }
1995 if (not defined $heap) {
1996 daemon_log("$session_id DEBUG: cannot get heap for session_id '$session_id'", 7);
1997 return;
1998 }
2000 # TODO: This "if" is nonsense, because it doesn't prove that the
2001 # used handle is still valid - or if we've to reconnect...
2002 #if (not exists $heap->{ldap_handle}) {
2003 $ldap_handle = Net::LDAP->new( $ldap_uri );
2004 $ldap_handle->bind($ldap_admin_dn, password => $ldap_admin_password) or daemon_log("$session_id ERROR: Bind to LDAP $ldap_uri as $ldap_admin_dn failed!");
2005 $heap->{ldap_handle} = $ldap_handle;
2006 #}
2007 }
2008 return $ldap_handle;
2009 }
2012 sub change_fai_state {
2013 my ($st, $targets, $session_id) = @_;
2014 $session_id = 0 if not defined $session_id;
2015 # Set FAI state to localboot
2016 my %mapActions= (
2017 reboot => '',
2018 update => 'softupdate',
2019 localboot => 'localboot',
2020 reinstall => 'install',
2021 rescan => '',
2022 wake => '',
2023 memcheck => 'memcheck',
2024 sysinfo => 'sysinfo',
2025 install => 'install',
2026 );
2028 # Return if this is unknown
2029 if (!exists $mapActions{ $st }){
2030 daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1);
2031 return;
2032 }
2034 my $state= $mapActions{ $st };
2036 my $ldap_handle = &get_ldap_handle($session_id);
2037 if( defined($ldap_handle) ) {
2039 # Build search filter for hosts
2040 my $search= "(&(objectClass=GOhard)";
2041 foreach (@{$targets}){
2042 $search.= "(macAddress=$_)";
2043 }
2044 $search.= ")";
2046 # If there's any host inside of the search string, procress them
2047 if (!($search =~ /macAddress/)){
2048 daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);
2049 return;
2050 }
2052 # Perform search for Unit Tag
2053 my $mesg = $ldap_handle->search(
2054 base => $ldap_base,
2055 scope => 'sub',
2056 attrs => ['dn', 'FAIstate', 'objectClass'],
2057 filter => "$search"
2058 );
2060 if ($mesg->count) {
2061 my @entries = $mesg->entries;
2062 if (0 == @entries) {
2063 daemon_log("$session_id ERROR: ldap search failed: ldap_base=$ldap_base, filter=$search", 1);
2064 }
2066 foreach my $entry (@entries) {
2067 # Only modify entry if it is not set to '$state'
2068 if ($entry->get_value("FAIstate") ne "$state"){
2069 daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
2070 my $result;
2071 my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
2072 if (exists $tmp{'FAIobject'}){
2073 if ($state eq ''){
2074 $result= $ldap_handle->modify($entry->dn, changes => [
2075 delete => [ FAIstate => [] ] ]);
2076 } else {
2077 $result= $ldap_handle->modify($entry->dn, changes => [
2078 replace => [ FAIstate => $state ] ]);
2079 }
2080 } elsif ($state ne ''){
2081 $result= $ldap_handle->modify($entry->dn, changes => [
2082 add => [ objectClass => 'FAIobject' ],
2083 add => [ FAIstate => $state ] ]);
2084 }
2086 # Errors?
2087 if ($result->code){
2088 daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2089 }
2090 } else {
2091 daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 7);
2092 }
2093 }
2094 } else {
2095 daemon_log("$session_id ERROR: LDAP search failed: ldap_base=$ldap_base, filter=$search", 1);
2096 }
2098 # if no ldap handle defined
2099 } else {
2100 daemon_log("$session_id ERROR: no LDAP handle defined for update FAIstate", 1);
2101 }
2103 return;
2104 }
2107 sub change_goto_state {
2108 my ($st, $targets, $session_id) = @_;
2109 $session_id = 0 if not defined $session_id;
2111 # Switch on or off?
2112 my $state= $st eq 'active' ? 'active': 'locked';
2114 my $ldap_handle = &get_ldap_handle($session_id);
2115 if( defined($ldap_handle) ) {
2117 # Build search filter for hosts
2118 my $search= "(&(objectClass=GOhard)";
2119 foreach (@{$targets}){
2120 $search.= "(macAddress=$_)";
2121 }
2122 $search.= ")";
2124 # If there's any host inside of the search string, procress them
2125 if (!($search =~ /macAddress/)){
2126 return;
2127 }
2129 # Perform search for Unit Tag
2130 my $mesg = $ldap_handle->search(
2131 base => $ldap_base,
2132 scope => 'sub',
2133 attrs => ['dn', 'gotoMode'],
2134 filter => "$search"
2135 );
2137 if ($mesg->count) {
2138 my @entries = $mesg->entries;
2139 foreach my $entry (@entries) {
2141 # Only modify entry if it is not set to '$state'
2142 if ($entry->get_value("gotoMode") ne $state){
2144 daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
2145 my $result;
2146 $result= $ldap_handle->modify($entry->dn, changes => [
2147 replace => [ gotoMode => $state ] ]);
2149 # Errors?
2150 if ($result->code){
2151 &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2152 }
2154 }
2155 }
2156 } else {
2157 daemon_log("$session_id ERROR: LDAP search failed in function change_goto_state: ldap_base=$ldap_base, filter=$search", 1);
2158 }
2160 }
2161 }
2164 sub run_recreate_packages_db {
2165 my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2166 my $session_id = $session->ID;
2167 &main::daemon_log("$session_id INFO: Recreating FAI Packages DB ('$fai_release_tn', '$fai_server_tn', '$packages_list_tn')", 5);
2168 $kernel->yield('create_fai_release_db', $fai_release_tn);
2169 $kernel->yield('create_fai_server_db', $fai_server_tn);
2170 return;
2171 }
2174 sub run_create_fai_server_db {
2175 my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2176 my $session_id = $session->ID;
2177 my $task = POE::Wheel::Run->new(
2178 Program => sub { &create_fai_server_db($table_name,$kernel, undef, $session_id) },
2179 StdoutEvent => "session_run_result",
2180 StderrEvent => "session_run_debug",
2181 CloseEvent => "session_run_done",
2182 );
2184 $heap->{task}->{ $task->ID } = $task;
2185 return;
2186 }
2189 sub create_fai_server_db {
2190 my ($table_name, $kernel, $dont_create_packages_list, $session_id) = @_;
2191 my $result;
2193 if (not defined $session_id) { $session_id = 0; }
2194 my $ldap_handle = &get_ldap_handle();
2195 if(defined($ldap_handle)) {
2196 daemon_log("$session_id INFO: create_fai_server_db: start", 5);
2197 my $mesg= $ldap_handle->search(
2198 base => $ldap_base,
2199 scope => 'sub',
2200 attrs => ['FAIrepository', 'gosaUnitTag'],
2201 filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
2202 );
2203 if($mesg->{'resultCode'} == 0 &&
2204 $mesg->count != 0) {
2205 foreach my $entry (@{$mesg->{entries}}) {
2206 if($entry->exists('FAIrepository')) {
2207 # Add an entry for each Repository configured for server
2208 foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
2209 my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
2210 my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
2211 $result= $fai_server_db->add_dbentry( {
2212 table => $table_name,
2213 primkey => ['server', 'release', 'tag'],
2214 server => $tmp_url,
2215 release => $tmp_release,
2216 sections => $tmp_sections,
2217 tag => (length($tmp_tag)>0)?$tmp_tag:"",
2218 } );
2219 }
2220 }
2221 }
2222 }
2223 daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
2225 # TODO: Find a way to post the 'create_packages_list_db' event
2226 if(not defined($dont_create_packages_list)) {
2227 &create_packages_list_db(undef, undef, $session_id);
2228 }
2229 }
2231 $ldap_handle->disconnect;
2232 return $result;
2233 }
2236 sub run_create_fai_release_db {
2237 my ($session, $heap, $table_name) = @_[SESSION, HEAP, ARG0];
2238 my $session_id = $session->ID;
2239 my $task = POE::Wheel::Run->new(
2240 Program => sub { &create_fai_release_db($table_name, $session_id) },
2241 StdoutEvent => "session_run_result",
2242 StderrEvent => "session_run_debug",
2243 CloseEvent => "session_run_done",
2244 );
2246 $heap->{task}->{ $task->ID } = $task;
2247 return;
2248 }
2251 sub create_fai_release_db {
2252 my ($table_name, $session_id) = @_;
2253 my $result;
2255 # used for logging
2256 if (not defined $session_id) { $session_id = 0; }
2258 my $ldap_handle = &get_ldap_handle();
2259 if(defined($ldap_handle)) {
2260 daemon_log("$session_id INFO: create_fai_release_db: start",5);
2261 my $mesg= $ldap_handle->search(
2262 base => $ldap_base,
2263 scope => 'sub',
2264 attrs => [],
2265 filter => "(&(objectClass=organizationalUnit)(ou=fai))",
2266 );
2267 if($mesg->{'resultCode'} == 0 &&
2268 $mesg->count != 0) {
2269 # Walk through all possible FAI container ou's
2270 my @sql_list;
2271 my $timestamp= &get_time();
2272 foreach my $ou (@{$mesg->{entries}}) {
2273 my $tmp_classes= resolve_fai_classes($ou->dn, $ldap_handle, $session_id);
2274 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
2275 my @tmp_array=get_fai_release_entries($tmp_classes);
2276 if(@tmp_array) {
2277 foreach my $entry (@tmp_array) {
2278 if(defined($entry) && ref($entry) eq 'HASH') {
2279 my $sql=
2280 "INSERT INTO $table_name "
2281 ."(timestamp, release, class, type, state) VALUES ("
2282 .$timestamp.","
2283 ."'".$entry->{'release'}."',"
2284 ."'".$entry->{'class'}."',"
2285 ."'".$entry->{'type'}."',"
2286 ."'".$entry->{'state'}."')";
2287 push @sql_list, $sql;
2288 }
2289 }
2290 }
2291 }
2292 }
2294 daemon_log("$session_id DEBUG: Inserting ".scalar @sql_list." entries to DB",8);
2295 if(@sql_list) {
2296 unshift @sql_list, "VACUUM";
2297 unshift @sql_list, "DELETE FROM $table_name";
2298 $fai_release_db->exec_statementlist(\@sql_list);
2299 }
2300 daemon_log("$session_id DEBUG: Done with inserting",7);
2301 }
2302 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
2303 }
2304 $ldap_handle->disconnect;
2305 return $result;
2306 }
2308 sub get_fai_types {
2309 my $tmp_classes = shift || return undef;
2310 my @result;
2312 foreach my $type(keys %{$tmp_classes}) {
2313 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
2314 my $entry = {
2315 type => $type,
2316 state => $tmp_classes->{$type}[0],
2317 };
2318 push @result, $entry;
2319 }
2320 }
2322 return @result;
2323 }
2325 sub get_fai_state {
2326 my $result = "";
2327 my $tmp_classes = shift || return $result;
2329 foreach my $type(keys %{$tmp_classes}) {
2330 if(defined($tmp_classes->{$type}[0])) {
2331 $result = $tmp_classes->{$type}[0];
2333 # State is equal for all types in class
2334 last;
2335 }
2336 }
2338 return $result;
2339 }
2341 sub resolve_fai_classes {
2342 my ($fai_base, $ldap_handle, $session_id) = @_;
2343 if (not defined $session_id) { $session_id = 0; }
2344 my $result;
2345 my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
2346 my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
2347 my $fai_classes;
2349 daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base",7);
2350 my $mesg= $ldap_handle->search(
2351 base => $fai_base,
2352 scope => 'sub',
2353 attrs => ['cn','objectClass','FAIstate'],
2354 filter => $fai_filter,
2355 );
2356 daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries",7);
2358 if($mesg->{'resultCode'} == 0 &&
2359 $mesg->count != 0) {
2360 foreach my $entry (@{$mesg->{entries}}) {
2361 if($entry->exists('cn')) {
2362 my $tmp_dn= $entry->dn();
2364 # Skip classname and ou dn parts for class
2365 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?),$fai_base$/;
2367 # Skip classes without releases
2368 if((!defined($tmp_release)) || length($tmp_release)==0) {
2369 next;
2370 }
2372 my $tmp_cn= $entry->get_value('cn');
2373 my $tmp_state= $entry->get_value('FAIstate');
2375 my $tmp_type;
2376 # Get FAI type
2377 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
2378 if(grep $_ eq $oclass, @possible_fai_classes) {
2379 $tmp_type= $oclass;
2380 last;
2381 }
2382 }
2384 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2385 # A Subrelease
2386 my @sub_releases = split(/,/, $tmp_release);
2388 # Walk through subreleases and build hash tree
2389 my $hash;
2390 while(my $tmp_sub_release = pop @sub_releases) {
2391 $hash .= "\{'$tmp_sub_release'\}->";
2392 }
2393 eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
2394 } else {
2395 # A branch, no subrelease
2396 push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
2397 }
2398 } elsif (!$entry->exists('cn')) {
2399 my $tmp_dn= $entry->dn();
2400 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?),$fai_base$/;
2402 # Skip classes without releases
2403 if((!defined($tmp_release)) || length($tmp_release)==0) {
2404 next;
2405 }
2407 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2408 # A Subrelease
2409 my @sub_releases= split(/,/, $tmp_release);
2411 # Walk through subreleases and build hash tree
2412 my $hash;
2413 while(my $tmp_sub_release = pop @sub_releases) {
2414 $hash .= "\{'$tmp_sub_release'\}->";
2415 }
2416 # Remove the last two characters
2417 chop($hash);
2418 chop($hash);
2420 eval('$fai_classes->'.$hash.'= {}');
2421 } else {
2422 # A branch, no subrelease
2423 if(!exists($fai_classes->{$tmp_release})) {
2424 $fai_classes->{$tmp_release} = {};
2425 }
2426 }
2427 }
2428 }
2430 # The hash is complete, now we can honor the copy-on-write based missing entries
2431 foreach my $release (keys %$fai_classes) {
2432 $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
2433 }
2434 }
2435 return $result;
2436 }
2438 sub apply_fai_inheritance {
2439 my $fai_classes = shift || return {};
2440 my $tmp_classes;
2442 # Get the classes from the branch
2443 foreach my $class (keys %{$fai_classes}) {
2444 # Skip subreleases
2445 if($class =~ /^ou=.*$/) {
2446 next;
2447 } else {
2448 $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
2449 }
2450 }
2452 # Apply to each subrelease
2453 foreach my $subrelease (keys %{$fai_classes}) {
2454 if($subrelease =~ /ou=/) {
2455 foreach my $tmp_class (keys %{$tmp_classes}) {
2456 if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
2457 $fai_classes->{$subrelease}->{$tmp_class} =
2458 deep_copy($tmp_classes->{$tmp_class});
2459 } else {
2460 foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
2461 if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
2462 $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
2463 deep_copy($tmp_classes->{$tmp_class}->{$type});
2464 }
2465 }
2466 }
2467 }
2468 }
2469 }
2471 # Find subreleases in deeper levels
2472 foreach my $subrelease (keys %{$fai_classes}) {
2473 if($subrelease =~ /ou=/) {
2474 foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
2475 if($subsubrelease =~ /ou=/) {
2476 apply_fai_inheritance($fai_classes->{$subrelease});
2477 }
2478 }
2479 }
2480 }
2482 return $fai_classes;
2483 }
2485 sub get_fai_release_entries {
2486 my $tmp_classes = shift || return;
2487 my $parent = shift || "";
2488 my @result = shift || ();
2490 foreach my $entry (keys %{$tmp_classes}) {
2491 if(defined($entry)) {
2492 if($entry =~ /^ou=.*$/) {
2493 my $release_name = $entry;
2494 $release_name =~ s/ou=//g;
2495 if(length($parent)>0) {
2496 $release_name = $parent."/".$release_name;
2497 }
2498 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2499 foreach my $bufentry(@bufentries) {
2500 push @result, $bufentry;
2501 }
2502 } else {
2503 my @types = get_fai_types($tmp_classes->{$entry});
2504 foreach my $type (@types) {
2505 push @result,
2506 {
2507 'class' => $entry,
2508 'type' => $type->{'type'},
2509 'release' => $parent,
2510 'state' => $type->{'state'},
2511 };
2512 }
2513 }
2514 }
2515 }
2517 return @result;
2518 }
2520 sub deep_copy {
2521 my $this = shift;
2522 if (not ref $this) {
2523 $this;
2524 } elsif (ref $this eq "ARRAY") {
2525 [map deep_copy($_), @$this];
2526 } elsif (ref $this eq "HASH") {
2527 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2528 } else { die "what type is $_?" }
2529 }
2532 sub session_run_result {
2533 my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];
2534 $kernel->sig(CHLD => "child_reap");
2535 }
2537 sub session_run_debug {
2538 my $result = $_[ARG0];
2539 print STDERR "$result\n";
2540 }
2542 sub session_run_done {
2543 my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2544 delete $heap->{task}->{$task_id};
2545 }
2548 sub create_sources_list {
2549 my $session_id = shift;
2550 my $ldap_handle = &main::get_ldap_handle;
2551 my $result="/tmp/gosa_si_tmp_sources_list";
2553 # Remove old file
2554 if(stat($result)) {
2555 unlink($result);
2556 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7);
2557 }
2559 my $fh;
2560 open($fh, ">$result");
2561 if (not defined $fh) {
2562 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7);
2563 return undef;
2564 }
2565 if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2566 my $mesg=$ldap_handle->search(
2567 base => $main::ldap_server_dn,
2568 scope => 'base',
2569 attrs => 'FAIrepository',
2570 filter => 'objectClass=FAIrepositoryServer'
2571 );
2572 if($mesg->count) {
2573 foreach my $entry(@{$mesg->{'entries'}}) {
2574 foreach my $value(@{$entry->get_value('FAIrepository', asref => 1)}) {
2575 my ($server, $tag, $release, $sections)= split /\|/, $value;
2576 my $line = "deb $server $release";
2577 $sections =~ s/,/ /g;
2578 $line.= " $sections";
2579 print $fh $line."\n";
2580 }
2581 }
2582 }
2583 } else {
2584 if (defined $main::ldap_server_dn){
2585 &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1);
2586 } else {
2587 &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2588 }
2589 }
2590 close($fh);
2592 return $result;
2593 }
2596 sub run_create_packages_list_db {
2597 my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2598 my $session_id = $session->ID;
2600 my $task = POE::Wheel::Run->new(
2601 Priority => +20,
2602 Program => sub {&create_packages_list_db(undef, undef, $session_id)},
2603 StdoutEvent => "session_run_result",
2604 StderrEvent => "session_run_debug",
2605 CloseEvent => "session_run_done",
2606 );
2607 $heap->{task}->{ $task->ID } = $task;
2608 }
2611 sub create_packages_list_db {
2612 my ($ldap_handle, $sources_file, $session_id) = @_;
2614 # it should not be possible to trigger a recreation of packages_list_db
2615 # while packages_list_db is under construction, so set flag packages_list_under_construction
2616 # which is tested befor recreation can be started
2617 if (-r $packages_list_under_construction) {
2618 daemon_log("$session_id WARNING: packages_list_db is right now under construction, please wait until this process is finished", 3);
2619 return;
2620 } else {
2621 daemon_log("$session_id INFO: create_packages_list_db: start", 5);
2622 # set packages_list_under_construction to true
2623 system("touch $packages_list_under_construction");
2624 @packages_list_statements=();
2625 }
2627 if (not defined $session_id) { $session_id = 0; }
2628 if (not defined $ldap_handle) {
2629 $ldap_handle= &get_ldap_handle();
2631 if (not defined $ldap_handle) {
2632 daemon_log("$session_id ERROR: no ldap_handle available to create_packages_list_db", 1);
2633 unlink($packages_list_under_construction);
2634 return;
2635 }
2636 }
2637 if (not defined $sources_file) {
2638 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5);
2639 $sources_file = &create_sources_list($session_id);
2640 }
2642 if (not defined $sources_file) {
2643 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1);
2644 unlink($packages_list_under_construction);
2645 return;
2646 }
2648 my $line;
2650 open(CONFIG, "<$sources_file") or do {
2651 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2652 unlink($packages_list_under_construction);
2653 return;
2654 };
2656 # Read lines
2657 while ($line = <CONFIG>){
2658 # Unify
2659 chop($line);
2660 $line =~ s/^\s+//;
2661 $line =~ s/^\s+/ /;
2663 # Strip comments
2664 $line =~ s/#.*$//g;
2666 # Skip empty lines
2667 if ($line =~ /^\s*$/){
2668 next;
2669 }
2671 # Interpret deb line
2672 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2673 my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2674 my $section;
2675 foreach $section (split(' ', $sections)){
2676 &parse_package_info( $baseurl, $dist, $section, $session_id );
2677 }
2678 }
2679 }
2681 close (CONFIG);
2684 find(\&cleanup_and_extract, keys( %repo_dirs ));
2685 &main::strip_packages_list_statements();
2686 unshift @packages_list_statements, "VACUUM";
2687 $packages_list_db->exec_statementlist(\@packages_list_statements);
2688 unlink($packages_list_under_construction);
2689 daemon_log("$session_id INFO: create_packages_list_db: finished", 5);
2690 return;
2691 }
2693 # This function should do some intensive task to minimize the db-traffic
2694 sub strip_packages_list_statements {
2695 my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2696 my @new_statement_list=();
2697 my $hash;
2698 my $insert_hash;
2699 my $update_hash;
2700 my $delete_hash;
2701 my $local_timestamp=get_time();
2703 foreach my $existing_entry (@existing_entries) {
2704 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
2705 }
2707 foreach my $statement (@packages_list_statements) {
2708 if($statement =~ /^INSERT/i) {
2709 # Assign the values from the insert statement
2710 my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~
2711 /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
2712 if(exists($hash->{$distribution}->{$package}->{$version})) {
2713 # If section or description has changed, update the DB
2714 if(
2715 (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or
2716 (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
2717 ) {
2718 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
2719 }
2720 } else {
2721 # Insert a non-existing entry to db
2722 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2723 }
2724 } elsif ($statement =~ /^UPDATE/i) {
2725 my ($template,$package,$version) = ($1,$2,$3) if $statement =~
2726 /^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;
2727 foreach my $distribution (keys %{$hash}) {
2728 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
2729 # update the insertion hash to execute only one query per package (insert instead insert+update)
2730 @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
2731 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
2732 if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
2733 my $section;
2734 my $description;
2735 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
2736 length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
2737 $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
2738 }
2739 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2740 $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
2741 }
2742 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2743 }
2744 }
2745 }
2746 }
2747 }
2749 # TODO: Check for orphaned entries
2751 # unroll the insert_hash
2752 foreach my $distribution (keys %{$insert_hash}) {
2753 foreach my $package (keys %{$insert_hash->{$distribution}}) {
2754 foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
2755 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
2756 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
2757 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
2758 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
2759 ."'$local_timestamp')";
2760 }
2761 }
2762 }
2764 # unroll the update hash
2765 foreach my $distribution (keys %{$update_hash}) {
2766 foreach my $package (keys %{$update_hash->{$distribution}}) {
2767 foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
2768 my $set = "";
2769 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
2770 $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
2771 }
2772 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2773 $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
2774 }
2775 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
2776 $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
2777 }
2778 if(defined($set) and length($set) > 0) {
2779 $set .= "timestamp = '$local_timestamp'";
2780 } else {
2781 next;
2782 }
2783 push @new_statement_list,
2784 "UPDATE $main::packages_list_tn SET $set WHERE"
2785 ." distribution = '$distribution'"
2786 ." AND package = '$package'"
2787 ." AND version = '$version'";
2788 }
2789 }
2790 }
2792 @packages_list_statements = @new_statement_list;
2793 }
2796 sub parse_package_info {
2797 my ($baseurl, $dist, $section, $session_id)= @_;
2798 my ($package);
2799 if (not defined $session_id) { $session_id = 0; }
2800 my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
2801 $repo_dirs{ "${repo_path}/pool" } = 1;
2803 foreach $package ("Packages.gz"){
2804 daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
2805 get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
2806 parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
2807 }
2809 }
2812 sub get_package {
2813 my ($url, $dest, $session_id)= @_;
2814 if (not defined $session_id) { $session_id = 0; }
2816 my $tpath = dirname($dest);
2817 -d "$tpath" || mkpath "$tpath";
2819 # This is ugly, but I've no time to take a look at "how it works in perl"
2820 if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
2821 system("gunzip -cd '$dest' > '$dest.in'");
2822 daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 5);
2823 unlink($dest);
2824 daemon_log("$session_id DEBUG: delete file '$dest'", 5);
2825 } else {
2826 daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' failed!", 1);
2827 }
2828 return 0;
2829 }
2832 sub parse_package {
2833 my ($path, $dist, $srv_path, $session_id)= @_;
2834 if (not defined $session_id) { $session_id = 0;}
2835 my ($package, $version, $section, $description);
2836 my $PACKAGES;
2837 my $timestamp = &get_time();
2839 if(not stat("$path.in")) {
2840 daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
2841 return;
2842 }
2844 open($PACKAGES, "<$path.in");
2845 if(not defined($PACKAGES)) {
2846 daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1);
2847 return;
2848 }
2850 # Read lines
2851 while (<$PACKAGES>){
2852 my $line = $_;
2853 # Unify
2854 chop($line);
2856 # Use empty lines as a trigger
2857 if ($line =~ /^\s*$/){
2858 my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
2859 push(@packages_list_statements, $sql);
2860 $package = "none";
2861 $version = "none";
2862 $section = "none";
2863 $description = "none";
2864 next;
2865 }
2867 # Trigger for package name
2868 if ($line =~ /^Package:\s/){
2869 ($package)= ($line =~ /^Package: (.*)$/);
2870 next;
2871 }
2873 # Trigger for version
2874 if ($line =~ /^Version:\s/){
2875 ($version)= ($line =~ /^Version: (.*)$/);
2876 next;
2877 }
2879 # Trigger for description
2880 if ($line =~ /^Description:\s/){
2881 ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
2882 next;
2883 }
2885 # Trigger for section
2886 if ($line =~ /^Section:\s/){
2887 ($section)= ($line =~ /^Section: (.*)$/);
2888 next;
2889 }
2891 # Trigger for filename
2892 if ($line =~ /^Filename:\s/){
2893 my ($filename) = ($line =~ /^Filename: (.*)$/);
2894 store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
2895 next;
2896 }
2897 }
2899 close( $PACKAGES );
2900 unlink( "$path.in" );
2901 }
2904 sub store_fileinfo {
2905 my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
2907 my %fileinfo = (
2908 'package' => $package,
2909 'dist' => $dist,
2910 'version' => $vers,
2911 );
2913 $repo_files{ "${srvdir}/$file" } = \%fileinfo;
2914 }
2917 sub cleanup_and_extract {
2918 my $fileinfo = $repo_files{ $File::Find::name };
2920 if( defined $fileinfo ) {
2922 my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
2923 my $sql;
2924 my $package = $fileinfo->{ 'package' };
2925 my $newver = $fileinfo->{ 'version' };
2927 mkpath($dir);
2928 system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
2930 if( -f "$dir/DEBIAN/templates" ) {
2932 daemon_log("DEBUG: Found debconf templates in '$package' - $newver", 7);
2934 my $tmpl= "";
2935 {
2936 local $/=undef;
2937 open FILE, "$dir/DEBIAN/templates";
2938 $tmpl = &encode_base64(<FILE>);
2939 close FILE;
2940 }
2941 rmtree("$dir/DEBIAN/templates");
2943 $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
2944 push @packages_list_statements, $sql;
2945 }
2946 }
2948 return;
2949 }
2952 sub register_at_foreign_servers {
2953 my ($kernel) = $_[KERNEL];
2955 # hole alle bekannten server aus known_server_db
2956 my $server_sql = "SELECT * FROM $known_server_tn";
2957 my $server_res = $known_server_db->exec_statement($server_sql);
2959 # no entries in known_server_db
2960 if (not ref(@$server_res[0]) eq "ARRAY") {
2961 # TODO
2962 }
2964 # detect already connected clients
2965 my $client_sql = "SELECT * FROM $known_clients_tn";
2966 my $client_res = $known_clients_db->exec_statement($client_sql);
2968 # send my server details to all other gosa-si-server within the network
2969 foreach my $hit (@$server_res) {
2970 my $hostname = @$hit[0];
2971 my $hostkey = &create_passwd;
2973 # add already connected clients to registration message
2974 my $myhash = &create_xml_hash('new_server', $server_address, $hostname);
2975 &add_content2xml_hash($myhash, 'key', $hostkey);
2976 map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res);
2978 # build registration message and send it
2979 my $foreign_server_msg = &create_xml_string($myhash);
2980 my $error = &send_msg_to_target($foreign_server_msg, $hostname, $ServerPackages_key, "new_server", 0);
2981 }
2983 $kernel->delay_set("register_at_foreign_servers", $foreign_servers_register_delay);
2984 return;
2985 }
2988 #==== MAIN = main ==============================================================
2989 # parse commandline options
2990 Getopt::Long::Configure( "bundling" );
2991 GetOptions("h|help" => \&usage,
2992 "c|config=s" => \$cfg_file,
2993 "f|foreground" => \$foreground,
2994 "v|verbose+" => \$verbose,
2995 "no-arp+" => \$no_arp,
2996 );
2998 # read and set config parameters
2999 &check_cmdline_param ;
3000 &read_configfile($cfg_file, %cfg_defaults);
3001 &check_pid;
3003 $SIG{CHLD} = 'IGNORE';
3005 # forward error messages to logfile
3006 if( ! $foreground ) {
3007 open( STDIN, '+>/dev/null' );
3008 open( STDOUT, '+>&STDIN' );
3009 open( STDERR, '+>&STDIN' );
3010 }
3012 # Just fork, if we are not in foreground mode
3013 if( ! $foreground ) {
3014 chdir '/' or die "Can't chdir to /: $!";
3015 $pid = fork;
3016 setsid or die "Can't start a new session: $!";
3017 umask 0;
3018 } else {
3019 $pid = $$;
3020 }
3022 # Do something useful - put our PID into the pid_file
3023 if( 0 != $pid ) {
3024 open( LOCK_FILE, ">$pid_file" );
3025 print LOCK_FILE "$pid\n";
3026 close( LOCK_FILE );
3027 if( !$foreground ) {
3028 exit( 0 )
3029 };
3030 }
3032 # parse head url and revision from svn
3033 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
3034 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
3035 $server_headURL = defined $1 ? $1 : 'unknown' ;
3036 $server_revision = defined $2 ? $2 : 'unknown' ;
3037 if ($server_headURL =~ /\/tag\// ||
3038 $server_headURL =~ /\/branches\// ) {
3039 $server_status = "stable";
3040 } else {
3041 $server_status = "developmental" ;
3042 }
3045 daemon_log(" ", 1);
3046 daemon_log("$0 started!", 1);
3047 daemon_log("status: $server_status", 1);
3048 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1);
3050 # connect to incoming_db
3051 unlink($incoming_file_name);
3052 $incoming_db = GOSA::DBsqlite->new($incoming_file_name);
3053 $incoming_db->create_table($incoming_tn, \@incoming_col_names);
3055 # connect to gosa-si job queue
3056 $job_db = GOSA::DBsqlite->new($job_queue_file_name);
3057 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
3059 # connect to known_clients_db
3060 $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
3061 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
3063 # connect to foreign_clients_db
3064 $foreign_clients_db = GOSA::DBsqlite->new($foreign_clients_file_name);
3065 $foreign_clients_db->create_table($foreign_clients_tn, \@foreign_clients_col_names);
3067 # connect to known_server_db
3068 unlink($known_server_file_name);
3069 $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
3070 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
3072 # connect to login_usr_db
3073 $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
3074 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
3076 # connect to fai_server_db and fai_release_db
3077 unlink($fai_server_file_name);
3078 $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
3079 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
3081 unlink($fai_release_file_name);
3082 $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
3083 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
3085 # connect to packages_list_db
3086 #unlink($packages_list_file_name);
3087 unlink($packages_list_under_construction);
3088 $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
3089 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
3091 # connect to messaging_db
3092 $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
3093 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
3096 # create xml object used for en/decrypting
3097 $xml = new XML::Simple();
3100 # foreign servers
3101 my @foreign_server_list;
3103 # add foreign server from cfg file
3104 if ($foreign_server_string ne "") {
3105 my @cfg_foreign_server_list = split(",", $foreign_server_string);
3106 foreach my $foreign_server (@cfg_foreign_server_list) {
3107 push(@foreign_server_list, $foreign_server);
3108 }
3109 }
3111 # add foreign server from dns
3112 my @tmp_servers;
3113 if ( !$server_domain) {
3114 # Try our DNS Searchlist
3115 for my $domain(get_dns_domains()) {
3116 chomp($domain);
3117 my @tmp_domains= &get_server_addresses($domain);
3118 if(@tmp_domains) {
3119 for my $tmp_server(@tmp_domains) {
3120 push @tmp_servers, $tmp_server;
3121 }
3122 }
3123 }
3124 if(@tmp_servers && length(@tmp_servers)==0) {
3125 daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3126 }
3127 } else {
3128 @tmp_servers = &get_server_addresses($server_domain);
3129 if( 0 == @tmp_servers ) {
3130 daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3131 }
3132 }
3133 foreach my $server (@tmp_servers) {
3134 unshift(@foreign_server_list, $server);
3135 }
3136 # eliminate duplicate entries
3137 @foreign_server_list = &del_doubles(@foreign_server_list);
3138 my $all_foreign_server = join(", ", @foreign_server_list);
3139 daemon_log("0 INFO: found foreign server in config file and DNS: $all_foreign_server", 5);
3141 # add all found foreign servers to known_server
3142 my $act_timestamp = &get_time();
3143 foreach my $foreign_server (@foreign_server_list) {
3145 # do not add myself to known_server_db
3146 if (&is_local($foreign_server)) { next; }
3147 ######################################
3149 my $res = $known_server_db->add_dbentry( {table=>$known_server_tn,
3150 primkey=>['hostname'],
3151 hostname=>$foreign_server,
3152 status=>'not_jet_registered',
3153 hostkey=>"none",
3154 timestamp=>$act_timestamp,
3155 } );
3156 }
3159 # Import all modules
3160 &import_modules;
3162 # Check wether all modules are gosa-si valid passwd check
3163 &password_check;
3165 # Prepare for using Opsi
3166 if ($opsi_enabled eq "true") {
3167 use JSON::RPC::Client;
3168 use XML::Quote qw(:all);
3169 $opsi_url= "https://".$opsi_admin.":".$opsi_password."@".$opsi_server.":4447/rpc";
3170 $opsi_client = new JSON::RPC::Client;
3171 }
3174 POE::Component::Server::TCP->new(
3175 Alias => "TCP_SERVER",
3176 Port => $server_port,
3177 ClientInput => sub {
3178 my ($kernel, $input) = @_[KERNEL, ARG0];
3179 push(@tasks, $input);
3180 push(@msgs_to_decrypt, $input);
3181 $kernel->yield("msg_to_decrypt");
3182 },
3183 InlineStates => {
3184 msg_to_decrypt => \&msg_to_decrypt,
3185 next_task => \&next_task,
3186 task_result => \&handle_task_result,
3187 task_done => \&handle_task_done,
3188 task_debug => \&handle_task_debug,
3189 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!" },
3190 }
3191 );
3193 daemon_log("start socket for incoming xml messages at port '$server_port' ", 1);
3195 # create session for repeatedly checking the job queue for jobs
3196 POE::Session->create(
3197 inline_states => {
3198 _start => \&session_start,
3199 register_at_foreign_servers => \®ister_at_foreign_servers,
3200 sig_handler => \&sig_handler,
3201 next_task => \&next_task,
3202 task_result => \&handle_task_result,
3203 task_done => \&handle_task_done,
3204 task_debug => \&handle_task_debug,
3205 watch_for_next_tasks => \&watch_for_next_tasks,
3206 watch_for_new_messages => \&watch_for_new_messages,
3207 watch_for_delivery_messages => \&watch_for_delivery_messages,
3208 watch_for_done_messages => \&watch_for_done_messages,
3209 watch_for_new_jobs => \&watch_for_new_jobs,
3210 watch_for_modified_jobs => \&watch_for_modified_jobs,
3211 watch_for_done_jobs => \&watch_for_done_jobs,
3212 watch_for_opsi_jobs => \&watch_for_opsi_jobs,
3213 watch_for_old_known_clients => \&watch_for_old_known_clients,
3214 create_packages_list_db => \&run_create_packages_list_db,
3215 create_fai_server_db => \&run_create_fai_server_db,
3216 create_fai_release_db => \&run_create_fai_release_db,
3217 recreate_packages_db => \&run_recreate_packages_db,
3218 session_run_result => \&session_run_result,
3219 session_run_debug => \&session_run_debug,
3220 session_run_done => \&session_run_done,
3221 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!" },
3222 }
3223 );
3226 POE::Kernel->run();
3227 exit;