e2ca1c36d8d6a3095c271b594a65c2c8248ee83d
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 ($procid, $pid);
75 my ($arp_fifo);
76 my ($xml);
77 my $sources_list;
78 my $max_clients;
79 my %repo_files=();
80 my $repo_path;
81 my %repo_dirs=();
82 # variables declared in config file are always set to 'our'
83 our (%cfg_defaults, $log_file, $pid_file,
84 $server_ip, $server_port, $ClientPackages_key,
85 $arp_activ, $gosa_unit_tag,
86 $GosaPackages_key, $gosa_timeout,
87 $foreign_server_string, $server_domain, $ServerPackages_key, $foreign_servers_register_delay,
88 $wake_on_lan_passwd, $job_synchronization, $modified_jobs_loop_delay,
89 $arp_enabled, $arp_interface,
90 $opsi_enabled, $opsi_server, $opsi_admin, $opsi_password,
91 );
93 # additional variable which should be globaly accessable
94 our $server_address;
95 our $server_mac_address;
96 our $gosa_address;
97 our $no_arp;
98 our $verbose;
99 our $forground;
100 our $cfg_file;
101 our ($ldap_uri, $ldap_base, $ldap_admin_dn, $ldap_admin_password, $ldap_server_dn);
102 our $known_modules;
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", "macaddress", "status", "hostkey", "loaded_modules", "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, $event_hash) = @{$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";
839 #
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');
845 #
846 # open(PROC_NET_ROUTE, "<$PROC_NET_ROUTE")
847 # or die "Could not open $PROC_NET_ROUTE";
848 #
849 # my @ifs = <PROC_NET_ROUTE>;
850 #
851 # close(PROC_NET_ROUTE);
852 #
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 $local_mac;
1061 my ($target_ip, $target_port) = split(':', $target);
1063 # Determine the local ip address if target is an ip address
1064 if ($target =~ /^\d+\.\d+\.\d+\.\d+:\d+$/) {
1065 $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1066 } else {
1067 $local_address = $server_address;
1068 }
1070 # Determine the local mac address if target is a mac address
1071 if ($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) {
1072 my $loc_ip = &get_local_ip_for_remote_ip($heap->{'remote_ip'});
1073 my $network_interface= &get_interface_for_ip($loc_ip);
1074 $local_mac = &get_mac_for_interface($network_interface);
1075 } else {
1076 $local_mac = $server_mac_address;
1077 }
1079 # target and source is equal to GOSA -> process here
1080 if (not $done) {
1081 if ($target eq "GOSA" && $source eq "GOSA") {
1082 $done = 1;
1083 }
1084 }
1086 # target is own address without forward_to_gosa-tag -> process here
1087 if (not $done) {
1088 if ((($target eq $local_address) || ($target eq $local_mac) ) && (not exists $msg_hash->{'forward_to_gosa'})) {
1089 $done = 1;
1090 if ($source eq "GOSA") {
1091 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1092 }
1093 #print STDERR "target is own address without forward_to_gosa-tag -> process here\n";
1094 }
1095 }
1097 # target is a client address in known_clients -> process here
1098 if (not $done) {
1099 $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1100 $res = $known_clients_db->select_dbentry($sql);
1101 if (keys(%$res) > 0) {
1102 $done = 1;
1103 my $hostname = $res->{1}->{'hostname'};
1104 $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1105 #print STDERR "target is a client address in known_clients -> process here\n";
1106 my $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1107 if ($source eq "GOSA") {
1108 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1109 }
1111 } else {
1112 $not_found_in_known_clients_db = 1;
1113 }
1114 }
1116 # target ist own address with forward_to_gosa-tag not pointing to myself -> process here
1117 if (not $done) {
1118 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1119 my $gosa_at;
1120 my $gosa_session_id;
1121 if ((($target eq $local_address) || ($target eq $local_mac) ) && (defined $forward_to_gosa)){
1122 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1123 if ($gosa_at ne $local_address) {
1124 $done = 1;
1125 #print STDERR "target is own address with forward_to_gosa-tag not pointing to myself -> process here\n";
1126 }
1127 }
1128 }
1130 # if message should be processed here -> add message to incoming_db
1131 if ($done) {
1132 # if a job or a gosa message comes from a foreign server, fake module to GosaPackages
1133 # so gosa-si-server knows how to process this kind of messages
1134 if ($header =~ /^gosa_/ || $header =~ /^job_/) {
1135 $module = "GosaPackages";
1136 }
1138 my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1139 primkey=>[],
1140 headertag=>$header,
1141 targettag=>$target,
1142 xmlmessage=>&encode_base64($msg),
1143 timestamp=>&get_time,
1144 module=>$module,
1145 sessionid=>$session_id,
1146 } );
1147 }
1149 # target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa
1150 if (not $done) {
1151 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1152 my $gosa_at;
1153 my $gosa_session_id;
1154 if ((($target eq $local_address) || ($target eq $local_mac) ) && (defined $forward_to_gosa)){
1155 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1156 if ($gosa_at eq $local_address) {
1157 my $session_reference = $kernel->ID_id_to_session($gosa_session_id);
1158 if( defined $session_reference ) {
1159 $heap = $session_reference->get_heap();
1160 }
1161 if(exists $heap->{'client'}) {
1162 $msg = &encrypt_msg($msg, $GosaPackages_key);
1163 $heap->{'client'}->put($msg);
1164 &daemon_log("$session_id INFO: incoming '$header' message forwarded to GOsa", 5);
1165 }
1166 $done = 1;
1167 #print STDERR "target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa\n";
1168 }
1169 }
1171 }
1173 # target is a client address in foreign_clients -> forward to registration server
1174 if (not $done) {
1175 $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1176 $res = $foreign_clients_db->select_dbentry($sql);
1177 if (keys(%$res) > 0) {
1178 my $hostname = $res->{1}->{'hostname'};
1179 my ($host_ip, $host_port) = split(/:/, $hostname);
1180 my $local_address = &get_local_ip_for_remote_ip($host_ip).":$server_port";
1181 my $regserver = $res->{1}->{'regserver'};
1182 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$regserver'";
1183 my $res = $known_server_db->select_dbentry($sql);
1184 if (keys(%$res) > 0) {
1185 my $regserver_key = $res->{1}->{'hostkey'};
1186 $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1187 $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1188 if ($source eq "GOSA") {
1189 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1190 }
1191 &send_msg_to_target($msg, $regserver, $regserver_key, $header, $session_id);
1192 }
1193 $done = 1;
1194 #print STDERR "target is a client address in foreign_clients -> forward to registration server\n";
1195 } else {
1196 $not_found_in_foreign_clients_db = 1;
1197 }
1198 }
1200 # target is a server address -> forward to server
1201 if (not $done) {
1202 $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1203 $res = $known_server_db->select_dbentry($sql);
1204 if (keys(%$res) > 0) {
1205 my $hostkey = $res->{1}->{'hostkey'};
1207 if ($source eq "GOSA") {
1208 $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1209 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1211 }
1213 &send_msg_to_target($msg, $target, $hostkey, $header, $session_id);
1214 $done = 1;
1215 } else {
1216 $not_found_in_known_server_db = 1;
1217 }
1218 }
1221 # target is not in foreign_clients_db, known_server_db or known_clients_db, maybe it is a complete new one -> process here
1222 if ( $not_found_in_foreign_clients_db
1223 && $not_found_in_known_server_db
1224 && $not_found_in_known_clients_db) {
1225 my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1226 primkey=>[],
1227 headertag=>$header,
1228 targettag=>$target,
1229 xmlmessage=>&encode_base64($msg),
1230 timestamp=>&get_time,
1231 module=>$module,
1232 sessionid=>$session_id,
1233 } );
1234 $done = 1;
1235 }
1238 if (not $done) {
1239 daemon_log("$session_id ERROR: do not know what to do with this message: $msg", 1);
1240 if ($source eq "GOSA") {
1241 my %data = ('error_msg' => &encode_base64($msg), 'error_string' => "Do not know what to do with this message!");
1242 my $error_msg = &build_msg("error", $local_address, "GOSA", \%data );
1244 my $session_reference = $kernel->ID_id_to_session($session_id);
1245 if( defined $session_reference ) {
1246 $heap = $session_reference->get_heap();
1247 }
1248 if(exists $heap->{'client'}) {
1249 $error_msg = &encrypt_msg($error_msg, $GosaPackages_key);
1250 $heap->{'client'}->put($error_msg);
1251 }
1252 }
1253 }
1255 }
1257 return;
1258 }
1261 sub next_task {
1262 my ($session, $heap, $task) = @_[SESSION, HEAP, ARG0];
1263 my $running_task = POE::Wheel::Run->new(
1264 Program => sub { process_task($session, $heap, $task) },
1265 StdioFilter => POE::Filter::Reference->new(),
1266 StdoutEvent => "task_result",
1267 StderrEvent => "task_debug",
1268 CloseEvent => "task_done",
1269 );
1270 $heap->{task}->{ $running_task->ID } = $running_task;
1271 }
1273 sub handle_task_result {
1274 my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
1275 my $client_answer = $result->{'answer'};
1276 if( $client_answer =~ s/session_id=(\d+)$// ) {
1277 my $session_id = $1;
1278 if( defined $session_id ) {
1279 my $session_reference = $kernel->ID_id_to_session($session_id);
1280 if( defined $session_reference ) {
1281 $heap = $session_reference->get_heap();
1282 }
1283 }
1285 if(exists $heap->{'client'}) {
1286 $heap->{'client'}->put($client_answer);
1287 }
1288 }
1289 $kernel->sig(CHLD => "child_reap");
1290 }
1292 sub handle_task_debug {
1293 my $result = $_[ARG0];
1294 print STDERR "$result\n";
1295 }
1297 sub handle_task_done {
1298 my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1299 delete $heap->{task}->{$task_id};
1300 }
1302 sub process_task {
1303 no strict "refs";
1304 #CHECK: Not @_[...]?
1305 my ($session, $heap, $task) = @_;
1306 my $error = 0;
1307 my $answer_l;
1308 my ($answer_header, @answer_target_l, $answer_source);
1309 my $client_answer = "";
1311 # prepare all variables needed to process message
1312 #my $msg = $task->{'xmlmessage'};
1313 my $msg = &decode_base64($task->{'xmlmessage'});
1314 my $incoming_id = $task->{'id'};
1315 my $module = $task->{'module'};
1316 my $header = $task->{'headertag'};
1317 my $session_id = $task->{'sessionid'};
1318 my $msg_hash = $xml->XMLin($msg, ForceArray=>1);
1319 my $source = @{$msg_hash->{'source'}}[0];
1321 # set timestamp of incoming client uptodate, so client will not
1322 # be deleted from known_clients because of expiration
1323 my $act_time = &get_time();
1324 my $sql = "UPDATE $known_clients_tn SET timestamp='$act_time' WHERE hostname='$source'";
1325 my $res = $known_clients_db->exec_statement($sql);
1327 ######################
1328 # process incoming msg
1329 if( $error == 0) {
1330 daemon_log("$session_id INFO: Incoming msg (session_id=$session_id) with header '".@{$msg_hash->{'header'}}[0]."'", 5);
1331 daemon_log("$session_id DEBUG: Processing module ".$module, 7);
1332 $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1334 if ( 0 < @{$answer_l} ) {
1335 my $answer_str = join("\n", @{$answer_l});
1336 while ($answer_str =~ /<header>(\w+)<\/header>/g) {
1337 daemon_log("$session_id INFO: got answer message with header '$1'", 5);
1338 }
1339 daemon_log("$session_id DEBUG: $module: got answer from module: \n".$answer_str,9);
1340 } else {
1341 daemon_log("$session_id DEBUG: $module: got no answer from module!" ,7);
1342 }
1344 }
1345 if( !$answer_l ) { $error++ };
1347 ########
1348 # answer
1349 if( $error == 0 ) {
1351 foreach my $answer ( @{$answer_l} ) {
1352 # check outgoing msg to xml validity
1353 my $answer_hash = &check_outgoing_xml_validity($answer, $session_id);
1354 if( not defined $answer_hash ) { next; }
1356 $answer_header = @{$answer_hash->{'header'}}[0];
1357 @answer_target_l = @{$answer_hash->{'target'}};
1358 $answer_source = @{$answer_hash->{'source'}}[0];
1360 # deliver msg to all targets
1361 foreach my $answer_target ( @answer_target_l ) {
1363 # targets of msg are all gosa-si-clients in known_clients_db
1364 if( $answer_target eq "*" ) {
1365 # answer is for all clients
1366 my $sql_statement= "SELECT * FROM known_clients";
1367 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1368 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1369 my $host_name = $hit->{hostname};
1370 my $host_key = $hit->{hostkey};
1371 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1372 &update_jobdb_status_for_send_msgs($answer, $error);
1373 }
1374 }
1376 # targets of msg are all gosa-si-server in known_server_db
1377 elsif( $answer_target eq "KNOWN_SERVER" ) {
1378 # answer is for all server in known_server
1379 my $sql_statement= "SELECT * FROM $known_server_tn";
1380 my $query_res = $known_server_db->select_dbentry( $sql_statement );
1381 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1382 my $host_name = $hit->{hostname};
1383 my $host_key = $hit->{hostkey};
1384 $answer =~ s/<target>\S+<\/target>/<target>$host_name<\/target>/g;
1385 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1386 &update_jobdb_status_for_send_msgs($answer, $error);
1387 }
1388 }
1390 # target of msg is GOsa
1391 elsif( $answer_target eq "GOSA" ) {
1392 my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1393 my $add_on = "";
1394 if( defined $session_id ) {
1395 $add_on = ".session_id=$session_id";
1396 }
1397 # answer is for GOSA and has to returned to connected client
1398 my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1399 $client_answer = $gosa_answer.$add_on;
1400 }
1402 # target of msg is job queue at this host
1403 elsif( $answer_target eq "JOBDB") {
1404 $answer =~ /<header>(\S+)<\/header>/;
1405 my $header;
1406 if( defined $1 ) { $header = $1; }
1407 my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1408 &update_jobdb_status_for_send_msgs($answer, $error);
1409 }
1411 # Target of msg is a mac address
1412 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 ) {
1413 daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients and foreign_clients", 5);
1414 my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1415 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1416 my $found_ip_flag = 0;
1417 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1418 my $host_name = $hit->{hostname};
1419 my $host_key = $hit->{hostkey};
1420 $answer =~ s/$answer_target/$host_name/g;
1421 daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1422 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1423 &update_jobdb_status_for_send_msgs($answer, $error);
1424 $found_ip_flag++ ;
1425 }
1426 if ($found_ip_flag == 0) {
1427 my $sql = "SELECT * FROM $foreign_clients_tn WHERE macaddress LIKE '$answer_target'";
1428 my $res = $foreign_clients_db->select_dbentry($sql);
1429 while( my ($hit_num, $hit) = each %{ $res } ) {
1430 my $host_name = $hit->{hostname};
1431 my $reg_server = $hit->{regserver};
1432 daemon_log("$session_id INFO: found host '$host_name' with mac '$answer_target', registered at '$reg_server'", 5);
1434 # Fetch key for reg_server
1435 my $reg_server_key;
1436 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$reg_server'";
1437 my $res = $known_server_db->select_dbentry($sql);
1438 if (exists $res->{1}) {
1439 $reg_server_key = $res->{1}->{'hostkey'};
1440 } else {
1441 daemon_log("$session_id ERROR: cannot find hostkey for '$host_name' in '$known_server_tn'", 1);
1442 daemon_log("$session_id ERROR: unable to forward answer to correct registration server, processing is aborted!", 1);
1443 $reg_server_key = undef;
1444 }
1446 # Send answer to server where client is registered
1447 if (defined $reg_server_key) {
1448 $answer =~ s/$answer_target/$host_name/g;
1449 my $error = &send_msg_to_target($answer, $reg_server, $reg_server_key, $answer_header, $session_id);
1450 &update_jobdb_status_for_send_msgs($answer, $error);
1451 $found_ip_flag++ ;
1452 }
1453 }
1454 }
1455 if( $found_ip_flag == 0) {
1456 daemon_log("$session_id WARNING: no host found in known_clients with mac address '$answer_target'", 3);
1457 }
1459 # Answer is for one specific host
1460 } else {
1461 # get encrypt_key
1462 my $encrypt_key = &get_encrypt_key($answer_target);
1463 if( not defined $encrypt_key ) {
1464 # unknown target
1465 daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1466 next;
1467 }
1468 my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1469 &update_jobdb_status_for_send_msgs($answer, $error);
1470 }
1471 }
1472 }
1473 }
1475 my $filter = POE::Filter::Reference->new();
1476 my %result = (
1477 status => "seems ok to me",
1478 answer => $client_answer,
1479 );
1481 my $output = $filter->put( [ \%result ] );
1482 print @$output;
1485 }
1487 sub session_start {
1488 my ($kernel) = $_[KERNEL];
1489 $global_kernel = $kernel;
1490 $kernel->yield('register_at_foreign_servers');
1491 $kernel->yield('create_fai_server_db', $fai_server_tn );
1492 $kernel->yield('create_fai_release_db', $fai_release_tn );
1493 $kernel->yield('watch_for_next_tasks');
1494 $kernel->sig(USR1 => "sig_handler");
1495 $kernel->sig(USR2 => "recreate_packages_db");
1496 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1497 $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay);
1498 $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay);
1499 $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1500 $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1501 $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1502 $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1504 # Start opsi check
1505 if ($opsi_enabled eq "true") {
1506 $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay);
1507 }
1509 }
1512 sub watch_for_done_jobs {
1513 #CHECK: $heap for what?
1514 my ($kernel,$heap) = @_[KERNEL, HEAP];
1516 my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((status='done') AND (modified='0'))";
1517 my $res = $job_db->select_dbentry( $sql_statement );
1519 while( my ($id, $hit) = each %{$res} ) {
1520 my $jobdb_id = $hit->{id};
1521 my $sql_statement = "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1522 my $res = $job_db->del_dbentry($sql_statement);
1523 }
1525 $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1526 }
1529 sub watch_for_opsi_jobs {
1530 my ($kernel) = $_[KERNEL];
1532 # 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
1533 # opsi install job is to parse the xml message. There is still the correct header.
1534 my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((xmlmessage LIKE '%opsi_install_client</header>%') AND (status='processing') AND (siserver='localhost'))";
1535 my $res = $job_db->select_dbentry( $sql_statement );
1537 # Ask OPSI for an update of the running jobs
1538 while (my ($id, $hit) = each %$res ) {
1539 # Determine current parameters of the job
1540 my $hostId = $hit->{'plainname'};
1541 my $macaddress = $hit->{'macaddress'};
1542 my $progress = $hit->{'progress'};
1544 my $result= {};
1546 # For hosts, only return the products that are or get installed
1547 my $callobj;
1548 $callobj = {
1549 method => 'getProductStates_hash',
1550 params => [ $hostId ],
1551 id => 1,
1552 };
1554 my $hres = $opsi_client->call($opsi_url, $callobj);
1555 #my ($hres_err, $hres_err_string) = &check_opsi_res($hres);
1556 if (not &check_opsi_res($hres)) {
1557 my $htmp= $hres->result->{$hostId};
1559 # Check state != not_installed or action == setup -> load and add
1560 my $products= 0;
1561 my $installed= 0;
1562 my $installing = 0;
1563 my $error= 0;
1564 my @installed_list;
1565 my @error_list;
1566 my $act_status = "none";
1567 foreach my $product (@{$htmp}){
1569 if ($product->{'installationStatus'} ne "not_installed" or
1570 $product->{'actionRequest'} eq "setup"){
1572 # Increase number of products for this host
1573 $products++;
1575 if ($product->{'installationStatus'} eq "failed"){
1576 $result->{$product->{'productId'}}= "error";
1577 unshift(@error_list, $product->{'productId'});
1578 $error++;
1579 }
1580 if ($product->{'installationStatus'} eq "installed" && $product->{'actionRequest'} eq "none"){
1581 $result->{$product->{'productId'}}= "installed";
1582 unshift(@installed_list, $product->{'productId'});
1583 $installed++;
1584 }
1585 if ($product->{'installationStatus'} eq "installing"){
1586 $result->{$product->{'productId'}}= "installing";
1587 $installing++;
1588 $act_status = "installing - ".$product->{'productId'};
1589 }
1590 }
1591 }
1593 # Estimate "rough" progress, avoid division by zero
1594 if ($products == 0) {
1595 $result->{'progress'}= 0;
1596 } else {
1597 $result->{'progress'}= int($installed * 100 / $products);
1598 }
1600 # Set updates in job queue
1601 if ((not $error) && (not $installing) && ($installed)) {
1602 $act_status = "installed - ".join(", ", @installed_list);
1603 }
1604 if ($error) {
1605 $act_status = "error - ".join(", ", @error_list);
1606 }
1607 if ($progress ne $result->{'progress'} ) {
1608 # Updating progress and result
1609 my $update_statement = "UPDATE $job_queue_tn SET modified='1', progress='".$result->{'progress'}."', result='$act_status' WHERE macaddress='$macaddress' AND siserver='localhost'";
1610 my $update_res = $job_db->update_dbentry($update_statement);
1611 }
1612 if ($progress eq 100) {
1613 # Updateing status
1614 my $done_statement = "UPDATE $job_queue_tn SET modified='1', ";
1615 if ($error) {
1616 $done_statement .= "status='error'";
1617 } else {
1618 $done_statement .= "status='done'";
1619 }
1620 $done_statement .= " WHERE macaddress='$macaddress' AND siserver='localhost'";
1621 my $done_res = $job_db->update_dbentry($done_statement);
1622 }
1625 }
1626 }
1628 $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay);
1629 }
1632 # If a job got an update or was modified anyway, send to all other si-server an update message of this jobs.
1633 sub watch_for_modified_jobs {
1634 my ($kernel,$heap) = @_[KERNEL, HEAP];
1636 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE ((siserver='localhost') AND (modified='1'))";
1637 my $res = $job_db->select_dbentry( $sql_statement );
1639 # if db contains no jobs which should be update, do nothing
1640 if (keys %$res != 0) {
1642 if ($job_synchronization eq "true") {
1643 # make out of the db result a gosa-si message
1644 my $update_msg = &db_res2si_msg ($res, "foreign_job_updates", "KNOWN_SERVER", "MY_LOCAL_ADDRESS");
1646 # update all other SI-server
1647 &inform_all_other_si_server($update_msg);
1648 }
1650 # set jobs all jobs to modified = 0, wait until the next modification for updates of other si-server
1651 $sql_statement = "UPDATE $job_queue_tn SET modified='0' ";
1652 $res = $job_db->update_dbentry($sql_statement);
1653 }
1655 $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay);
1656 }
1659 sub watch_for_new_jobs {
1660 if($watch_for_new_jobs_in_progress == 0) {
1661 $watch_for_new_jobs_in_progress = 1;
1662 my ($kernel,$heap) = @_[KERNEL, HEAP];
1664 # check gosa job quaeue for jobs with executable timestamp
1665 my $timestamp = &get_time();
1666 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE status='waiting' AND (CAST (timestamp AS INTEGER)) < $timestamp ORDER BY timestamp";
1667 my $res = $job_db->exec_statement( $sql_statement );
1669 # Merge all new jobs that would do the same actions
1670 my @drops;
1671 my $hits;
1672 foreach my $hit (reverse @{$res} ) {
1673 my $macaddress= lc @{$hit}[8];
1674 my $headertag= @{$hit}[5];
1675 if(
1676 defined($hits->{$macaddress}) &&
1677 defined($hits->{$macaddress}->{$headertag}) &&
1678 defined($hits->{$macaddress}->{$headertag}[0])
1679 ) {
1680 push @drops, "DELETE FROM $job_queue_tn WHERE id = $hits->{$macaddress}->{$headertag}[0]";
1681 }
1682 $hits->{$macaddress}->{$headertag}= $hit;
1683 }
1685 # Delete new jobs with a matching job in state 'processing'
1686 foreach my $macaddress (keys %{$hits}) {
1687 foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1688 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1689 if(defined($jobdb_id)) {
1690 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND headertag='$jobdb_headertag' AND status='processing'";
1691 my $res = $job_db->exec_statement( $sql_statement );
1692 foreach my $hit (@{$res}) {
1693 push @drops, "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1694 }
1695 } else {
1696 daemon_log("J ERROR: Job without id exists for macaddress $macaddress!", 1);
1697 }
1698 }
1699 }
1701 # Commit deletion
1702 $job_db->exec_statementlist(\@drops);
1704 # Look for new jobs that could be executed
1705 foreach my $macaddress (keys %{$hits}) {
1707 # Look if there is an executing job
1708 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='processing'";
1709 my $res = $job_db->exec_statement( $sql_statement );
1711 # Skip new jobs for host if there is a processing job
1712 if(defined($res) and defined @{$res}[0]) {
1713 next;
1714 }
1716 foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1717 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1718 if(defined($jobdb_id)) {
1719 my $job_msg = @{$hits->{$macaddress}->{$jobdb_headertag}}[7];
1721 daemon_log("J DEBUG: its time to execute $job_msg", 7);
1722 my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1723 my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1725 # expect macaddress is unique!!!!!!
1726 my $target = $res_hash->{1}->{hostname};
1728 # change header
1729 $job_msg =~ s/<header>job_/<header>gosa_/;
1731 # add sqlite_id
1732 $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1734 $job_msg =~ /<header>(\S+)<\/header>/;
1735 my $header = $1 ;
1736 my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");
1738 # update status in job queue to 'processing'
1739 $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
1740 my $res = $job_db->update_dbentry($sql_statement);
1741 # TODO: abfangen ob alles in ordnung ist oder nicht, wenn nicht error schmeißen
1743 # We don't want parallel processing
1744 last;
1745 }
1746 }
1747 }
1749 $watch_for_new_jobs_in_progress = 0;
1750 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1751 }
1752 }
1755 sub watch_for_new_messages {
1756 my ($kernel,$heap) = @_[KERNEL, HEAP];
1757 my @coll_user_msg; # collection list of outgoing messages
1759 # check messaging_db for new incoming messages with executable timestamp
1760 my $timestamp = &get_time();
1761 my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS INTEGER))<$timestamp AND flag='n' AND direction='in' )";
1762 my $res = $messaging_db->exec_statement( $sql_statement );
1763 foreach my $hit (@{$res}) {
1765 # create outgoing messages
1766 my $message_to = @{$hit}[3];
1767 # translate message_to to plain login name
1768 my @message_to_l = split(/,/, $message_to);
1769 my %receiver_h;
1770 foreach my $receiver (@message_to_l) {
1771 if ($receiver =~ /^u_([\s\S]*)$/) {
1772 $receiver_h{$1} = 0;
1773 } elsif ($receiver =~ /^g_([\s\S]*)$/) {
1774 my $group_name = $1;
1775 # fetch all group members from ldap and add them to receiver hash
1776 my $ldap_handle = &get_ldap_handle();
1777 if (defined $ldap_handle) {
1778 my $mesg = $ldap_handle->search(
1779 base => $ldap_base,
1780 scope => 'sub',
1781 attrs => ['memberUid'],
1782 filter => "cn=$group_name",
1783 );
1784 if ($mesg->count) {
1785 my @entries = $mesg->entries;
1786 foreach my $entry (@entries) {
1787 my @receivers= $entry->get_value("memberUid");
1788 foreach my $receiver (@receivers) {
1789 $receiver_h{$1} = 0;
1790 }
1791 }
1792 }
1793 # translating errors ?
1794 if ($mesg->code) {
1795 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: $mesg->error", 1);
1796 }
1797 # ldap handle error ?
1798 } else {
1799 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: no ldap handle available", 1);
1800 }
1801 } else {
1802 my $sbjct = &encode_base64(@{$hit}[1]);
1803 my $msg = &encode_base64(@{$hit}[7]);
1804 &daemon_log("M WARNING: unknown receiver '$receiver' for a user-message '$sbjct - $msg'", 3);
1805 }
1806 }
1807 my @receiver_l = keys(%receiver_h);
1809 my $message_id = @{$hit}[0];
1811 #add each outgoing msg to messaging_db
1812 my $receiver;
1813 foreach $receiver (@receiver_l) {
1814 my $sql_statement = "INSERT INTO $messaging_tn (id, subject, message_from, message_to, flag, direction, delivery_time, message, timestamp) ".
1815 "VALUES ('".
1816 $message_id."', '". # id
1817 @{$hit}[1]."', '". # subject
1818 @{$hit}[2]."', '". # message_from
1819 $receiver."', '". # message_to
1820 "none"."', '". # flag
1821 "out"."', '". # direction
1822 @{$hit}[6]."', '". # delivery_time
1823 @{$hit}[7]."', '". # message
1824 $timestamp."'". # timestamp
1825 ")";
1826 &daemon_log("M DEBUG: $sql_statement", 1);
1827 my $res = $messaging_db->exec_statement($sql_statement);
1828 &daemon_log("M INFO: message '".@{$hit}[0]."' is prepared for delivery to receiver '$receiver'", 5);
1829 }
1831 # set incoming message to flag d=deliverd
1832 $sql_statement = "UPDATE $messaging_tn SET flag='p' WHERE id='$message_id'";
1833 &daemon_log("M DEBUG: $sql_statement", 7);
1834 $res = $messaging_db->update_dbentry($sql_statement);
1835 &daemon_log("M INFO: message '$message_id' is set to flag 'p' (processed)", 5);
1836 }
1838 $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1839 return;
1840 }
1842 sub watch_for_delivery_messages {
1843 my ($kernel, $heap) = @_[KERNEL, HEAP];
1845 # select outgoing messages
1846 my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( flag='p' AND direction='out' )";
1847 #&daemon_log("0 DEBUG: $sql", 7);
1848 my $res = $messaging_db->exec_statement( $sql_statement );
1850 # build out msg for each usr
1851 foreach my $hit (@{$res}) {
1852 my $receiver = @{$hit}[3];
1853 my $msg_id = @{$hit}[0];
1854 my $subject = @{$hit}[1];
1855 my $message = @{$hit}[7];
1857 # resolve usr -> host where usr is logged in
1858 my $sql = "SELECT * FROM $login_users_tn WHERE (user='$receiver')";
1859 #&daemon_log("0 DEBUG: $sql", 7);
1860 my $res = $login_users_db->exec_statement($sql);
1862 # reciver is logged in nowhere
1863 if (not ref(@$res[0]) eq "ARRAY") { next; }
1865 my $send_succeed = 0;
1866 foreach my $hit (@$res) {
1867 my $receiver_host = @$hit[0];
1868 &daemon_log("M DEBUG: user '$receiver' is logged in at host '$receiver_host'", 7);
1870 # fetch key to encrypt msg propperly for usr/host
1871 my $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$receiver_host')";
1872 &daemon_log("0 DEBUG: $sql", 7);
1873 my $res = $known_clients_db->exec_statement($sql);
1875 # host is already down
1876 if (not ref(@$res[0]) eq "ARRAY") { next; }
1878 # host is on
1879 my $receiver_key = @{@{$res}[0]}[2];
1880 my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
1881 my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data );
1882 my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0);
1883 if ($error == 0 ) {
1884 $send_succeed++ ;
1885 }
1886 }
1888 if ($send_succeed) {
1889 # set outgoing msg at db to deliverd
1890 my $sql = "UPDATE $messaging_tn SET flag='d' WHERE (id='$msg_id' AND direction='out' AND message_to='$receiver')";
1891 &daemon_log("0 DEBUG: $sql", 7);
1892 my $res = $messaging_db->exec_statement($sql);
1893 }
1894 }
1896 $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1897 return;
1898 }
1901 sub watch_for_done_messages {
1902 my ($kernel,$heap) = @_[KERNEL, HEAP];
1904 my $sql = "SELECT * FROM $messaging_tn WHERE (flag='p' AND direction='in')";
1905 #&daemon_log("0 DEBUG: $sql", 7);
1906 my $res = $messaging_db->exec_statement($sql);
1908 foreach my $hit (@{$res}) {
1909 my $msg_id = @{$hit}[0];
1911 my $sql = "SELECT * FROM $messaging_tn WHERE (id='$msg_id' AND direction='out' AND (NOT flag='s'))";
1912 #&daemon_log("0 DEBUG: $sql", 7);
1913 my $res = $messaging_db->exec_statement($sql);
1915 # not all usr msgs have been seen till now
1916 if ( ref(@$res[0]) eq "ARRAY") { next; }
1918 $sql = "DELETE FROM $messaging_tn WHERE (id='$msg_id')";
1919 #&daemon_log("0 DEBUG: $sql", 7);
1920 $res = $messaging_db->exec_statement($sql);
1922 }
1924 $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1925 return;
1926 }
1929 sub watch_for_old_known_clients {
1930 my ($kernel,$heap) = @_[KERNEL, HEAP];
1932 my $sql_statement = "SELECT * FROM $known_clients_tn";
1933 my $res = $known_clients_db->select_dbentry( $sql_statement );
1935 my $act_time = int(&get_time());
1937 while ( my ($hit_num, $hit) = each %$res) {
1938 my $expired_timestamp = int($hit->{'timestamp'});
1939 $expired_timestamp =~ /(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/;
1940 my $dt = DateTime->new( year => $1,
1941 month => $2,
1942 day => $3,
1943 hour => $4,
1944 minute => $5,
1945 second => $6,
1946 );
1948 $dt->add( seconds => 2 * int($hit->{'keylifetime'}) );
1949 $expired_timestamp = $dt->ymd('').$dt->hms('');
1950 if ($act_time > $expired_timestamp) {
1951 my $hostname = $hit->{'hostname'};
1952 my $del_sql = "DELETE FROM $known_clients_tn WHERE hostname='$hostname'";
1953 my $del_res = $known_clients_db->exec_statement($del_sql);
1955 &main::daemon_log("0 INFO: timestamp '".$hit->{'timestamp'}."' of client '$hostname' is expired('$expired_timestamp'), client will be deleted from known_clients_db", 5);
1956 }
1958 }
1960 $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1961 }
1964 sub watch_for_next_tasks {
1965 my ($kernel,$heap) = @_[KERNEL, HEAP];
1967 my $sql = "SELECT * FROM $incoming_tn";
1968 my $res = $incoming_db->select_dbentry($sql);
1970 while ( my ($hit_num, $hit) = each %$res) {
1971 my $headertag = $hit->{'headertag'};
1972 if ($headertag =~ /^answer_(\d+)/) {
1973 # do not start processing, this message is for a still running POE::Wheel
1974 next;
1975 }
1976 my $message_id = $hit->{'id'};
1977 $kernel->yield('next_task', $hit);
1979 my $sql = "DELETE FROM $incoming_tn WHERE id=$message_id";
1980 my $res = $incoming_db->exec_statement($sql);
1981 }
1983 $kernel->delay_set('watch_for_next_tasks', 0.1);
1984 }
1987 sub get_ldap_handle {
1988 my ($session_id) = @_;
1989 my $heap;
1990 my $ldap_handle;
1992 if (not defined $session_id ) { $session_id = 0 };
1993 if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
1995 if ($session_id == 0) {
1996 daemon_log("$session_id DEBUG: get_ldap_handle invoked without a session_id, create a new ldap_handle", 7);
1997 $ldap_handle = Net::LDAP->new( $ldap_uri );
1998 $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!");
2000 } else {
2001 my $session_reference = $global_kernel->ID_id_to_session($session_id);
2002 if( defined $session_reference ) {
2003 $heap = $session_reference->get_heap();
2004 }
2006 if (not defined $heap) {
2007 daemon_log("$session_id DEBUG: cannot get heap for session_id '$session_id'", 7);
2008 return;
2009 }
2011 # TODO: This "if" is nonsense, because it doesn't prove that the
2012 # used handle is still valid - or if we've to reconnect...
2013 #if (not exists $heap->{ldap_handle}) {
2014 $ldap_handle = Net::LDAP->new( $ldap_uri );
2015 $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!");
2016 $heap->{ldap_handle} = $ldap_handle;
2017 #}
2018 }
2019 return $ldap_handle;
2020 }
2023 sub change_fai_state {
2024 my ($st, $targets, $session_id) = @_;
2025 $session_id = 0 if not defined $session_id;
2026 # Set FAI state to localboot
2027 my %mapActions= (
2028 reboot => '',
2029 update => 'softupdate',
2030 localboot => 'localboot',
2031 reinstall => 'install',
2032 rescan => '',
2033 wake => '',
2034 memcheck => 'memcheck',
2035 sysinfo => 'sysinfo',
2036 install => 'install',
2037 );
2039 # Return if this is unknown
2040 if (!exists $mapActions{ $st }){
2041 daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1);
2042 return;
2043 }
2045 my $state= $mapActions{ $st };
2047 my $ldap_handle = &get_ldap_handle($session_id);
2048 if( defined($ldap_handle) ) {
2050 # Build search filter for hosts
2051 my $search= "(&(objectClass=GOhard)";
2052 foreach (@{$targets}){
2053 $search.= "(macAddress=$_)";
2054 }
2055 $search.= ")";
2057 # If there's any host inside of the search string, procress them
2058 if (!($search =~ /macAddress/)){
2059 daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);
2060 return;
2061 }
2063 # Perform search for Unit Tag
2064 my $mesg = $ldap_handle->search(
2065 base => $ldap_base,
2066 scope => 'sub',
2067 attrs => ['dn', 'FAIstate', 'objectClass'],
2068 filter => "$search"
2069 );
2071 if ($mesg->count) {
2072 my @entries = $mesg->entries;
2073 if (0 == @entries) {
2074 daemon_log("$session_id ERROR: ldap search failed: ldap_base=$ldap_base, filter=$search", 1);
2075 }
2077 foreach my $entry (@entries) {
2078 # Only modify entry if it is not set to '$state'
2079 if ($entry->get_value("FAIstate") ne "$state"){
2080 daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
2081 my $result;
2082 my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
2083 if (exists $tmp{'FAIobject'}){
2084 if ($state eq ''){
2085 $result= $ldap_handle->modify($entry->dn, changes => [
2086 delete => [ FAIstate => [] ] ]);
2087 } else {
2088 $result= $ldap_handle->modify($entry->dn, changes => [
2089 replace => [ FAIstate => $state ] ]);
2090 }
2091 } elsif ($state ne ''){
2092 $result= $ldap_handle->modify($entry->dn, changes => [
2093 add => [ objectClass => 'FAIobject' ],
2094 add => [ FAIstate => $state ] ]);
2095 }
2097 # Errors?
2098 if ($result->code){
2099 daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2100 }
2101 } else {
2102 daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 7);
2103 }
2104 }
2105 } else {
2106 daemon_log("$session_id ERROR: LDAP search failed: ldap_base=$ldap_base, filter=$search", 1);
2107 }
2109 # if no ldap handle defined
2110 } else {
2111 daemon_log("$session_id ERROR: no LDAP handle defined for update FAIstate", 1);
2112 }
2114 return;
2115 }
2118 sub change_goto_state {
2119 my ($st, $targets, $session_id) = @_;
2120 $session_id = 0 if not defined $session_id;
2122 # Switch on or off?
2123 my $state= $st eq 'active' ? 'active': 'locked';
2125 my $ldap_handle = &get_ldap_handle($session_id);
2126 if( defined($ldap_handle) ) {
2128 # Build search filter for hosts
2129 my $search= "(&(objectClass=GOhard)";
2130 foreach (@{$targets}){
2131 $search.= "(macAddress=$_)";
2132 }
2133 $search.= ")";
2135 # If there's any host inside of the search string, procress them
2136 if (!($search =~ /macAddress/)){
2137 return;
2138 }
2140 # Perform search for Unit Tag
2141 my $mesg = $ldap_handle->search(
2142 base => $ldap_base,
2143 scope => 'sub',
2144 attrs => ['dn', 'gotoMode'],
2145 filter => "$search"
2146 );
2148 if ($mesg->count) {
2149 my @entries = $mesg->entries;
2150 foreach my $entry (@entries) {
2152 # Only modify entry if it is not set to '$state'
2153 if ($entry->get_value("gotoMode") ne $state){
2155 daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
2156 my $result;
2157 $result= $ldap_handle->modify($entry->dn, changes => [
2158 replace => [ gotoMode => $state ] ]);
2160 # Errors?
2161 if ($result->code){
2162 &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2163 }
2165 }
2166 }
2167 } else {
2168 daemon_log("$session_id ERROR: LDAP search failed in function change_goto_state: ldap_base=$ldap_base, filter=$search", 1);
2169 }
2171 }
2172 }
2175 sub run_recreate_packages_db {
2176 my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2177 my $session_id = $session->ID;
2178 &main::daemon_log("$session_id INFO: Recreating FAI Packages DB ('$fai_release_tn', '$fai_server_tn', '$packages_list_tn')", 5);
2179 $kernel->yield('create_fai_release_db', $fai_release_tn);
2180 $kernel->yield('create_fai_server_db', $fai_server_tn);
2181 return;
2182 }
2185 sub run_create_fai_server_db {
2186 my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2187 my $session_id = $session->ID;
2188 my $task = POE::Wheel::Run->new(
2189 Program => sub { &create_fai_server_db($table_name,$kernel, undef, $session_id) },
2190 StdoutEvent => "session_run_result",
2191 StderrEvent => "session_run_debug",
2192 CloseEvent => "session_run_done",
2193 );
2195 $heap->{task}->{ $task->ID } = $task;
2196 return;
2197 }
2200 sub create_fai_server_db {
2201 my ($table_name, $kernel, $dont_create_packages_list, $session_id) = @_;
2202 my $result;
2204 if (not defined $session_id) { $session_id = 0; }
2205 my $ldap_handle = &get_ldap_handle();
2206 if(defined($ldap_handle)) {
2207 daemon_log("$session_id INFO: create_fai_server_db: start", 5);
2208 my $mesg= $ldap_handle->search(
2209 base => $ldap_base,
2210 scope => 'sub',
2211 attrs => ['FAIrepository', 'gosaUnitTag'],
2212 filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
2213 );
2214 if($mesg->{'resultCode'} == 0 &&
2215 $mesg->count != 0) {
2216 foreach my $entry (@{$mesg->{entries}}) {
2217 if($entry->exists('FAIrepository')) {
2218 # Add an entry for each Repository configured for server
2219 foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
2220 my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
2221 my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
2222 $result= $fai_server_db->add_dbentry( {
2223 table => $table_name,
2224 primkey => ['server', 'release', 'tag'],
2225 server => $tmp_url,
2226 release => $tmp_release,
2227 sections => $tmp_sections,
2228 tag => (length($tmp_tag)>0)?$tmp_tag:"",
2229 } );
2230 }
2231 }
2232 }
2233 }
2234 daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
2236 # TODO: Find a way to post the 'create_packages_list_db' event
2237 if(not defined($dont_create_packages_list)) {
2238 &create_packages_list_db(undef, undef, $session_id);
2239 }
2240 }
2242 $ldap_handle->disconnect;
2243 return $result;
2244 }
2247 sub run_create_fai_release_db {
2248 my ($session, $heap, $table_name) = @_[SESSION, HEAP, ARG0];
2249 my $session_id = $session->ID;
2250 my $task = POE::Wheel::Run->new(
2251 Program => sub { &create_fai_release_db($table_name, $session_id) },
2252 StdoutEvent => "session_run_result",
2253 StderrEvent => "session_run_debug",
2254 CloseEvent => "session_run_done",
2255 );
2257 $heap->{task}->{ $task->ID } = $task;
2258 return;
2259 }
2262 sub create_fai_release_db {
2263 my ($table_name, $session_id) = @_;
2264 my $result;
2266 # used for logging
2267 if (not defined $session_id) { $session_id = 0; }
2269 my $ldap_handle = &get_ldap_handle();
2270 if(defined($ldap_handle)) {
2271 daemon_log("$session_id INFO: create_fai_release_db: start",5);
2272 my $mesg= $ldap_handle->search(
2273 base => $ldap_base,
2274 scope => 'sub',
2275 attrs => [],
2276 filter => "(&(objectClass=organizationalUnit)(ou=fai))",
2277 );
2278 if($mesg->{'resultCode'} == 0 &&
2279 $mesg->count != 0) {
2280 # Walk through all possible FAI container ou's
2281 my @sql_list;
2282 my $timestamp= &get_time();
2283 foreach my $ou (@{$mesg->{entries}}) {
2284 my $tmp_classes= resolve_fai_classes($ou->dn, $ldap_handle, $session_id);
2285 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
2286 my @tmp_array=get_fai_release_entries($tmp_classes);
2287 if(@tmp_array) {
2288 foreach my $entry (@tmp_array) {
2289 if(defined($entry) && ref($entry) eq 'HASH') {
2290 my $sql=
2291 "INSERT INTO $table_name "
2292 ."(timestamp, release, class, type, state) VALUES ("
2293 .$timestamp.","
2294 ."'".$entry->{'release'}."',"
2295 ."'".$entry->{'class'}."',"
2296 ."'".$entry->{'type'}."',"
2297 ."'".$entry->{'state'}."')";
2298 push @sql_list, $sql;
2299 }
2300 }
2301 }
2302 }
2303 }
2305 daemon_log("$session_id DEBUG: Inserting ".scalar @sql_list." entries to DB",8);
2306 if(@sql_list) {
2307 unshift @sql_list, "VACUUM";
2308 unshift @sql_list, "DELETE FROM $table_name";
2309 $fai_release_db->exec_statementlist(\@sql_list);
2310 }
2311 daemon_log("$session_id DEBUG: Done with inserting",7);
2312 }
2313 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
2314 }
2315 $ldap_handle->disconnect;
2316 return $result;
2317 }
2319 sub get_fai_types {
2320 my $tmp_classes = shift || return undef;
2321 my @result;
2323 foreach my $type(keys %{$tmp_classes}) {
2324 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
2325 my $entry = {
2326 type => $type,
2327 state => $tmp_classes->{$type}[0],
2328 };
2329 push @result, $entry;
2330 }
2331 }
2333 return @result;
2334 }
2336 sub get_fai_state {
2337 my $result = "";
2338 my $tmp_classes = shift || return $result;
2340 foreach my $type(keys %{$tmp_classes}) {
2341 if(defined($tmp_classes->{$type}[0])) {
2342 $result = $tmp_classes->{$type}[0];
2344 # State is equal for all types in class
2345 last;
2346 }
2347 }
2349 return $result;
2350 }
2352 sub resolve_fai_classes {
2353 my ($fai_base, $ldap_handle, $session_id) = @_;
2354 if (not defined $session_id) { $session_id = 0; }
2355 my $result;
2356 my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
2357 my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
2358 my $fai_classes;
2360 daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base",7);
2361 my $mesg= $ldap_handle->search(
2362 base => $fai_base,
2363 scope => 'sub',
2364 attrs => ['cn','objectClass','FAIstate'],
2365 filter => $fai_filter,
2366 );
2367 daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries",7);
2369 if($mesg->{'resultCode'} == 0 &&
2370 $mesg->count != 0) {
2371 foreach my $entry (@{$mesg->{entries}}) {
2372 if($entry->exists('cn')) {
2373 my $tmp_dn= $entry->dn();
2375 # Skip classname and ou dn parts for class
2376 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?),$fai_base$/;
2378 # Skip classes without releases
2379 if((!defined($tmp_release)) || length($tmp_release)==0) {
2380 next;
2381 }
2383 my $tmp_cn= $entry->get_value('cn');
2384 my $tmp_state= $entry->get_value('FAIstate');
2386 my $tmp_type;
2387 # Get FAI type
2388 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
2389 if(grep $_ eq $oclass, @possible_fai_classes) {
2390 $tmp_type= $oclass;
2391 last;
2392 }
2393 }
2395 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2396 # A Subrelease
2397 my @sub_releases = split(/,/, $tmp_release);
2399 # Walk through subreleases and build hash tree
2400 my $hash;
2401 while(my $tmp_sub_release = pop @sub_releases) {
2402 $hash .= "\{'$tmp_sub_release'\}->";
2403 }
2404 eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
2405 } else {
2406 # A branch, no subrelease
2407 push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
2408 }
2409 } elsif (!$entry->exists('cn')) {
2410 my $tmp_dn= $entry->dn();
2411 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?),$fai_base$/;
2413 # Skip classes without releases
2414 if((!defined($tmp_release)) || length($tmp_release)==0) {
2415 next;
2416 }
2418 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2419 # A Subrelease
2420 my @sub_releases= split(/,/, $tmp_release);
2422 # Walk through subreleases and build hash tree
2423 my $hash;
2424 while(my $tmp_sub_release = pop @sub_releases) {
2425 $hash .= "\{'$tmp_sub_release'\}->";
2426 }
2427 # Remove the last two characters
2428 chop($hash);
2429 chop($hash);
2431 eval('$fai_classes->'.$hash.'= {}');
2432 } else {
2433 # A branch, no subrelease
2434 if(!exists($fai_classes->{$tmp_release})) {
2435 $fai_classes->{$tmp_release} = {};
2436 }
2437 }
2438 }
2439 }
2441 # The hash is complete, now we can honor the copy-on-write based missing entries
2442 foreach my $release (keys %$fai_classes) {
2443 $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
2444 }
2445 }
2446 return $result;
2447 }
2449 sub apply_fai_inheritance {
2450 my $fai_classes = shift || return {};
2451 my $tmp_classes;
2453 # Get the classes from the branch
2454 foreach my $class (keys %{$fai_classes}) {
2455 # Skip subreleases
2456 if($class =~ /^ou=.*$/) {
2457 next;
2458 } else {
2459 $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
2460 }
2461 }
2463 # Apply to each subrelease
2464 foreach my $subrelease (keys %{$fai_classes}) {
2465 if($subrelease =~ /ou=/) {
2466 foreach my $tmp_class (keys %{$tmp_classes}) {
2467 if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
2468 $fai_classes->{$subrelease}->{$tmp_class} =
2469 deep_copy($tmp_classes->{$tmp_class});
2470 } else {
2471 foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
2472 if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
2473 $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
2474 deep_copy($tmp_classes->{$tmp_class}->{$type});
2475 }
2476 }
2477 }
2478 }
2479 }
2480 }
2482 # Find subreleases in deeper levels
2483 foreach my $subrelease (keys %{$fai_classes}) {
2484 if($subrelease =~ /ou=/) {
2485 foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
2486 if($subsubrelease =~ /ou=/) {
2487 apply_fai_inheritance($fai_classes->{$subrelease});
2488 }
2489 }
2490 }
2491 }
2493 return $fai_classes;
2494 }
2496 sub get_fai_release_entries {
2497 my $tmp_classes = shift || return;
2498 my $parent = shift || "";
2499 my @result = shift || ();
2501 foreach my $entry (keys %{$tmp_classes}) {
2502 if(defined($entry)) {
2503 if($entry =~ /^ou=.*$/) {
2504 my $release_name = $entry;
2505 $release_name =~ s/ou=//g;
2506 if(length($parent)>0) {
2507 $release_name = $parent."/".$release_name;
2508 }
2509 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2510 foreach my $bufentry(@bufentries) {
2511 push @result, $bufentry;
2512 }
2513 } else {
2514 my @types = get_fai_types($tmp_classes->{$entry});
2515 foreach my $type (@types) {
2516 push @result,
2517 {
2518 'class' => $entry,
2519 'type' => $type->{'type'},
2520 'release' => $parent,
2521 'state' => $type->{'state'},
2522 };
2523 }
2524 }
2525 }
2526 }
2528 return @result;
2529 }
2531 sub deep_copy {
2532 my $this = shift;
2533 if (not ref $this) {
2534 $this;
2535 } elsif (ref $this eq "ARRAY") {
2536 [map deep_copy($_), @$this];
2537 } elsif (ref $this eq "HASH") {
2538 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2539 } else { die "what type is $_?" }
2540 }
2543 sub session_run_result {
2544 my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];
2545 $kernel->sig(CHLD => "child_reap");
2546 }
2548 sub session_run_debug {
2549 my $result = $_[ARG0];
2550 print STDERR "$result\n";
2551 }
2553 sub session_run_done {
2554 my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2555 delete $heap->{task}->{$task_id};
2556 }
2559 sub create_sources_list {
2560 my $session_id = shift;
2561 my $ldap_handle = &main::get_ldap_handle;
2562 my $result="/tmp/gosa_si_tmp_sources_list";
2564 # Remove old file
2565 if(stat($result)) {
2566 unlink($result);
2567 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7);
2568 }
2570 my $fh;
2571 open($fh, ">$result");
2572 if (not defined $fh) {
2573 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7);
2574 return undef;
2575 }
2576 if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2577 my $mesg=$ldap_handle->search(
2578 base => $main::ldap_server_dn,
2579 scope => 'base',
2580 attrs => 'FAIrepository',
2581 filter => 'objectClass=FAIrepositoryServer'
2582 );
2583 if($mesg->count) {
2584 foreach my $entry(@{$mesg->{'entries'}}) {
2585 foreach my $value(@{$entry->get_value('FAIrepository', asref => 1)}) {
2586 my ($server, $tag, $release, $sections)= split /\|/, $value;
2587 my $line = "deb $server $release";
2588 $sections =~ s/,/ /g;
2589 $line.= " $sections";
2590 print $fh $line."\n";
2591 }
2592 }
2593 }
2594 } else {
2595 if (defined $main::ldap_server_dn){
2596 &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1);
2597 } else {
2598 &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2599 }
2600 }
2601 close($fh);
2603 return $result;
2604 }
2607 sub run_create_packages_list_db {
2608 my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2609 my $session_id = $session->ID;
2611 my $task = POE::Wheel::Run->new(
2612 Priority => +20,
2613 Program => sub {&create_packages_list_db(undef, undef, $session_id)},
2614 StdoutEvent => "session_run_result",
2615 StderrEvent => "session_run_debug",
2616 CloseEvent => "session_run_done",
2617 );
2618 $heap->{task}->{ $task->ID } = $task;
2619 }
2622 sub create_packages_list_db {
2623 my ($ldap_handle, $sources_file, $session_id) = @_;
2625 # it should not be possible to trigger a recreation of packages_list_db
2626 # while packages_list_db is under construction, so set flag packages_list_under_construction
2627 # which is tested befor recreation can be started
2628 if (-r $packages_list_under_construction) {
2629 daemon_log("$session_id WARNING: packages_list_db is right now under construction, please wait until this process is finished", 3);
2630 return;
2631 } else {
2632 daemon_log("$session_id INFO: create_packages_list_db: start", 5);
2633 # set packages_list_under_construction to true
2634 system("touch $packages_list_under_construction");
2635 @packages_list_statements=();
2636 }
2638 if (not defined $session_id) { $session_id = 0; }
2639 if (not defined $ldap_handle) {
2640 $ldap_handle= &get_ldap_handle();
2642 if (not defined $ldap_handle) {
2643 daemon_log("$session_id ERROR: no ldap_handle available to create_packages_list_db", 1);
2644 unlink($packages_list_under_construction);
2645 return;
2646 }
2647 }
2648 if (not defined $sources_file) {
2649 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5);
2650 $sources_file = &create_sources_list($session_id);
2651 }
2653 if (not defined $sources_file) {
2654 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1);
2655 unlink($packages_list_under_construction);
2656 return;
2657 }
2659 my $line;
2661 open(CONFIG, "<$sources_file") or do {
2662 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2663 unlink($packages_list_under_construction);
2664 return;
2665 };
2667 # Read lines
2668 while ($line = <CONFIG>){
2669 # Unify
2670 chop($line);
2671 $line =~ s/^\s+//;
2672 $line =~ s/^\s+/ /;
2674 # Strip comments
2675 $line =~ s/#.*$//g;
2677 # Skip empty lines
2678 if ($line =~ /^\s*$/){
2679 next;
2680 }
2682 # Interpret deb line
2683 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2684 my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2685 my $section;
2686 foreach $section (split(' ', $sections)){
2687 &parse_package_info( $baseurl, $dist, $section, $session_id );
2688 }
2689 }
2690 }
2692 close (CONFIG);
2695 find(\&cleanup_and_extract, keys( %repo_dirs ));
2696 &main::strip_packages_list_statements();
2697 unshift @packages_list_statements, "VACUUM";
2698 $packages_list_db->exec_statementlist(\@packages_list_statements);
2699 unlink($packages_list_under_construction);
2700 daemon_log("$session_id INFO: create_packages_list_db: finished", 5);
2701 return;
2702 }
2704 # This function should do some intensive task to minimize the db-traffic
2705 sub strip_packages_list_statements {
2706 my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2707 my @new_statement_list=();
2708 my $hash;
2709 my $insert_hash;
2710 my $update_hash;
2711 my $delete_hash;
2712 my $local_timestamp=get_time();
2714 foreach my $existing_entry (@existing_entries) {
2715 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
2716 }
2718 foreach my $statement (@packages_list_statements) {
2719 if($statement =~ /^INSERT/i) {
2720 # Assign the values from the insert statement
2721 my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~
2722 /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
2723 if(exists($hash->{$distribution}->{$package}->{$version})) {
2724 # If section or description has changed, update the DB
2725 if(
2726 (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or
2727 (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
2728 ) {
2729 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
2730 }
2731 } else {
2732 # Insert a non-existing entry to db
2733 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2734 }
2735 } elsif ($statement =~ /^UPDATE/i) {
2736 my ($template,$package,$version) = ($1,$2,$3) if $statement =~
2737 /^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;
2738 foreach my $distribution (keys %{$hash}) {
2739 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
2740 # update the insertion hash to execute only one query per package (insert instead insert+update)
2741 @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
2742 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
2743 if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
2744 my $section;
2745 my $description;
2746 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
2747 length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
2748 $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
2749 }
2750 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2751 $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
2752 }
2753 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2754 }
2755 }
2756 }
2757 }
2758 }
2760 # TODO: Check for orphaned entries
2762 # unroll the insert_hash
2763 foreach my $distribution (keys %{$insert_hash}) {
2764 foreach my $package (keys %{$insert_hash->{$distribution}}) {
2765 foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
2766 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
2767 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
2768 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
2769 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
2770 ."'$local_timestamp')";
2771 }
2772 }
2773 }
2775 # unroll the update hash
2776 foreach my $distribution (keys %{$update_hash}) {
2777 foreach my $package (keys %{$update_hash->{$distribution}}) {
2778 foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
2779 my $set = "";
2780 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
2781 $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
2782 }
2783 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2784 $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
2785 }
2786 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
2787 $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
2788 }
2789 if(defined($set) and length($set) > 0) {
2790 $set .= "timestamp = '$local_timestamp'";
2791 } else {
2792 next;
2793 }
2794 push @new_statement_list,
2795 "UPDATE $main::packages_list_tn SET $set WHERE"
2796 ." distribution = '$distribution'"
2797 ." AND package = '$package'"
2798 ." AND version = '$version'";
2799 }
2800 }
2801 }
2803 @packages_list_statements = @new_statement_list;
2804 }
2807 sub parse_package_info {
2808 my ($baseurl, $dist, $section, $session_id)= @_;
2809 my ($package);
2810 if (not defined $session_id) { $session_id = 0; }
2811 my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
2812 $repo_dirs{ "${repo_path}/pool" } = 1;
2814 foreach $package ("Packages.gz"){
2815 daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
2816 get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
2817 parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
2818 }
2820 }
2823 sub get_package {
2824 my ($url, $dest, $session_id)= @_;
2825 if (not defined $session_id) { $session_id = 0; }
2827 my $tpath = dirname($dest);
2828 -d "$tpath" || mkpath "$tpath";
2830 # This is ugly, but I've no time to take a look at "how it works in perl"
2831 if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
2832 system("gunzip -cd '$dest' > '$dest.in'");
2833 daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 5);
2834 unlink($dest);
2835 daemon_log("$session_id DEBUG: delete file '$dest'", 5);
2836 } else {
2837 daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' failed!", 1);
2838 }
2839 return 0;
2840 }
2843 sub parse_package {
2844 my ($path, $dist, $srv_path, $session_id)= @_;
2845 if (not defined $session_id) { $session_id = 0;}
2846 my ($package, $version, $section, $description);
2847 my $PACKAGES;
2848 my $timestamp = &get_time();
2850 if(not stat("$path.in")) {
2851 daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
2852 return;
2853 }
2855 open($PACKAGES, "<$path.in");
2856 if(not defined($PACKAGES)) {
2857 daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1);
2858 return;
2859 }
2861 # Read lines
2862 while (<$PACKAGES>){
2863 my $line = $_;
2864 # Unify
2865 chop($line);
2867 # Use empty lines as a trigger
2868 if ($line =~ /^\s*$/){
2869 my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
2870 push(@packages_list_statements, $sql);
2871 $package = "none";
2872 $version = "none";
2873 $section = "none";
2874 $description = "none";
2875 next;
2876 }
2878 # Trigger for package name
2879 if ($line =~ /^Package:\s/){
2880 ($package)= ($line =~ /^Package: (.*)$/);
2881 next;
2882 }
2884 # Trigger for version
2885 if ($line =~ /^Version:\s/){
2886 ($version)= ($line =~ /^Version: (.*)$/);
2887 next;
2888 }
2890 # Trigger for description
2891 if ($line =~ /^Description:\s/){
2892 ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
2893 next;
2894 }
2896 # Trigger for section
2897 if ($line =~ /^Section:\s/){
2898 ($section)= ($line =~ /^Section: (.*)$/);
2899 next;
2900 }
2902 # Trigger for filename
2903 if ($line =~ /^Filename:\s/){
2904 my ($filename) = ($line =~ /^Filename: (.*)$/);
2905 store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
2906 next;
2907 }
2908 }
2910 close( $PACKAGES );
2911 unlink( "$path.in" );
2912 }
2915 sub store_fileinfo {
2916 my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
2918 my %fileinfo = (
2919 'package' => $package,
2920 'dist' => $dist,
2921 'version' => $vers,
2922 );
2924 $repo_files{ "${srvdir}/$file" } = \%fileinfo;
2925 }
2928 sub cleanup_and_extract {
2929 my $fileinfo = $repo_files{ $File::Find::name };
2931 if( defined $fileinfo ) {
2933 my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
2934 my $sql;
2935 my $package = $fileinfo->{ 'package' };
2936 my $newver = $fileinfo->{ 'version' };
2938 mkpath($dir);
2939 system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
2941 if( -f "$dir/DEBIAN/templates" ) {
2943 daemon_log("DEBUG: Found debconf templates in '$package' - $newver", 7);
2945 my $tmpl= "";
2946 {
2947 local $/=undef;
2948 open FILE, "$dir/DEBIAN/templates";
2949 $tmpl = &encode_base64(<FILE>);
2950 close FILE;
2951 }
2952 rmtree("$dir/DEBIAN/templates");
2954 $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
2955 push @packages_list_statements, $sql;
2956 }
2957 }
2959 return;
2960 }
2963 sub register_at_foreign_servers {
2964 my ($kernel) = $_[KERNEL];
2966 # hole alle bekannten server aus known_server_db
2967 my $server_sql = "SELECT * FROM $known_server_tn";
2968 my $server_res = $known_server_db->exec_statement($server_sql);
2970 # no entries in known_server_db
2971 if (not ref(@$server_res[0]) eq "ARRAY") {
2972 # TODO
2973 }
2975 # detect already connected clients
2976 my $client_sql = "SELECT * FROM $known_clients_tn";
2977 my $client_res = $known_clients_db->exec_statement($client_sql);
2979 # send my server details to all other gosa-si-server within the network
2980 foreach my $hit (@$server_res) {
2981 my $hostname = @$hit[0];
2982 my $hostkey = &create_passwd;
2984 # add already connected clients to registration message
2985 my $myhash = &create_xml_hash('new_server', $server_address, $hostname);
2986 &add_content2xml_hash($myhash, 'key', $hostkey);
2987 map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res);
2989 # add locally loaded gosa-si modules to registration message
2990 my $loaded_modules = {};
2991 while (my ($package, $pck_info) = each %$known_modules) {
2992 foreach my $act_module (keys(%{@$pck_info[2]})) {
2993 $loaded_modules->{$act_module} = "";
2994 }
2995 }
2996 map(&add_content2xml_hash($myhash, "loaded_modules", $_), keys(%$loaded_modules));
2998 # add macaddress to registration message
2999 my ($host_ip, $host_port) = split(/:/, $hostname);
3000 my $local_ip = &get_local_ip_for_remote_ip($host_ip);
3001 my $network_interface= &get_interface_for_ip($local_ip);
3002 my $host_mac = &get_mac_for_interface($network_interface);
3003 &add_content2xml_hash($myhash, 'macaddress', $host_mac);
3005 # build registration message and send it
3006 my $foreign_server_msg = &create_xml_string($myhash);
3007 my $error = &send_msg_to_target($foreign_server_msg, $hostname, $ServerPackages_key, "new_server", 0);
3008 }
3010 $kernel->delay_set("register_at_foreign_servers", $foreign_servers_register_delay);
3011 return;
3012 }
3015 #==== MAIN = main ==============================================================
3016 # parse commandline options
3017 Getopt::Long::Configure( "bundling" );
3018 GetOptions("h|help" => \&usage,
3019 "c|config=s" => \$cfg_file,
3020 "f|foreground" => \$foreground,
3021 "v|verbose+" => \$verbose,
3022 "no-arp+" => \$no_arp,
3023 );
3025 # read and set config parameters
3026 &check_cmdline_param ;
3027 &read_configfile($cfg_file, %cfg_defaults);
3028 &check_pid;
3030 $SIG{CHLD} = 'IGNORE';
3032 # forward error messages to logfile
3033 if( ! $foreground ) {
3034 open( STDIN, '+>/dev/null' );
3035 open( STDOUT, '+>&STDIN' );
3036 open( STDERR, '+>&STDIN' );
3037 }
3039 # Just fork, if we are not in foreground mode
3040 if( ! $foreground ) {
3041 chdir '/' or die "Can't chdir to /: $!";
3042 $pid = fork;
3043 setsid or die "Can't start a new session: $!";
3044 umask 0;
3045 } else {
3046 $pid = $$;
3047 }
3049 # Do something useful - put our PID into the pid_file
3050 if( 0 != $pid ) {
3051 open( LOCK_FILE, ">$pid_file" );
3052 print LOCK_FILE "$pid\n";
3053 close( LOCK_FILE );
3054 if( !$foreground ) {
3055 exit( 0 )
3056 };
3057 }
3059 # parse head url and revision from svn
3060 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
3061 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
3062 $server_headURL = defined $1 ? $1 : 'unknown' ;
3063 $server_revision = defined $2 ? $2 : 'unknown' ;
3064 if ($server_headURL =~ /\/tag\// ||
3065 $server_headURL =~ /\/branches\// ) {
3066 $server_status = "stable";
3067 } else {
3068 $server_status = "developmental" ;
3069 }
3072 daemon_log(" ", 1);
3073 daemon_log("$0 started!", 1);
3074 daemon_log("status: $server_status", 1);
3075 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1);
3077 # connect to incoming_db
3078 unlink($incoming_file_name);
3079 $incoming_db = GOSA::DBsqlite->new($incoming_file_name);
3080 $incoming_db->create_table($incoming_tn, \@incoming_col_names);
3082 # connect to gosa-si job queue
3083 $job_db = GOSA::DBsqlite->new($job_queue_file_name);
3084 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
3086 # connect to known_clients_db
3087 $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
3088 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
3090 # connect to foreign_clients_db
3091 $foreign_clients_db = GOSA::DBsqlite->new($foreign_clients_file_name);
3092 $foreign_clients_db->create_table($foreign_clients_tn, \@foreign_clients_col_names);
3094 # connect to known_server_db
3095 unlink($known_server_file_name);
3096 $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
3097 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
3099 # connect to login_usr_db
3100 $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
3101 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
3103 # connect to fai_server_db and fai_release_db
3104 unlink($fai_server_file_name);
3105 $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
3106 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
3108 unlink($fai_release_file_name);
3109 $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
3110 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
3112 # connect to packages_list_db
3113 #unlink($packages_list_file_name);
3114 unlink($packages_list_under_construction);
3115 $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
3116 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
3118 # connect to messaging_db
3119 $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
3120 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
3123 # create xml object used for en/decrypting
3124 $xml = new XML::Simple();
3127 # foreign servers
3128 my @foreign_server_list;
3130 # add foreign server from cfg file
3131 if ($foreign_server_string ne "") {
3132 my @cfg_foreign_server_list = split(",", $foreign_server_string);
3133 foreach my $foreign_server (@cfg_foreign_server_list) {
3134 push(@foreign_server_list, $foreign_server);
3135 }
3136 }
3138 # add foreign server from dns
3139 my @tmp_servers;
3140 if ( !$server_domain) {
3141 # Try our DNS Searchlist
3142 for my $domain(get_dns_domains()) {
3143 chomp($domain);
3144 my ($tmp_domains, $error_string) = &get_server_addresses($domain);
3145 if(@$tmp_domains) {
3146 for my $tmp_server(@$tmp_domains) {
3147 push @tmp_servers, $tmp_server;
3148 }
3149 }
3150 }
3151 if(@tmp_servers && length(@tmp_servers)==0) {
3152 daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3153 }
3154 } else {
3155 @tmp_servers = &get_server_addresses($server_domain);
3156 if( 0 == @tmp_servers ) {
3157 daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3158 }
3159 }
3160 foreach my $server (@tmp_servers) {
3161 unshift(@foreign_server_list, $server);
3162 }
3163 # eliminate duplicate entries
3164 @foreign_server_list = &del_doubles(@foreign_server_list);
3165 my $all_foreign_server = join(", ", @foreign_server_list);
3166 daemon_log("0 INFO: found foreign server in config file and DNS: $all_foreign_server", 5);
3168 # add all found foreign servers to known_server
3169 my $act_timestamp = &get_time();
3170 foreach my $foreign_server (@foreign_server_list) {
3172 # do not add myself to known_server_db
3173 if (&is_local($foreign_server)) { next; }
3174 ######################################
3176 my $res = $known_server_db->add_dbentry( {table=>$known_server_tn,
3177 primkey=>['hostname'],
3178 hostname=>$foreign_server,
3179 macaddress=>"",
3180 status=>'not_jet_registered',
3181 hostkey=>"none",
3182 loaded_modules => "none",
3183 timestamp=>$act_timestamp,
3184 } );
3185 }
3188 # Import all modules
3189 &import_modules;
3191 # Check wether all modules are gosa-si valid passwd check
3192 &password_check;
3194 # Prepare for using Opsi
3195 if ($opsi_enabled eq "true") {
3196 use JSON::RPC::Client;
3197 use XML::Quote qw(:all);
3198 $opsi_url= "https://".$opsi_admin.":".$opsi_password."@".$opsi_server.":4447/rpc";
3199 $opsi_client = new JSON::RPC::Client;
3200 }
3203 POE::Component::Server::TCP->new(
3204 Alias => "TCP_SERVER",
3205 Port => $server_port,
3206 ClientInput => sub {
3207 my ($kernel, $input) = @_[KERNEL, ARG0];
3208 push(@tasks, $input);
3209 push(@msgs_to_decrypt, $input);
3210 $kernel->yield("msg_to_decrypt");
3211 },
3212 InlineStates => {
3213 msg_to_decrypt => \&msg_to_decrypt,
3214 next_task => \&next_task,
3215 task_result => \&handle_task_result,
3216 task_done => \&handle_task_done,
3217 task_debug => \&handle_task_debug,
3218 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!" },
3219 }
3220 );
3222 daemon_log("start socket for incoming xml messages at port '$server_port' ", 1);
3224 # create session for repeatedly checking the job queue for jobs
3225 POE::Session->create(
3226 inline_states => {
3227 _start => \&session_start,
3228 register_at_foreign_servers => \®ister_at_foreign_servers,
3229 sig_handler => \&sig_handler,
3230 next_task => \&next_task,
3231 task_result => \&handle_task_result,
3232 task_done => \&handle_task_done,
3233 task_debug => \&handle_task_debug,
3234 watch_for_next_tasks => \&watch_for_next_tasks,
3235 watch_for_new_messages => \&watch_for_new_messages,
3236 watch_for_delivery_messages => \&watch_for_delivery_messages,
3237 watch_for_done_messages => \&watch_for_done_messages,
3238 watch_for_new_jobs => \&watch_for_new_jobs,
3239 watch_for_modified_jobs => \&watch_for_modified_jobs,
3240 watch_for_done_jobs => \&watch_for_done_jobs,
3241 watch_for_opsi_jobs => \&watch_for_opsi_jobs,
3242 watch_for_old_known_clients => \&watch_for_old_known_clients,
3243 create_packages_list_db => \&run_create_packages_list_db,
3244 create_fai_server_db => \&run_create_fai_server_db,
3245 create_fai_release_db => \&run_create_fai_release_db,
3246 recreate_packages_db => \&run_recreate_packages_db,
3247 session_run_result => \&session_run_result,
3248 session_run_debug => \&session_run_debug,
3249 session_run_done => \&session_run_done,
3250 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!" },
3251 }
3252 );
3255 POE::Kernel->run();
3256 exit;