aa8590f954ff932dd183b1bb0c6bb90aae0616e2
1 #!/usr/bin/perl
2 #===============================================================================
3 #
4 # FILE: gosa-sd
5 #
6 # USAGE: ./gosa-sd
7 #
8 # DESCRIPTION:
9 #
10 # OPTIONS: ---
11 # REQUIREMENTS: libconfig-inifiles-perl libcrypt-rijndael-perl libxml-simple-perl
12 # libdata-dumper-simple-perl libdbd-sqlite3-perl libnet-ldap-perl
13 # libpoe-perl
14 # BUGS: ---
15 # NOTES:
16 # AUTHOR: (Andreas Rettenberger), <rettenberger@gonicus.de>
17 # COMPANY:
18 # VERSION: 1.0
19 # CREATED: 12.09.2007 08:54:41 CEST
20 # REVISION: ---
21 #===============================================================================
24 # TODO
25 #
26 # max_children wird momentan nicht mehr verwendet, jede eingehende nachricht bekommt ein eigenes POE child
28 use strict;
29 use warnings;
30 use Getopt::Long;
31 use Config::IniFiles;
32 use POSIX;
34 use Fcntl;
35 use IO::Socket::INET;
36 use IO::Handle;
37 use IO::Select;
38 use Symbol qw(qualify_to_ref);
39 use Crypt::Rijndael;
40 use MIME::Base64;
41 use Digest::MD5 qw(md5 md5_hex md5_base64);
42 use XML::Simple;
43 use Data::Dumper;
44 use Sys::Syslog qw( :DEFAULT setlogsock);
45 use Cwd;
46 use File::Spec;
47 use File::Basename;
48 use File::Find;
49 use File::Copy;
50 use File::Path;
51 use GOSA::DBsqlite;
52 use GOSA::GosaSupportDaemon;
53 use POE qw(Component::Server::TCP Wheel::Run Filter::Reference);
54 use Net::LDAP;
55 use Net::LDAP::Util qw(:escape);
56 use Time::HiRes qw( usleep);
58 my $modules_path = "/usr/lib/gosa-si/modules";
59 use lib "/usr/lib/gosa-si/modules";
61 # revision number of server and program name
62 my $server_version = '$HeadURL: https://oss.gonicus.de/repositories/gosa/trunk/gosa-si/gosa-si-server $:$Rev: 10826 $';
63 my $server_headURL;
64 my $server_revision;
65 my $server_status;
66 our $prg= basename($0);
68 our $global_kernel;
69 my ($foreground, $ping_timeout);
70 my ($server);
71 my ($gosa_server, $job_queue_timeout, $job_queue_loop_delay);
72 my ($messaging_db_loop_delay);
73 my ($procid, $pid);
74 my ($arp_fifo);
75 my ($xml);
76 my $sources_list;
77 my $max_clients;
78 my %repo_files=();
79 my $repo_path;
80 my %repo_dirs=();
81 # variables declared in config file are always set to 'our'
82 our (%cfg_defaults, $log_file, $pid_file,
83 $server_ip, $server_port, $ClientPackages_key,
84 $arp_activ, $gosa_unit_tag,
85 $GosaPackages_key, $gosa_timeout,
86 $foreign_server_string, $server_domain, $ServerPackages_key, $foreign_servers_register_delay,
87 $wake_on_lan_passwd, $job_synchronization, $modified_jobs_loop_delay,
88 $arp_enabled, $arp_interface,
89 $opsi_enabled, $opsi_server, $opsi_admin, $opsi_password,
90 $new_systems_ou,
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;
213 # Lifetime of logged in user information. If no update information comes after n seconds,
214 # the user is expeceted to be no longer logged in or the host is no longer running. Because
215 # of this, the user is deleted from login_users_db
216 our $logged_in_user_date_of_expiry = 600;
219 %cfg_defaults = (
220 "general" => {
221 "log-file" => [\$log_file, "/var/run/".$prg.".log"],
222 "pid-file" => [\$pid_file, "/var/run/".$prg.".pid"],
223 },
224 "server" => {
225 "ip" => [\$server_ip, "0.0.0.0"],
226 "port" => [\$server_port, "20081"],
227 "known-clients" => [\$known_clients_file_name, '/var/lib/gosa-si/clients.db' ],
228 "known-servers" => [\$known_server_file_name, '/var/lib/gosa-si/servers.db'],
229 "incoming" => [\$incoming_file_name, '/var/lib/gosa-si/incoming.db'],
230 "login-users" => [\$login_users_file_name, '/var/lib/gosa-si/users.db'],
231 "fai-server" => [\$fai_server_file_name, '/var/lib/gosa-si/fai_server.db'],
232 "fai-release" => [\$fai_release_file_name, '/var/lib/gosa-si/fai_release.db'],
233 "packages-list" => [\$packages_list_file_name, '/var/lib/gosa-si/packages.db'],
234 "messaging" => [\$messaging_file_name, '/var/lib/gosa-si/messaging.db'],
235 "foreign-clients" => [\$foreign_clients_file_name, '/var/lib/gosa-si/foreign_clients.db'],
236 "source-list" => [\$sources_list, '/etc/apt/sources.list'],
237 "repo-path" => [\$repo_path, '/srv/www/repository'],
238 "ldap-uri" => [\$ldap_uri, ""],
239 "ldap-base" => [\$ldap_base, ""],
240 "ldap-admin-dn" => [\$ldap_admin_dn, ""],
241 "ldap-admin-password" => [\$ldap_admin_password, ""],
242 "gosa-unit-tag" => [\$gosa_unit_tag, ""],
243 "max-clients" => [\$max_clients, 10],
244 "wol-password" => [\$wake_on_lan_passwd, ""],
245 },
246 "GOsaPackages" => {
247 "job-queue" => [\$job_queue_file_name, '/var/lib/gosa-si/jobs.db'],
248 "job-queue-loop-delay" => [\$job_queue_loop_delay, 3],
249 "messaging-db-loop-delay" => [\$messaging_db_loop_delay, 3],
250 "key" => [\$GosaPackages_key, "none"],
251 "new-systems-ou" => [\$new_systems_ou, 'ou=workstations,ou=systems'],
252 },
253 "ClientPackages" => {
254 "key" => [\$ClientPackages_key, "none"],
255 "user-date-of-expiry" => [\$logged_in_user_date_of_expiry, 600],
256 },
257 "ServerPackages"=> {
258 "address" => [\$foreign_server_string, ""],
259 "domain" => [\$server_domain, ""],
260 "key" => [\$ServerPackages_key, "none"],
261 "key-lifetime" => [\$foreign_servers_register_delay, 120],
262 "job-synchronization-enabled" => [\$job_synchronization, "true"],
263 "synchronization-loop" => [\$modified_jobs_loop_delay, 5],
264 },
265 "ArpHandler" => {
266 "enabled" => [\$arp_enabled, "true"],
267 "interface" => [\$arp_interface, "all"],
268 },
269 "Opsi" => {
270 "enabled" => [\$opsi_enabled, "false"],
271 "server" => [\$opsi_server, "localhost"],
272 "admin" => [\$opsi_admin, "opsi-admin"],
273 "password" => [\$opsi_password, "secret"],
274 },
276 );
279 #=== FUNCTION ================================================================
280 # NAME: usage
281 # PARAMETERS: nothing
282 # RETURNS: nothing
283 # DESCRIPTION: print out usage text to STDERR
284 #===============================================================================
285 sub usage {
286 print STDERR << "EOF" ;
287 usage: $prg [-hvf] [-c config]
289 -h : this (help) message
290 -c <file> : config file
291 -f : foreground, process will not be forked to background
292 -v : be verbose (multiple to increase verbosity)
293 -no-arp : starts $prg without connection to arp module
295 EOF
296 print "\n" ;
297 }
300 #=== FUNCTION ================================================================
301 # NAME: logging
302 # PARAMETERS: level - string - default 'info'
303 # msg - string -
304 # facility - string - default 'LOG_DAEMON'
305 # RETURNS: nothing
306 # DESCRIPTION: function for logging
307 #===============================================================================
308 sub daemon_log {
309 # log into log_file
310 my( $msg, $level ) = @_;
311 if(not defined $msg) { return }
312 if(not defined $level) { $level = 1 }
313 if(defined $log_file){
314 open(LOG_HANDLE, ">>$log_file");
315 chmod 0600, $log_file;
316 if(not defined open( LOG_HANDLE, ">>$log_file" )) {
317 print STDERR "cannot open $log_file: $!";
318 return
319 }
320 chomp($msg);
321 #$msg =~s/\n//g; # no newlines are allowed in log messages, this is important for later log parsing
322 if($level <= $verbose){
323 my ($seconds, $minutes, $hours, $monthday, $month,
324 $year, $weekday, $yearday, $sommertime) = localtime(time);
325 $hours = $hours < 10 ? $hours = "0".$hours : $hours;
326 $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
327 $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
328 my @monthnames = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
329 $month = $monthnames[$month];
330 $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
331 $year+=1900;
332 my $name = $prg;
334 my $log_msg = "$month $monthday $hours:$minutes:$seconds $name $msg\n";
335 print LOG_HANDLE $log_msg;
336 if( $foreground ) {
337 print STDERR $log_msg;
338 }
339 }
340 close( LOG_HANDLE );
341 }
342 }
345 #=== FUNCTION ================================================================
346 # NAME: check_cmdline_param
347 # PARAMETERS: nothing
348 # RETURNS: nothing
349 # DESCRIPTION: validates commandline parameter
350 #===============================================================================
351 sub check_cmdline_param () {
352 my $err_config;
353 my $err_counter = 0;
354 if(not defined($cfg_file)) {
355 $cfg_file = "/etc/gosa-si/server.conf";
356 if(! -r $cfg_file) {
357 $err_config = "please specify a config file";
358 $err_counter += 1;
359 }
360 }
361 if( $err_counter > 0 ) {
362 &usage( "", 1 );
363 if( defined( $err_config)) { print STDERR "$err_config\n"}
364 print STDERR "\n";
365 exit( -1 );
366 }
367 }
370 #=== FUNCTION ================================================================
371 # NAME: check_pid
372 # PARAMETERS: nothing
373 # RETURNS: nothing
374 # DESCRIPTION: handels pid processing
375 #===============================================================================
376 sub check_pid {
377 $pid = -1;
378 # Check, if we are already running
379 if( open(LOCK_FILE, "<$pid_file") ) {
380 $pid = <LOCK_FILE>;
381 if( defined $pid ) {
382 chomp( $pid );
383 if( -f "/proc/$pid/stat" ) {
384 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
385 if( $stat ) {
386 daemon_log("ERROR: Already running",1);
387 close( LOCK_FILE );
388 exit -1;
389 }
390 }
391 }
392 close( LOCK_FILE );
393 unlink( $pid_file );
394 }
396 # create a syslog msg if it is not to possible to open PID file
397 if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
398 my($msg) = "Couldn't obtain lockfile '$pid_file' ";
399 if (open(LOCK_FILE, '<', $pid_file)
400 && ($pid = <LOCK_FILE>))
401 {
402 chomp($pid);
403 $msg .= "(PID $pid)\n";
404 } else {
405 $msg .= "(unable to read PID)\n";
406 }
407 if( ! ($foreground) ) {
408 openlog( $0, "cons,pid", "daemon" );
409 syslog( "warning", $msg );
410 closelog();
411 }
412 else {
413 print( STDERR " $msg " );
414 }
415 exit( -1 );
416 }
417 }
419 #=== FUNCTION ================================================================
420 # NAME: import_modules
421 # PARAMETERS: module_path - string - abs. path to the directory the modules
422 # are stored
423 # RETURNS: nothing
424 # DESCRIPTION: each file in module_path which ends with '.pm' and activation
425 # state is on is imported by "require 'file';"
426 #===============================================================================
427 sub import_modules {
428 daemon_log(" ", 1);
430 if (not -e $modules_path) {
431 daemon_log("0 ERROR: cannot find directory or directory is not readable: $modules_path", 1);
432 }
434 opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
435 while (defined (my $file = readdir (DIR))) {
436 if (not $file =~ /(\S*?).pm$/) {
437 next;
438 }
439 my $mod_name = $1;
441 # ArpHandler switch
442 if( $file =~ /ArpHandler.pm/ ) {
443 if( $arp_enabled eq "false" ) { next; }
444 }
446 eval { require $file; };
447 if ($@) {
448 daemon_log("0 ERROR: gosa-si-server could not load module $file", 1);
449 daemon_log("$@", 5);
450 } else {
451 my $info = eval($mod_name.'::get_module_info()');
452 # Only load module if get_module_info() returns a non-null object
453 if( $info ) {
454 my ($input_address, $input_key, $event_hash) = @{$info};
455 $known_modules->{$mod_name} = $info;
456 daemon_log("0 INFO: module $mod_name loaded", 5);
457 }
458 }
459 }
461 close (DIR);
462 }
464 #=== FUNCTION ================================================================
465 # NAME: password_check
466 # PARAMETERS: nothing
467 # RETURNS: nothing
468 # DESCRIPTION: escalates an critical error if two modules exist which are avaialable by
469 # the same password
470 #===============================================================================
471 sub password_check {
472 my $passwd_hash = {};
473 while (my ($mod_name, $mod_info) = each %$known_modules) {
474 my $mod_passwd = @$mod_info[1];
475 if (not defined $mod_passwd) { next; }
476 if (not exists $passwd_hash->{$mod_passwd}) {
477 $passwd_hash->{$mod_passwd} = $mod_name;
479 # escalates critical error
480 } else {
481 &daemon_log("0 ERROR: two loaded modules do have the same password. Please modify the 'key'-parameter in config file");
482 &daemon_log("0 ERROR: module='$mod_name' and module='".$passwd_hash->{$mod_passwd}."'");
483 exit( -1 );
484 }
485 }
487 }
490 #=== FUNCTION ================================================================
491 # NAME: sig_int_handler
492 # PARAMETERS: signal - string - signal arose from system
493 # RETURNS: nothing
494 # DESCRIPTION: handels tasks to be done befor signal becomes active
495 #===============================================================================
496 sub sig_int_handler {
497 my ($signal) = @_;
499 # if (defined($ldap_handle)) {
500 # $ldap_handle->disconnect;
501 # }
502 # TODO alle verbliebenden ldap verbindungen aus allen heaps beenden
505 daemon_log("shutting down gosa-si-server", 1);
506 system("kill `ps -C gosa-si-server -o pid=`");
507 }
508 $SIG{INT} = \&sig_int_handler;
511 sub check_key_and_xml_validity {
512 my ($crypted_msg, $module_key, $session_id) = @_;
513 my $msg;
514 my $msg_hash;
515 my $error_string;
516 eval{
517 $msg = &decrypt_msg($crypted_msg, $module_key);
519 if ($msg =~ /<xml>/i){
520 $msg =~ s/\s+/ /g; # just for better daemon_log
521 daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 8);
522 $msg_hash = $xml->XMLin($msg, ForceArray=>1);
524 ##############
525 # check header
526 if( not exists $msg_hash->{'header'} ) { die "no header specified"; }
527 my $header_l = $msg_hash->{'header'};
528 if( 1 > @{$header_l} ) { die 'empty header tag'; }
529 if( 1 < @{$header_l} ) { die 'more than one header specified'; }
530 my $header = @{$header_l}[0];
531 if( 0 == length $header) { die 'empty string in header tag'; }
533 ##############
534 # check source
535 if( not exists $msg_hash->{'source'} ) { die "no source specified"; }
536 my $source_l = $msg_hash->{'source'};
537 if( 1 > @{$source_l} ) { die 'empty source tag'; }
538 if( 1 < @{$source_l} ) { die 'more than one source specified'; }
539 my $source = @{$source_l}[0];
540 if( 0 == length $source) { die 'source error'; }
542 ##############
543 # check target
544 if( not exists $msg_hash->{'target'} ) { die "no target specified"; }
545 my $target_l = $msg_hash->{'target'};
546 if( 1 > @{$target_l} ) { die 'empty target tag'; }
547 }
548 };
549 if($@) {
550 daemon_log("$session_id ERROR: do not understand the message: $@", 1);
551 $msg = undef;
552 $msg_hash = undef;
553 }
555 return ($msg, $msg_hash);
556 }
559 sub check_outgoing_xml_validity {
560 my ($msg, $session_id) = @_;
562 my $msg_hash;
563 eval{
564 $msg_hash = $xml->XMLin($msg, ForceArray=>1);
566 ##############
567 # check header
568 my $header_l = $msg_hash->{'header'};
569 if( 1 != @{$header_l} ) {
570 die 'no or more than one headers specified';
571 }
572 my $header = @{$header_l}[0];
573 if( 0 == length $header) {
574 die 'header has length 0';
575 }
577 ##############
578 # check source
579 my $source_l = $msg_hash->{'source'};
580 if( 1 != @{$source_l} ) {
581 die 'no or more than 1 sources specified';
582 }
583 my $source = @{$source_l}[0];
584 if( 0 == length $source) {
585 die 'source has length 0';
586 }
587 unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
588 $source =~ /^GOSA$/i ) {
589 die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
590 }
592 ##############
593 # check target
594 my $target_l = $msg_hash->{'target'};
595 if( 0 == @{$target_l} ) {
596 die "no targets specified";
597 }
598 foreach my $target (@$target_l) {
599 if( 0 == length $target) {
600 die "target has length 0";
601 }
602 unless( $target =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
603 $target =~ /^GOSA$/i ||
604 $target =~ /^\*$/ ||
605 $target =~ /KNOWN_SERVER/i ||
606 $target =~ /JOBDB/i ||
607 $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 ){
608 die "target '$target' is not a complete ip-address with port or a valid target name or a mac-address";
609 }
610 }
611 };
612 if($@) {
613 daemon_log("$session_id ERROR: outgoing msg is not gosa-si envelope conform: $@", 1);
614 daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 1);
615 $msg_hash = undef;
616 }
618 return ($msg_hash);
619 }
622 sub input_from_known_server {
623 my ($input, $remote_ip, $session_id) = @_ ;
624 my ($msg, $msg_hash, $module);
626 my $sql_statement= "SELECT * FROM known_server";
627 my $query_res = $known_server_db->select_dbentry( $sql_statement );
629 while( my ($hit_num, $hit) = each %{ $query_res } ) {
630 my $host_name = $hit->{hostname};
631 if( not $host_name =~ "^$remote_ip") {
632 next;
633 }
634 my $host_key = $hit->{hostkey};
635 daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 7);
636 daemon_log("$session_id DEBUG: input_from_known_server: host_key: $host_key", 7);
638 # check if module can open msg envelope with module key
639 my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
640 if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
641 daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 7);
642 daemon_log("$@", 8);
643 next;
644 }
645 else {
646 $msg = $tmp_msg;
647 $msg_hash = $tmp_msg_hash;
648 $module = "ServerPackages";
649 last;
650 }
651 }
653 if( (!$msg) || (!$msg_hash) || (!$module) ) {
654 daemon_log("$session_id DEBUG: Incoming message is not from a known server", 7);
655 }
657 return ($msg, $msg_hash, $module);
658 }
661 sub input_from_known_client {
662 my ($input, $remote_ip, $session_id) = @_ ;
663 my ($msg, $msg_hash, $module);
665 my $sql_statement= "SELECT * FROM known_clients";
666 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
667 while( my ($hit_num, $hit) = each %{ $query_res } ) {
668 my $host_name = $hit->{hostname};
669 if( not $host_name =~ /^$remote_ip:\d*$/) {
670 next;
671 }
672 my $host_key = $hit->{hostkey};
673 &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 7);
674 &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 7);
676 # check if module can open msg envelope with module key
677 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
679 if( (!$msg) || (!$msg_hash) ) {
680 &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 7);
681 &daemon_log("$@", 8);
682 next;
683 }
684 else {
685 $module = "ClientPackages";
686 last;
687 }
688 }
690 if( (!$msg) || (!$msg_hash) || (!$module) ) {
691 &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 7);
692 }
694 return ($msg, $msg_hash, $module);
695 }
698 sub input_from_unknown_host {
699 no strict "refs";
700 my ($input, $session_id) = @_ ;
701 my ($msg, $msg_hash, $module);
702 my $error_string;
704 my %act_modules = %$known_modules;
706 while( my ($mod, $info) = each(%act_modules)) {
708 # check a key exists for this module
709 my $module_key = ${$mod."_key"};
710 if( not defined $module_key ) {
711 if( $mod eq 'ArpHandler' ) {
712 next;
713 }
714 daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
715 next;
716 }
717 daemon_log("$session_id DEBUG: $mod: $module_key", 7);
719 # check if module can open msg envelope with module key
720 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
721 if( (not defined $msg) || (not defined $msg_hash) ) {
722 next;
723 }
724 else {
725 $module = $mod;
726 last;
727 }
728 }
730 if( (!$msg) || (!$msg_hash) || (!$module)) {
731 daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 7);
732 }
734 return ($msg, $msg_hash, $module);
735 }
738 sub create_ciphering {
739 my ($passwd) = @_;
740 if((!defined($passwd)) || length($passwd)==0) {
741 $passwd = "";
742 }
743 $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
744 my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
745 my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
746 $my_cipher->set_iv($iv);
747 return $my_cipher;
748 }
751 sub encrypt_msg {
752 my ($msg, $key) = @_;
753 my $my_cipher = &create_ciphering($key);
754 my $len;
755 {
756 use bytes;
757 $len= 16-length($msg)%16;
758 }
759 $msg = "\0"x($len).$msg;
760 $msg = $my_cipher->encrypt($msg);
761 chomp($msg = &encode_base64($msg));
762 # there are no newlines allowed inside msg
763 $msg=~ s/\n//g;
764 return $msg;
765 }
768 sub decrypt_msg {
770 my ($msg, $key) = @_ ;
771 $msg = &decode_base64($msg);
772 my $my_cipher = &create_ciphering($key);
773 $msg = $my_cipher->decrypt($msg);
774 $msg =~ s/\0*//g;
775 return $msg;
776 }
779 sub get_encrypt_key {
780 my ($target) = @_ ;
781 my $encrypt_key;
782 my $error = 0;
784 # target can be in known_server
785 if( not defined $encrypt_key ) {
786 my $sql_statement= "SELECT * FROM known_server WHERE hostname='$target'";
787 my $query_res = $known_server_db->select_dbentry( $sql_statement );
788 while( my ($hit_num, $hit) = each %{ $query_res } ) {
789 my $host_name = $hit->{hostname};
790 if( $host_name ne $target ) {
791 next;
792 }
793 $encrypt_key = $hit->{hostkey};
794 last;
795 }
796 }
798 # target can be in known_client
799 if( not defined $encrypt_key ) {
800 my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$target'";
801 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
802 while( my ($hit_num, $hit) = each %{ $query_res } ) {
803 my $host_name = $hit->{hostname};
804 if( $host_name ne $target ) {
805 next;
806 }
807 $encrypt_key = $hit->{hostkey};
808 last;
809 }
810 }
812 return $encrypt_key;
813 }
816 #=== FUNCTION ================================================================
817 # NAME: open_socket
818 # PARAMETERS: PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
819 # [PeerPort] string necessary if port not appended by PeerAddr
820 # RETURNS: socket IO::Socket::INET
821 # DESCRIPTION: open a socket to PeerAddr
822 #===============================================================================
823 sub open_socket {
824 my ($PeerAddr, $PeerPort) = @_ ;
825 if(defined($PeerPort)){
826 $PeerAddr = $PeerAddr.":".$PeerPort;
827 }
828 my $socket;
829 $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
830 Porto => "tcp",
831 Type => SOCK_STREAM,
832 Timeout => 5,
833 );
834 if(not defined $socket) {
835 return;
836 }
837 # &daemon_log("DEBUG: open_socket: $PeerAddr", 7);
838 return $socket;
839 }
842 #sub get_local_ip_for_remote_ip {
843 # my $remote_ip= shift;
844 # my $result="0.0.0.0";
845 #
846 # if($remote_ip =~ /^(\d\d?\d?\.){3}\d\d?\d?$/) {
847 # if($remote_ip eq "127.0.0.1") {
848 # $result = "127.0.0.1";
849 # } else {
850 # my $PROC_NET_ROUTE= ('/proc/net/route');
851 #
852 # open(PROC_NET_ROUTE, "<$PROC_NET_ROUTE")
853 # or die "Could not open $PROC_NET_ROUTE";
854 #
855 # my @ifs = <PROC_NET_ROUTE>;
856 #
857 # close(PROC_NET_ROUTE);
858 #
859 # # Eat header line
860 # shift @ifs;
861 # chomp @ifs;
862 # foreach my $line(@ifs) {
863 # my ($Iface,$Destination,$Gateway,$Flags,$RefCnt,$Use,$Metric,$Mask,$MTU,$Window,$IRTT)=split(/\s/, $line);
864 # my $destination;
865 # my $mask;
866 # my ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Destination);
867 # $destination= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
868 # ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Mask);
869 # $mask= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
870 # if(new NetAddr::IP($remote_ip)->within(new NetAddr::IP($destination, $mask))) {
871 # # destination matches route, save mac and exit
872 # $result= &get_ip($Iface);
873 # last;
874 # }
875 # }
876 # }
877 # } else {
878 # daemon_log("0 WARNING: get_local_ip_for_remote_ip() was called with a non-ip parameter: '$remote_ip'", 1);
879 # }
880 # return $result;
881 #}
884 sub send_msg_to_target {
885 my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
886 my $error = 0;
887 my $header;
888 my $timestamp = &get_time();
889 my $new_status;
890 my $act_status;
891 my ($sql_statement, $res);
893 if( $msg_header ) {
894 $header = "'$msg_header'-";
895 } else {
896 $header = "";
897 }
899 # Patch the source ip
900 if($msg =~ /<source>0\.0\.0\.0:\d*?<\/source>/) {
901 my $remote_ip = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
902 $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$remote_ip:$2<\/source>/s;
903 }
905 # encrypt xml msg
906 my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
908 # opensocket
909 my $socket = &open_socket($address);
910 if( !$socket ) {
911 daemon_log("$session_id WARNING: cannot send ".$header."msg to $address , host not reachable", 3);
912 $error++;
913 }
915 if( $error == 0 ) {
916 # send xml msg
917 print $socket $crypted_msg."\n";
919 daemon_log("$session_id INFO: send ".$header."msg to $address", 5);
920 daemon_log("$session_id DEBUG: message:\n$msg", 9);
922 }
924 # close socket in any case
925 if( $socket ) {
926 close $socket;
927 }
929 if( $error > 0 ) { $new_status = "down"; }
930 else { $new_status = $msg_header; }
933 # known_clients
934 $sql_statement = "SELECT * FROM $known_clients_tn WHERE hostname='$address'";
935 $res = $known_clients_db->select_dbentry($sql_statement);
936 if( keys(%$res) == 1) {
937 $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
938 if ($act_status eq "down" && $new_status eq "down") {
939 $sql_statement = "DELETE FROM known_clients WHERE hostname='$address'";
940 $res = $known_clients_db->del_dbentry($sql_statement);
941 daemon_log("$session_id WARNING: failed 2x to send msg to host '$address', delete host from known_clients", 3);
942 } else {
943 $sql_statement = "UPDATE known_clients SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
944 $res = $known_clients_db->update_dbentry($sql_statement);
945 if($new_status eq "down"){
946 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
947 } else {
948 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
949 }
950 }
951 }
953 # known_server
954 $sql_statement = "SELECT * FROM $known_server_tn WHERE hostname='$address'";
955 $res = $known_server_db->select_dbentry($sql_statement);
956 if( keys(%$res) == 1) {
957 $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
958 if ($act_status eq "down" && $new_status eq "down") {
959 $sql_statement = "DELETE FROM known_server WHERE hostname='$address'";
960 $res = $known_server_db->del_dbentry($sql_statement);
961 daemon_log("$session_id WARNING: failed 2x to send a message to host '$address', delete host from known_server", 3);
962 }
963 else {
964 $sql_statement = "UPDATE known_server SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
965 $res = $known_server_db->update_dbentry($sql_statement);
966 if($new_status eq "down"){
967 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
968 } else {
969 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
970 }
971 }
972 }
973 return $error;
974 }
977 sub update_jobdb_status_for_send_msgs {
978 my ($answer, $error) = @_;
979 if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
980 my $jobdb_id = $1;
982 # sending msg faild
983 if( $error ) {
984 if (not $answer =~ /<header>trigger_action_reinstall<\/header>/) {
985 my $sql_statement = "UPDATE $job_queue_tn ".
986 "SET status='error', result='can not deliver msg, please consult log file' ".
987 "WHERE id=$jobdb_id";
988 my $res = $job_db->update_dbentry($sql_statement);
989 }
991 # sending msg was successful
992 } else {
993 my $sql_statement = "UPDATE $job_queue_tn ".
994 "SET status='done' ".
995 "WHERE id=$jobdb_id AND status='processed'";
996 my $res = $job_db->update_dbentry($sql_statement);
997 }
998 }
999 }
1002 sub sig_handler {
1003 my ($kernel, $signal) = @_[KERNEL, ARG0] ;
1004 daemon_log("0 INFO got signal '$signal'", 1);
1005 $kernel->sig_handled();
1006 return;
1007 }
1010 sub msg_to_decrypt {
1011 my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
1012 my $session_id = $session->ID;
1013 my ($msg, $msg_hash, $module);
1014 my $error = 0;
1016 # hole neue msg aus @msgs_to_decrypt
1017 my $next_msg = shift @msgs_to_decrypt;
1019 # entschlüssle sie
1021 # msg is from a new client or gosa
1022 ($msg, $msg_hash, $module) = &input_from_unknown_host($next_msg, $session_id);
1023 # msg is from a gosa-si-server
1024 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1025 ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $heap->{'remote_ip'}, $session_id);
1026 }
1027 # msg is from a gosa-si-client
1028 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1029 ($msg, $msg_hash, $module) = &input_from_known_client($next_msg, $heap->{'remote_ip'}, $session_id);
1030 }
1031 # an error occurred
1032 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1033 # if an incoming msg could not be decrypted (maybe a wrong key), send client a ping. If the client
1034 # could not understand a msg from its server the client cause a re-registering process
1035 daemon_log("$session_id WARNING cannot understand incoming msg, send 'ping'-msg to all host with ip '".$heap->{remote_ip}.
1036 "' to cause a re-registering of the client if necessary", 3);
1037 my $sql_statement = "SELECT * FROM $main::known_clients_tn WHERE (hostname LIKE '".$heap->{'remote_ip'}."%')";
1038 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1039 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1040 my $host_name = $hit->{'hostname'};
1041 my $host_key = $hit->{'hostkey'};
1042 my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source> <target>$host_name</target></xml>";
1043 my $error = &send_msg_to_target($ping_msg, $host_name, $host_key, "gosa_ping", $session_id);
1044 &update_jobdb_status_for_send_msgs($ping_msg, $error);
1045 }
1046 $error++;
1047 }
1050 my $header;
1051 my $target;
1052 my $source;
1053 my $done = 0;
1054 my $sql;
1055 my $res;
1057 # check whether this message should be processed here
1058 if ($error == 0) {
1059 $header = @{$msg_hash->{'header'}}[0];
1060 $target = @{$msg_hash->{'target'}}[0];
1061 $source = @{$msg_hash->{'source'}}[0];
1062 my $not_found_in_known_clients_db = 0;
1063 my $not_found_in_known_server_db = 0;
1064 my $not_found_in_foreign_clients_db = 0;
1065 my $local_address;
1066 my $local_mac;
1067 my ($target_ip, $target_port) = split(':', $target);
1069 # Determine the local ip address if target is an ip address
1070 if ($target =~ /^\d+\.\d+\.\d+\.\d+:\d+$/) {
1071 $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1072 } else {
1073 $local_address = $server_address;
1074 }
1076 # Determine the local mac address if target is a mac address
1077 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) {
1078 my $loc_ip = &get_local_ip_for_remote_ip($heap->{'remote_ip'});
1079 my $network_interface= &get_interface_for_ip($loc_ip);
1080 $local_mac = &get_mac_for_interface($network_interface);
1081 } else {
1082 $local_mac = $server_mac_address;
1083 }
1085 # target and source is equal to GOSA -> process here
1086 if (not $done) {
1087 if ($target eq "GOSA" && $source eq "GOSA") {
1088 $done = 1;
1089 }
1090 }
1092 # target is own address without forward_to_gosa-tag -> process here
1093 if (not $done) {
1094 #if ((($target eq $local_address) || ($target eq $local_mac) ) && (not exists $msg_hash->{'forward_to_gosa'})) {
1095 if (($target eq $local_address) && (not exists $msg_hash->{'forward_to_gosa'})) {
1096 $done = 1;
1097 if ($source eq "GOSA") {
1098 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1099 }
1100 #print STDERR "target is own address without forward_to_gosa-tag -> process here\n";
1101 }
1102 }
1104 # target is a client address in known_clients -> process here
1105 if (not $done) {
1106 $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1107 $res = $known_clients_db->select_dbentry($sql);
1108 if (keys(%$res) > 0) {
1109 $done = 1;
1110 my $hostname = $res->{1}->{'hostname'};
1111 $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1112 #print STDERR "target is a client address in known_clients -> process here\n";
1113 my $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1114 if ($source eq "GOSA") {
1115 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1116 }
1118 } else {
1119 $not_found_in_known_clients_db = 1;
1120 }
1121 }
1123 # target ist own address with forward_to_gosa-tag not pointing to myself -> process here
1124 if (not $done) {
1125 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1126 my $gosa_at;
1127 my $gosa_session_id;
1128 if (($target eq $local_address) && (defined $forward_to_gosa)){
1129 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1130 if ($gosa_at ne $local_address) {
1131 $done = 1;
1132 #print STDERR "target is own address with forward_to_gosa-tag not pointing to myself -> process here\n";
1133 }
1134 }
1135 }
1137 # if message should be processed here -> add message to incoming_db
1138 if ($done) {
1139 # if a job or a gosa message comes from a foreign server, fake module to GosaPackages
1140 # so gosa-si-server knows how to process this kind of messages
1141 if ($header =~ /^gosa_/ || $header =~ /^job_/) {
1142 $module = "GosaPackages";
1143 }
1145 my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1146 primkey=>[],
1147 headertag=>$header,
1148 targettag=>$target,
1149 xmlmessage=>&encode_base64($msg),
1150 timestamp=>&get_time,
1151 module=>$module,
1152 sessionid=>$session_id,
1153 } );
1154 }
1156 # target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa
1157 if (not $done) {
1158 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1159 my $gosa_at;
1160 my $gosa_session_id;
1161 if (($target eq $local_address) && (defined $forward_to_gosa)){
1162 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1163 if ($gosa_at eq $local_address) {
1164 my $session_reference = $kernel->ID_id_to_session($gosa_session_id);
1165 if( defined $session_reference ) {
1166 $heap = $session_reference->get_heap();
1167 }
1168 if(exists $heap->{'client'}) {
1169 $msg = &encrypt_msg($msg, $GosaPackages_key);
1170 $heap->{'client'}->put($msg);
1171 &daemon_log("$session_id INFO: incoming '$header' message forwarded to GOsa", 5);
1172 }
1173 $done = 1;
1174 #print STDERR "target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa\n";
1175 }
1176 }
1178 }
1180 # target is a client address in foreign_clients -> forward to registration server
1181 if (not $done) {
1182 $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1183 $res = $foreign_clients_db->select_dbentry($sql);
1184 if (keys(%$res) > 0) {
1185 my $hostname = $res->{1}->{'hostname'};
1186 my ($host_ip, $host_port) = split(/:/, $hostname);
1187 my $local_address = &get_local_ip_for_remote_ip($host_ip).":$server_port";
1188 my $regserver = $res->{1}->{'regserver'};
1189 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$regserver'";
1190 my $res = $known_server_db->select_dbentry($sql);
1191 if (keys(%$res) > 0) {
1192 my $regserver_key = $res->{1}->{'hostkey'};
1193 $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1194 $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1195 if ($source eq "GOSA") {
1196 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1197 }
1198 &send_msg_to_target($msg, $regserver, $regserver_key, $header, $session_id);
1199 }
1200 $done = 1;
1201 #print STDERR "target is a client address in foreign_clients -> forward to registration server\n";
1202 } else {
1203 $not_found_in_foreign_clients_db = 1;
1204 }
1205 }
1207 # target is a server address -> forward to server
1208 if (not $done) {
1209 $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1210 $res = $known_server_db->select_dbentry($sql);
1211 if (keys(%$res) > 0) {
1212 my $hostkey = $res->{1}->{'hostkey'};
1214 if ($source eq "GOSA") {
1215 $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1216 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1218 }
1220 &send_msg_to_target($msg, $target, $hostkey, $header, $session_id);
1221 $done = 1;
1222 } else {
1223 $not_found_in_known_server_db = 1;
1224 }
1225 }
1228 # target is not in foreign_clients_db, known_server_db or known_clients_db, maybe it is a complete new one -> process here
1229 if ( $not_found_in_foreign_clients_db
1230 && $not_found_in_known_server_db
1231 && $not_found_in_known_clients_db) {
1232 my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1233 primkey=>[],
1234 headertag=>$header,
1235 targettag=>$target,
1236 xmlmessage=>&encode_base64($msg),
1237 timestamp=>&get_time,
1238 module=>$module,
1239 sessionid=>$session_id,
1240 } );
1241 $done = 1;
1242 }
1245 if (not $done) {
1246 daemon_log("$session_id ERROR: do not know what to do with this message: $msg", 1);
1247 if ($source eq "GOSA") {
1248 my %data = ('error_msg' => &encode_base64($msg), 'error_string' => "Do not know what to do with this message!");
1249 my $error_msg = &build_msg("error", $local_address, "GOSA", \%data );
1251 my $session_reference = $kernel->ID_id_to_session($session_id);
1252 if( defined $session_reference ) {
1253 $heap = $session_reference->get_heap();
1254 }
1255 if(exists $heap->{'client'}) {
1256 $error_msg = &encrypt_msg($error_msg, $GosaPackages_key);
1257 $heap->{'client'}->put($error_msg);
1258 }
1259 }
1260 }
1262 }
1264 return;
1265 }
1268 sub next_task {
1269 my ($session, $heap, $task) = @_[SESSION, HEAP, ARG0];
1270 my $running_task = POE::Wheel::Run->new(
1271 Program => sub { process_task($session, $heap, $task) },
1272 StdioFilter => POE::Filter::Reference->new(),
1273 StdoutEvent => "task_result",
1274 StderrEvent => "task_debug",
1275 CloseEvent => "task_done",
1276 );
1277 $heap->{task}->{ $running_task->ID } = $running_task;
1278 }
1280 sub handle_task_result {
1281 my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
1282 my $client_answer = $result->{'answer'};
1283 if( $client_answer =~ s/session_id=(\d+)$// ) {
1284 my $session_id = $1;
1285 if( defined $session_id ) {
1286 my $session_reference = $kernel->ID_id_to_session($session_id);
1287 if( defined $session_reference ) {
1288 $heap = $session_reference->get_heap();
1289 }
1290 }
1292 if(exists $heap->{'client'}) {
1293 $heap->{'client'}->put($client_answer);
1294 }
1295 }
1296 $kernel->sig(CHLD => "child_reap");
1297 }
1299 sub handle_task_debug {
1300 my $result = $_[ARG0];
1301 print STDERR "$result\n";
1302 }
1304 sub handle_task_done {
1305 my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1306 delete $heap->{task}->{$task_id};
1307 }
1309 sub process_task {
1310 no strict "refs";
1311 #CHECK: Not @_[...]?
1312 my ($session, $heap, $task) = @_;
1313 my $error = 0;
1314 my $answer_l;
1315 my ($answer_header, @answer_target_l, $answer_source);
1316 my $client_answer = "";
1318 # prepare all variables needed to process message
1319 #my $msg = $task->{'xmlmessage'};
1320 my $msg = &decode_base64($task->{'xmlmessage'});
1321 my $incoming_id = $task->{'id'};
1322 my $module = $task->{'module'};
1323 my $header = $task->{'headertag'};
1324 my $session_id = $task->{'sessionid'};
1325 my $msg_hash = $xml->XMLin($msg, ForceArray=>1);
1326 my $source = @{$msg_hash->{'source'}}[0];
1328 # set timestamp of incoming client uptodate, so client will not
1329 # be deleted from known_clients because of expiration
1330 my $act_time = &get_time();
1331 my $sql = "UPDATE $known_clients_tn SET timestamp='$act_time' WHERE hostname='$source'";
1332 my $res = $known_clients_db->exec_statement($sql);
1334 ######################
1335 # process incoming msg
1336 if( $error == 0) {
1337 daemon_log("$session_id INFO: Incoming msg (session_id=$session_id) with header '".@{$msg_hash->{'header'}}[0]."'", 5);
1338 daemon_log("$session_id DEBUG: Processing module ".$module, 7);
1339 $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1341 if ( 0 < @{$answer_l} ) {
1342 my $answer_str = join("\n", @{$answer_l});
1343 while ($answer_str =~ /<header>(\w+)<\/header>/g) {
1344 daemon_log("$session_id INFO: got answer message with header '$1'", 5);
1345 }
1346 daemon_log("$session_id DEBUG: $module: got answer from module: \n".$answer_str,9);
1347 } else {
1348 daemon_log("$session_id DEBUG: $module: got no answer from module!" ,7);
1349 }
1351 }
1352 if( !$answer_l ) { $error++ };
1354 ########
1355 # answer
1356 if( $error == 0 ) {
1358 foreach my $answer ( @{$answer_l} ) {
1359 # check outgoing msg to xml validity
1360 my $answer_hash = &check_outgoing_xml_validity($answer, $session_id);
1361 if( not defined $answer_hash ) { next; }
1363 $answer_header = @{$answer_hash->{'header'}}[0];
1364 @answer_target_l = @{$answer_hash->{'target'}};
1365 $answer_source = @{$answer_hash->{'source'}}[0];
1367 # deliver msg to all targets
1368 foreach my $answer_target ( @answer_target_l ) {
1370 # targets of msg are all gosa-si-clients in known_clients_db
1371 if( $answer_target eq "*" ) {
1372 # answer is for all clients
1373 my $sql_statement= "SELECT * FROM known_clients";
1374 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1375 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1376 my $host_name = $hit->{hostname};
1377 my $host_key = $hit->{hostkey};
1378 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1379 &update_jobdb_status_for_send_msgs($answer, $error);
1380 }
1381 }
1383 # targets of msg are all gosa-si-server in known_server_db
1384 elsif( $answer_target eq "KNOWN_SERVER" ) {
1385 # answer is for all server in known_server
1386 my $sql_statement= "SELECT * FROM $known_server_tn";
1387 my $query_res = $known_server_db->select_dbentry( $sql_statement );
1388 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1389 my $host_name = $hit->{hostname};
1390 my $host_key = $hit->{hostkey};
1391 $answer =~ s/<target>\S+<\/target>/<target>$host_name<\/target>/g;
1392 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1393 &update_jobdb_status_for_send_msgs($answer, $error);
1394 }
1395 }
1397 # target of msg is GOsa
1398 elsif( $answer_target eq "GOSA" ) {
1399 my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1400 my $add_on = "";
1401 if( defined $session_id ) {
1402 $add_on = ".session_id=$session_id";
1403 }
1404 # answer is for GOSA and has to returned to connected client
1405 my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1406 $client_answer = $gosa_answer.$add_on;
1407 }
1409 # target of msg is job queue at this host
1410 elsif( $answer_target eq "JOBDB") {
1411 $answer =~ /<header>(\S+)<\/header>/;
1412 my $header;
1413 if( defined $1 ) { $header = $1; }
1414 my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1415 &update_jobdb_status_for_send_msgs($answer, $error);
1416 }
1418 # Target of msg is a mac address
1419 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 ) {
1420 daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients and foreign_clients", 5);
1421 my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1422 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1423 my $found_ip_flag = 0;
1424 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1425 my $host_name = $hit->{hostname};
1426 my $host_key = $hit->{hostkey};
1427 $answer =~ s/$answer_target/$host_name/g;
1428 daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1429 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1430 &update_jobdb_status_for_send_msgs($answer, $error);
1431 $found_ip_flag++ ;
1432 }
1433 if ($found_ip_flag == 0) {
1434 my $sql = "SELECT * FROM $foreign_clients_tn WHERE macaddress LIKE '$answer_target'";
1435 my $res = $foreign_clients_db->select_dbentry($sql);
1436 while( my ($hit_num, $hit) = each %{ $res } ) {
1437 my $host_name = $hit->{hostname};
1438 my $reg_server = $hit->{regserver};
1439 daemon_log("$session_id INFO: found host '$host_name' with mac '$answer_target', registered at '$reg_server'", 5);
1441 # Fetch key for reg_server
1442 my $reg_server_key;
1443 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$reg_server'";
1444 my $res = $known_server_db->select_dbentry($sql);
1445 if (exists $res->{1}) {
1446 $reg_server_key = $res->{1}->{'hostkey'};
1447 } else {
1448 daemon_log("$session_id ERROR: cannot find hostkey for '$host_name' in '$known_server_tn'", 1);
1449 daemon_log("$session_id ERROR: unable to forward answer to correct registration server, processing is aborted!", 1);
1450 $reg_server_key = undef;
1451 }
1453 # Send answer to server where client is registered
1454 if (defined $reg_server_key) {
1455 $answer =~ s/$answer_target/$host_name/g;
1456 my $error = &send_msg_to_target($answer, $reg_server, $reg_server_key, $answer_header, $session_id);
1457 &update_jobdb_status_for_send_msgs($answer, $error);
1458 $found_ip_flag++ ;
1459 }
1460 }
1461 }
1462 if( $found_ip_flag == 0) {
1463 daemon_log("$session_id WARNING: no host found in known_clients with mac address '$answer_target'", 3);
1464 }
1466 # Answer is for one specific host
1467 } else {
1468 # get encrypt_key
1469 my $encrypt_key = &get_encrypt_key($answer_target);
1470 if( not defined $encrypt_key ) {
1471 # unknown target
1472 daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1473 next;
1474 }
1475 my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1476 &update_jobdb_status_for_send_msgs($answer, $error);
1477 }
1478 }
1479 }
1480 }
1482 my $filter = POE::Filter::Reference->new();
1483 my %result = (
1484 status => "seems ok to me",
1485 answer => $client_answer,
1486 );
1488 my $output = $filter->put( [ \%result ] );
1489 print @$output;
1492 }
1494 sub session_start {
1495 my ($kernel) = $_[KERNEL];
1496 $global_kernel = $kernel;
1497 $kernel->yield('register_at_foreign_servers');
1498 $kernel->yield('create_fai_server_db', $fai_server_tn );
1499 $kernel->yield('create_fai_release_db', $fai_release_tn );
1500 $kernel->yield('watch_for_next_tasks');
1501 $kernel->sig(USR1 => "sig_handler");
1502 $kernel->sig(USR2 => "recreate_packages_db");
1503 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1504 $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay);
1505 $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay);
1506 $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1507 $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1508 $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1509 $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1511 # Start opsi check
1512 if ($opsi_enabled eq "true") {
1513 $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay);
1514 }
1516 }
1519 sub watch_for_done_jobs {
1520 #CHECK: $heap for what?
1521 my ($kernel,$heap) = @_[KERNEL, HEAP];
1523 my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((status='done') AND (modified='0'))";
1524 my $res = $job_db->select_dbentry( $sql_statement );
1526 while( my ($id, $hit) = each %{$res} ) {
1527 my $jobdb_id = $hit->{id};
1528 my $sql_statement = "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1529 my $res = $job_db->del_dbentry($sql_statement);
1530 }
1532 $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1533 }
1536 sub watch_for_opsi_jobs {
1537 my ($kernel) = $_[KERNEL];
1539 # 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
1540 # opsi install job is to parse the xml message. There is still the correct header.
1541 my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((xmlmessage LIKE '%opsi_install_client</header>%') AND (status='processing') AND (siserver='localhost'))";
1542 my $res = $job_db->select_dbentry( $sql_statement );
1544 # Ask OPSI for an update of the running jobs
1545 while (my ($id, $hit) = each %$res ) {
1546 # Determine current parameters of the job
1547 my $hostId = $hit->{'plainname'};
1548 my $macaddress = $hit->{'macaddress'};
1549 my $progress = $hit->{'progress'};
1551 my $result= {};
1553 # For hosts, only return the products that are or get installed
1554 my $callobj;
1555 $callobj = {
1556 method => 'getProductStates_hash',
1557 params => [ $hostId ],
1558 id => 1,
1559 };
1561 my $hres = $opsi_client->call($opsi_url, $callobj);
1562 #my ($hres_err, $hres_err_string) = &check_opsi_res($hres);
1563 if (not &check_opsi_res($hres)) {
1564 my $htmp= $hres->result->{$hostId};
1566 # Check state != not_installed or action == setup -> load and add
1567 my $products= 0;
1568 my $installed= 0;
1569 my $installing = 0;
1570 my $error= 0;
1571 my @installed_list;
1572 my @error_list;
1573 my $act_status = "none";
1574 foreach my $product (@{$htmp}){
1576 if ($product->{'installationStatus'} ne "not_installed" or
1577 $product->{'actionRequest'} eq "setup"){
1579 # Increase number of products for this host
1580 $products++;
1582 if ($product->{'installationStatus'} eq "failed"){
1583 $result->{$product->{'productId'}}= "error";
1584 unshift(@error_list, $product->{'productId'});
1585 $error++;
1586 }
1587 if ($product->{'installationStatus'} eq "installed" && $product->{'actionRequest'} eq "none"){
1588 $result->{$product->{'productId'}}= "installed";
1589 unshift(@installed_list, $product->{'productId'});
1590 $installed++;
1591 }
1592 if ($product->{'installationStatus'} eq "installing"){
1593 $result->{$product->{'productId'}}= "installing";
1594 $installing++;
1595 $act_status = "installing - ".$product->{'productId'};
1596 }
1597 }
1598 }
1600 # Estimate "rough" progress, avoid division by zero
1601 if ($products == 0) {
1602 $result->{'progress'}= 0;
1603 } else {
1604 $result->{'progress'}= int($installed * 100 / $products);
1605 }
1607 # Set updates in job queue
1608 if ((not $error) && (not $installing) && ($installed)) {
1609 $act_status = "installed - ".join(", ", @installed_list);
1610 }
1611 if ($error) {
1612 $act_status = "error - ".join(", ", @error_list);
1613 }
1614 if ($progress ne $result->{'progress'} ) {
1615 # Updating progress and result
1616 my $update_statement = "UPDATE $job_queue_tn SET modified='1', progress='".$result->{'progress'}."', result='$act_status' WHERE macaddress='$macaddress' AND siserver='localhost'";
1617 my $update_res = $job_db->update_dbentry($update_statement);
1618 }
1619 if ($progress eq 100) {
1620 # Updateing status
1621 my $done_statement = "UPDATE $job_queue_tn SET modified='1', ";
1622 if ($error) {
1623 $done_statement .= "status='error'";
1624 } else {
1625 $done_statement .= "status='done'";
1626 }
1627 $done_statement .= " WHERE macaddress='$macaddress' AND siserver='localhost'";
1628 my $done_res = $job_db->update_dbentry($done_statement);
1629 }
1632 }
1633 }
1635 $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay);
1636 }
1639 # If a job got an update or was modified anyway, send to all other si-server an update message of this jobs.
1640 sub watch_for_modified_jobs {
1641 my ($kernel,$heap) = @_[KERNEL, HEAP];
1643 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE ((siserver='localhost') AND (modified='1'))";
1644 my $res = $job_db->select_dbentry( $sql_statement );
1646 # if db contains no jobs which should be update, do nothing
1647 if (keys %$res != 0) {
1649 if ($job_synchronization eq "true") {
1650 # make out of the db result a gosa-si message
1651 my $update_msg = &db_res2si_msg ($res, "foreign_job_updates", "KNOWN_SERVER", "MY_LOCAL_ADDRESS");
1653 # update all other SI-server
1654 &inform_all_other_si_server($update_msg);
1655 }
1657 # set jobs all jobs to modified = 0, wait until the next modification for updates of other si-server
1658 $sql_statement = "UPDATE $job_queue_tn SET modified='0' ";
1659 $res = $job_db->update_dbentry($sql_statement);
1660 }
1662 $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay);
1663 }
1666 sub watch_for_new_jobs {
1667 if($watch_for_new_jobs_in_progress == 0) {
1668 $watch_for_new_jobs_in_progress = 1;
1669 my ($kernel,$heap) = @_[KERNEL, HEAP];
1671 # check gosa job quaeue for jobs with executable timestamp
1672 my $timestamp = &get_time();
1673 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE status='waiting' AND (CAST (timestamp AS INTEGER)) < $timestamp ORDER BY timestamp";
1674 my $res = $job_db->exec_statement( $sql_statement );
1676 # Merge all new jobs that would do the same actions
1677 my @drops;
1678 my $hits;
1679 foreach my $hit (reverse @{$res} ) {
1680 my $macaddress= lc @{$hit}[8];
1681 my $headertag= @{$hit}[5];
1682 if(
1683 defined($hits->{$macaddress}) &&
1684 defined($hits->{$macaddress}->{$headertag}) &&
1685 defined($hits->{$macaddress}->{$headertag}[0])
1686 ) {
1687 push @drops, "DELETE FROM $job_queue_tn WHERE id = $hits->{$macaddress}->{$headertag}[0]";
1688 }
1689 $hits->{$macaddress}->{$headertag}= $hit;
1690 }
1692 # Delete new jobs with a matching job in state 'processing'
1693 foreach my $macaddress (keys %{$hits}) {
1694 foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1695 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1696 if(defined($jobdb_id)) {
1697 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND headertag='$jobdb_headertag' AND status='processing'";
1698 my $res = $job_db->exec_statement( $sql_statement );
1699 foreach my $hit (@{$res}) {
1700 push @drops, "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1701 }
1702 } else {
1703 daemon_log("J ERROR: Job without id exists for macaddress $macaddress!", 1);
1704 }
1705 }
1706 }
1708 # Commit deletion
1709 $job_db->exec_statementlist(\@drops);
1711 # Look for new jobs that could be executed
1712 foreach my $macaddress (keys %{$hits}) {
1714 # Look if there is an executing job
1715 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='processing'";
1716 my $res = $job_db->exec_statement( $sql_statement );
1718 # Skip new jobs for host if there is a processing job
1719 if(defined($res) and defined @{$res}[0]) {
1720 # Prevent race condition if there is a trigger_activate job waiting and a goto-activation job processing
1721 if(@{$res}[5] eq 'goto-activation') {
1722 my $sql_statement_2 = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='waiting' AND headertag = 'trigger_activate_new'";
1723 my $res_2 = $job_db->exec_statement( $sql_statement_2 );
1724 if(defined($res_2) and defined @{$res}[0]) {
1725 # Set status from goto-activation to 'waiting' and update timestamp
1726 $job_db->exec_statement = "UPDATE $job_queue_tn SET status='waiting' WHERE macaddress LIKE '$macaddress' AND headertag = 'trigger_action_reinstall'";
1727 $job_db->exec_statement = "UPDATE $job_queue_tn SET timestamp='".&get_time(30)."' WHERE macaddress LIKE '$macaddress' AND headertag = 'trigger_action_reinstall'";
1728 next;
1729 }
1730 }
1731 }
1733 foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1734 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1735 if(defined($jobdb_id)) {
1736 my $job_msg = @{$hits->{$macaddress}->{$jobdb_headertag}}[7];
1738 daemon_log("J DEBUG: its time to execute $job_msg", 7);
1739 my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1740 my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1742 # expect macaddress is unique!!!!!!
1743 my $target = $res_hash->{1}->{hostname};
1745 # change header
1746 $job_msg =~ s/<header>job_/<header>gosa_/;
1748 # add sqlite_id
1749 $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1751 $job_msg =~ /<header>(\S+)<\/header>/;
1752 my $header = $1 ;
1753 my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");
1755 # update status in job queue to 'processing'
1756 $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
1757 my $res = $job_db->update_dbentry($sql_statement);
1758 # TODO: abfangen ob alles in ordnung ist oder nicht, wenn nicht error schmeißen
1760 # We don't want parallel processing
1761 last;
1762 }
1763 }
1764 }
1766 $watch_for_new_jobs_in_progress = 0;
1767 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1768 }
1769 }
1772 sub watch_for_new_messages {
1773 my ($kernel,$heap) = @_[KERNEL, HEAP];
1774 my @coll_user_msg; # collection list of outgoing messages
1776 # check messaging_db for new incoming messages with executable timestamp
1777 my $timestamp = &get_time();
1778 my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS INTEGER))<$timestamp AND flag='n' AND direction='in' )";
1779 my $res = $messaging_db->exec_statement( $sql_statement );
1780 foreach my $hit (@{$res}) {
1782 # create outgoing messages
1783 my $message_to = @{$hit}[3];
1784 # translate message_to to plain login name
1785 my @message_to_l = split(/,/, $message_to);
1786 my %receiver_h;
1787 foreach my $receiver (@message_to_l) {
1788 if ($receiver =~ /^u_([\s\S]*)$/) {
1789 $receiver_h{$1} = 0;
1790 } elsif ($receiver =~ /^g_([\s\S]*)$/) {
1791 my $group_name = $1;
1792 # fetch all group members from ldap and add them to receiver hash
1793 my $ldap_handle = &get_ldap_handle();
1794 if (defined $ldap_handle) {
1795 my $mesg = $ldap_handle->search(
1796 base => $ldap_base,
1797 scope => 'sub',
1798 attrs => ['memberUid'],
1799 filter => "cn=$group_name",
1800 );
1801 if ($mesg->count) {
1802 my @entries = $mesg->entries;
1803 foreach my $entry (@entries) {
1804 my @receivers= $entry->get_value("memberUid");
1805 foreach my $receiver (@receivers) {
1806 $receiver_h{$1} = 0;
1807 }
1808 }
1809 }
1810 # translating errors ?
1811 if ($mesg->code) {
1812 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: $mesg->error", 1);
1813 }
1814 # ldap handle error ?
1815 } else {
1816 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: no ldap handle available", 1);
1817 }
1818 } else {
1819 my $sbjct = &encode_base64(@{$hit}[1]);
1820 my $msg = &encode_base64(@{$hit}[7]);
1821 &daemon_log("M WARNING: unknown receiver '$receiver' for a user-message '$sbjct - $msg'", 3);
1822 }
1823 }
1824 my @receiver_l = keys(%receiver_h);
1826 my $message_id = @{$hit}[0];
1828 #add each outgoing msg to messaging_db
1829 my $receiver;
1830 foreach $receiver (@receiver_l) {
1831 my $sql_statement = "INSERT INTO $messaging_tn (id, subject, message_from, message_to, flag, direction, delivery_time, message, timestamp) ".
1832 "VALUES ('".
1833 $message_id."', '". # id
1834 @{$hit}[1]."', '". # subject
1835 @{$hit}[2]."', '". # message_from
1836 $receiver."', '". # message_to
1837 "none"."', '". # flag
1838 "out"."', '". # direction
1839 @{$hit}[6]."', '". # delivery_time
1840 @{$hit}[7]."', '". # message
1841 $timestamp."'". # timestamp
1842 ")";
1843 &daemon_log("M DEBUG: $sql_statement", 1);
1844 my $res = $messaging_db->exec_statement($sql_statement);
1845 &daemon_log("M INFO: message '".@{$hit}[0]."' is prepared for delivery to receiver '$receiver'", 5);
1846 }
1848 # set incoming message to flag d=deliverd
1849 $sql_statement = "UPDATE $messaging_tn SET flag='p' WHERE id='$message_id'";
1850 &daemon_log("M DEBUG: $sql_statement", 7);
1851 $res = $messaging_db->update_dbentry($sql_statement);
1852 &daemon_log("M INFO: message '$message_id' is set to flag 'p' (processed)", 5);
1853 }
1855 $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1856 return;
1857 }
1859 sub watch_for_delivery_messages {
1860 my ($kernel, $heap) = @_[KERNEL, HEAP];
1862 # select outgoing messages
1863 my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( flag='p' AND direction='out' )";
1864 #&daemon_log("0 DEBUG: $sql", 7);
1865 my $res = $messaging_db->exec_statement( $sql_statement );
1867 # build out msg for each usr
1868 foreach my $hit (@{$res}) {
1869 my $receiver = @{$hit}[3];
1870 my $msg_id = @{$hit}[0];
1871 my $subject = @{$hit}[1];
1872 my $message = @{$hit}[7];
1874 # resolve usr -> host where usr is logged in
1875 my $sql = "SELECT * FROM $login_users_tn WHERE (user='$receiver')";
1876 #&daemon_log("0 DEBUG: $sql", 7);
1877 my $res = $login_users_db->exec_statement($sql);
1879 # reciver is logged in nowhere
1880 if (not ref(@$res[0]) eq "ARRAY") { next; }
1882 my $send_succeed = 0;
1883 foreach my $hit (@$res) {
1884 my $receiver_host = @$hit[0];
1885 my $delivered2host = 0;
1886 &daemon_log("M DEBUG: user '$receiver' is logged in at host '$receiver_host'", 7);
1888 # Looking for host in know_clients_db
1889 my $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$receiver_host')";
1890 my $res = $known_clients_db->exec_statement($sql);
1892 # Host is known in known_clients_db
1893 if (ref(@$res[0]) eq "ARRAY") {
1894 my $receiver_key = @{@{$res}[0]}[2];
1895 my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
1896 my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data );
1897 my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0);
1898 if ($error == 0 ) {
1899 $send_succeed++ ;
1900 $delivered2host++ ;
1901 &daemon_log("M DEBUG: send message for user '$receiver' to host '$receiver_host'", 7);
1902 } else {
1903 &daemon_log("M DEBUG: cannot send message for user '$receiver' to host '$receiver_host'", 7);
1904 }
1905 }
1907 # Message already send, do not need to do anything more, otherwise ...
1908 if ($delivered2host) { next;}
1910 # ...looking for host in foreign_clients_db
1911 $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$receiver_host')";
1912 $res = $foreign_clients_db->exec_statement($sql);
1914 # Host is known in foreign_clients_db
1915 if (ref(@$res[0]) eq "ARRAY") {
1916 my $registration_server = @{@{$res}[0]}[2];
1918 # Fetch encryption key for registration server
1919 my $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$registration_server')";
1920 my $res = $known_server_db->exec_statement($sql);
1921 if (ref(@$res[0]) eq "ARRAY") {
1922 my $registration_server_key = @{@{$res}[0]}[3];
1923 my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
1924 my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data );
1925 my $error = &send_msg_to_target($out_msg, $registration_server, $registration_server_key, "usr_msg", 0);
1926 if ($error == 0 ) {
1927 $send_succeed++ ;
1928 $delivered2host++ ;
1929 &daemon_log("M DEBUG: send message for user '$receiver' to server '$registration_server'", 7);
1930 } else {
1931 &daemon_log("M ERROR: cannot send message for user '$receiver' to server '$registration_server'", 1);
1932 }
1934 } else {
1935 &daemon_log("M ERROR: host '$receiver_host' is reported to be ".
1936 "registrated at server '$registration_server', ".
1937 "but no data available in known_server_db ", 1);
1938 }
1939 }
1941 if (not $delivered2host) {
1942 &daemon_log("M ERROR: unable to send user message to host '$receiver_host'", 1);
1943 }
1944 }
1946 if ($send_succeed) {
1947 # set outgoing msg at db to deliverd
1948 my $sql = "UPDATE $messaging_tn SET flag='d' WHERE (id='$msg_id' AND direction='out' AND message_to='$receiver')";
1949 my $res = $messaging_db->exec_statement($sql);
1950 &daemon_log("M INFO: send message for user '$receiver' to logged in hosts", 5);
1951 } else {
1952 &daemon_log("M WARNING: failed to deliver message for user '$receiver'", 3);
1953 }
1954 }
1956 $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1957 return;
1958 }
1961 sub watch_for_done_messages {
1962 my ($kernel,$heap) = @_[KERNEL, HEAP];
1964 my $sql = "SELECT * FROM $messaging_tn WHERE (flag='p' AND direction='in')";
1965 #&daemon_log("0 DEBUG: $sql", 7);
1966 my $res = $messaging_db->exec_statement($sql);
1968 foreach my $hit (@{$res}) {
1969 my $msg_id = @{$hit}[0];
1971 my $sql = "SELECT * FROM $messaging_tn WHERE (id='$msg_id' AND direction='out' AND (NOT flag='s'))";
1972 #&daemon_log("0 DEBUG: $sql", 7);
1973 my $res = $messaging_db->exec_statement($sql);
1975 # not all usr msgs have been seen till now
1976 if ( ref(@$res[0]) eq "ARRAY") { next; }
1978 $sql = "DELETE FROM $messaging_tn WHERE (id='$msg_id')";
1979 #&daemon_log("0 DEBUG: $sql", 7);
1980 $res = $messaging_db->exec_statement($sql);
1982 }
1984 $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1985 return;
1986 }
1989 sub watch_for_old_known_clients {
1990 my ($kernel,$heap) = @_[KERNEL, HEAP];
1992 my $sql_statement = "SELECT * FROM $known_clients_tn";
1993 my $res = $known_clients_db->select_dbentry( $sql_statement );
1995 my $act_time = int(&get_time());
1997 while ( my ($hit_num, $hit) = each %$res) {
1998 my $expired_timestamp = int($hit->{'timestamp'});
1999 $expired_timestamp =~ /(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/;
2000 my $dt = DateTime->new( year => $1,
2001 month => $2,
2002 day => $3,
2003 hour => $4,
2004 minute => $5,
2005 second => $6,
2006 );
2008 $dt->add( seconds => 2 * int($hit->{'keylifetime'}) );
2009 $expired_timestamp = $dt->ymd('').$dt->hms('');
2010 if ($act_time > $expired_timestamp) {
2011 my $hostname = $hit->{'hostname'};
2012 my $del_sql = "DELETE FROM $known_clients_tn WHERE hostname='$hostname'";
2013 my $del_res = $known_clients_db->exec_statement($del_sql);
2015 &main::daemon_log("0 INFO: timestamp '".$hit->{'timestamp'}."' of client '$hostname' is expired('$expired_timestamp'), client will be deleted from known_clients_db", 5);
2016 }
2018 }
2020 $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
2021 }
2024 sub watch_for_next_tasks {
2025 my ($kernel,$heap) = @_[KERNEL, HEAP];
2027 my $sql = "SELECT * FROM $incoming_tn";
2028 my $res = $incoming_db->select_dbentry($sql);
2030 while ( my ($hit_num, $hit) = each %$res) {
2031 my $headertag = $hit->{'headertag'};
2032 if ($headertag =~ /^answer_(\d+)/) {
2033 # do not start processing, this message is for a still running POE::Wheel
2034 next;
2035 }
2036 my $message_id = $hit->{'id'};
2037 $kernel->yield('next_task', $hit);
2039 my $sql = "DELETE FROM $incoming_tn WHERE id=$message_id";
2040 my $res = $incoming_db->exec_statement($sql);
2041 }
2043 $kernel->delay_set('watch_for_next_tasks', 0.1);
2044 }
2047 sub get_ldap_handle {
2048 my ($session_id) = @_;
2049 my $heap;
2050 my $ldap_handle;
2052 if (not defined $session_id ) { $session_id = 0 };
2053 if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
2055 if ($session_id == 0) {
2056 daemon_log("$session_id DEBUG: get_ldap_handle invoked without a session_id, create a new ldap_handle", 7);
2057 $ldap_handle = Net::LDAP->new( $ldap_uri );
2058 $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!");
2060 } else {
2061 my $session_reference = $global_kernel->ID_id_to_session($session_id);
2062 if( defined $session_reference ) {
2063 $heap = $session_reference->get_heap();
2064 }
2066 if (not defined $heap) {
2067 daemon_log("$session_id DEBUG: cannot get heap for session_id '$session_id'", 7);
2068 return;
2069 }
2071 # TODO: This "if" is nonsense, because it doesn't prove that the
2072 # used handle is still valid - or if we've to reconnect...
2073 #if (not exists $heap->{ldap_handle}) {
2074 $ldap_handle = Net::LDAP->new( $ldap_uri );
2075 $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!");
2076 $heap->{ldap_handle} = $ldap_handle;
2077 #}
2078 }
2079 return $ldap_handle;
2080 }
2083 sub change_fai_state {
2084 my ($st, $targets, $session_id) = @_;
2085 $session_id = 0 if not defined $session_id;
2086 # Set FAI state to localboot
2087 my %mapActions= (
2088 reboot => '',
2089 update => 'softupdate',
2090 localboot => 'localboot',
2091 reinstall => 'install',
2092 rescan => '',
2093 wake => '',
2094 memcheck => 'memcheck',
2095 sysinfo => 'sysinfo',
2096 install => 'install',
2097 );
2099 # Return if this is unknown
2100 if (!exists $mapActions{ $st }){
2101 daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1);
2102 return;
2103 }
2105 my $state= $mapActions{ $st };
2107 my $ldap_handle = &get_ldap_handle($session_id);
2108 if( defined($ldap_handle) ) {
2110 # Build search filter for hosts
2111 my $search= "(&(objectClass=GOhard)";
2112 foreach (@{$targets}){
2113 $search.= "(macAddress=$_)";
2114 }
2115 $search.= ")";
2117 # If there's any host inside of the search string, procress them
2118 if (!($search =~ /macAddress/)){
2119 daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);
2120 return;
2121 }
2123 # Perform search for Unit Tag
2124 my $mesg = $ldap_handle->search(
2125 base => $ldap_base,
2126 scope => 'sub',
2127 attrs => ['dn', 'FAIstate', 'objectClass'],
2128 filter => "$search"
2129 );
2131 if ($mesg->count) {
2132 my @entries = $mesg->entries;
2133 if (0 == @entries) {
2134 daemon_log("$session_id ERROR: ldap search failed: ldap_base=$ldap_base, filter=$search", 1);
2135 }
2137 foreach my $entry (@entries) {
2138 # Only modify entry if it is not set to '$state'
2139 if ($entry->get_value("FAIstate") ne "$state"){
2140 daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
2141 my $result;
2142 my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
2143 if (exists $tmp{'FAIobject'}){
2144 if ($state eq ''){
2145 $result= $ldap_handle->modify($entry->dn, changes => [
2146 delete => [ FAIstate => [] ] ]);
2147 } else {
2148 $result= $ldap_handle->modify($entry->dn, changes => [
2149 replace => [ FAIstate => $state ] ]);
2150 }
2151 } elsif ($state ne ''){
2152 $result= $ldap_handle->modify($entry->dn, changes => [
2153 add => [ objectClass => 'FAIobject' ],
2154 add => [ FAIstate => $state ] ]);
2155 }
2157 # Errors?
2158 if ($result->code){
2159 daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2160 }
2161 } else {
2162 daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 7);
2163 }
2164 }
2165 } else {
2166 daemon_log("$session_id ERROR: LDAP search failed: ldap_base=$ldap_base, filter=$search", 1);
2167 }
2169 # if no ldap handle defined
2170 } else {
2171 daemon_log("$session_id ERROR: no LDAP handle defined for update FAIstate", 1);
2172 }
2174 return;
2175 }
2178 sub change_goto_state {
2179 my ($st, $targets, $session_id) = @_;
2180 $session_id = 0 if not defined $session_id;
2182 # Switch on or off?
2183 my $state= $st eq 'active' ? 'active': 'locked';
2185 my $ldap_handle = &get_ldap_handle($session_id);
2186 if( defined($ldap_handle) ) {
2188 # Build search filter for hosts
2189 my $search= "(&(objectClass=GOhard)";
2190 foreach (@{$targets}){
2191 $search.= "(macAddress=$_)";
2192 }
2193 $search.= ")";
2195 # If there's any host inside of the search string, procress them
2196 if (!($search =~ /macAddress/)){
2197 return;
2198 }
2200 # Perform search for Unit Tag
2201 my $mesg = $ldap_handle->search(
2202 base => $ldap_base,
2203 scope => 'sub',
2204 attrs => ['dn', 'gotoMode'],
2205 filter => "$search"
2206 );
2208 if ($mesg->count) {
2209 my @entries = $mesg->entries;
2210 foreach my $entry (@entries) {
2212 # Only modify entry if it is not set to '$state'
2213 if ($entry->get_value("gotoMode") ne $state){
2215 daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
2216 my $result;
2217 $result= $ldap_handle->modify($entry->dn, changes => [
2218 replace => [ gotoMode => $state ] ]);
2220 # Errors?
2221 if ($result->code){
2222 &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2223 }
2225 }
2226 }
2227 } else {
2228 daemon_log("$session_id ERROR: LDAP search failed in function change_goto_state: ldap_base=$ldap_base, filter=$search", 1);
2229 }
2231 }
2232 }
2235 sub run_recreate_packages_db {
2236 my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2237 my $session_id = $session->ID;
2238 &main::daemon_log("$session_id INFO: Recreating FAI Packages DB ('$fai_release_tn', '$fai_server_tn', '$packages_list_tn')", 5);
2239 $kernel->yield('create_fai_release_db', $fai_release_tn);
2240 $kernel->yield('create_fai_server_db', $fai_server_tn);
2241 return;
2242 }
2245 sub run_create_fai_server_db {
2246 my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2247 my $session_id = $session->ID;
2248 my $task = POE::Wheel::Run->new(
2249 Program => sub { &create_fai_server_db($table_name,$kernel, undef, $session_id) },
2250 StdoutEvent => "session_run_result",
2251 StderrEvent => "session_run_debug",
2252 CloseEvent => "session_run_done",
2253 );
2255 $heap->{task}->{ $task->ID } = $task;
2256 return;
2257 }
2260 sub create_fai_server_db {
2261 my ($table_name, $kernel, $dont_create_packages_list, $session_id) = @_;
2262 my $result;
2264 if (not defined $session_id) { $session_id = 0; }
2265 my $ldap_handle = &get_ldap_handle();
2266 if(defined($ldap_handle)) {
2267 daemon_log("$session_id INFO: create_fai_server_db: start", 5);
2268 my $mesg= $ldap_handle->search(
2269 base => $ldap_base,
2270 scope => 'sub',
2271 attrs => ['FAIrepository', 'gosaUnitTag'],
2272 filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
2273 );
2274 if($mesg->{'resultCode'} == 0 &&
2275 $mesg->count != 0) {
2276 foreach my $entry (@{$mesg->{entries}}) {
2277 if($entry->exists('FAIrepository')) {
2278 # Add an entry for each Repository configured for server
2279 foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
2280 my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
2281 my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
2282 $result= $fai_server_db->add_dbentry( {
2283 table => $table_name,
2284 primkey => ['server', 'release', 'tag'],
2285 server => $tmp_url,
2286 release => $tmp_release,
2287 sections => $tmp_sections,
2288 tag => (length($tmp_tag)>0)?$tmp_tag:"",
2289 } );
2290 }
2291 }
2292 }
2293 }
2294 daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
2296 # TODO: Find a way to post the 'create_packages_list_db' event
2297 if(not defined($dont_create_packages_list)) {
2298 &create_packages_list_db(undef, undef, $session_id);
2299 }
2300 }
2302 $ldap_handle->disconnect;
2303 return $result;
2304 }
2307 sub run_create_fai_release_db {
2308 my ($session, $heap, $table_name) = @_[SESSION, HEAP, ARG0];
2309 my $session_id = $session->ID;
2310 my $task = POE::Wheel::Run->new(
2311 Program => sub { &create_fai_release_db($table_name, $session_id) },
2312 StdoutEvent => "session_run_result",
2313 StderrEvent => "session_run_debug",
2314 CloseEvent => "session_run_done",
2315 );
2317 $heap->{task}->{ $task->ID } = $task;
2318 return;
2319 }
2322 sub create_fai_release_db {
2323 my ($table_name, $session_id) = @_;
2324 my $result;
2326 # used for logging
2327 if (not defined $session_id) { $session_id = 0; }
2329 my $ldap_handle = &get_ldap_handle();
2330 if(defined($ldap_handle)) {
2331 daemon_log("$session_id INFO: create_fai_release_db: start",5);
2332 my $mesg= $ldap_handle->search(
2333 base => $ldap_base,
2334 scope => 'sub',
2335 attrs => [],
2336 filter => "(&(objectClass=organizationalUnit)(ou=fai))",
2337 );
2338 if($mesg->{'resultCode'} == 0 &&
2339 $mesg->count != 0) {
2340 # Walk through all possible FAI container ou's
2341 my @sql_list;
2342 my $timestamp= &get_time();
2343 foreach my $ou (@{$mesg->{entries}}) {
2344 my $tmp_classes= resolve_fai_classes($ou->dn, $ldap_handle, $session_id);
2345 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
2346 my @tmp_array=get_fai_release_entries($tmp_classes);
2347 if(@tmp_array) {
2348 foreach my $entry (@tmp_array) {
2349 if(defined($entry) && ref($entry) eq 'HASH') {
2350 my $sql=
2351 "INSERT INTO $table_name "
2352 ."(timestamp, release, class, type, state) VALUES ("
2353 .$timestamp.","
2354 ."'".$entry->{'release'}."',"
2355 ."'".$entry->{'class'}."',"
2356 ."'".$entry->{'type'}."',"
2357 ."'".$entry->{'state'}."')";
2358 push @sql_list, $sql;
2359 }
2360 }
2361 }
2362 }
2363 }
2365 daemon_log("$session_id DEBUG: Inserting ".scalar @sql_list." entries to DB",8);
2366 if(@sql_list) {
2367 unshift @sql_list, "VACUUM";
2368 unshift @sql_list, "DELETE FROM $table_name";
2369 $fai_release_db->exec_statementlist(\@sql_list);
2370 }
2371 daemon_log("$session_id DEBUG: Done with inserting",7);
2372 }
2373 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
2374 }
2375 $ldap_handle->disconnect;
2376 return $result;
2377 }
2379 sub get_fai_types {
2380 my $tmp_classes = shift || return undef;
2381 my @result;
2383 foreach my $type(keys %{$tmp_classes}) {
2384 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
2385 my $entry = {
2386 type => $type,
2387 state => $tmp_classes->{$type}[0],
2388 };
2389 push @result, $entry;
2390 }
2391 }
2393 return @result;
2394 }
2396 sub get_fai_state {
2397 my $result = "";
2398 my $tmp_classes = shift || return $result;
2400 foreach my $type(keys %{$tmp_classes}) {
2401 if(defined($tmp_classes->{$type}[0])) {
2402 $result = $tmp_classes->{$type}[0];
2404 # State is equal for all types in class
2405 last;
2406 }
2407 }
2409 return $result;
2410 }
2412 sub resolve_fai_classes {
2413 my ($fai_base, $ldap_handle, $session_id) = @_;
2414 if (not defined $session_id) { $session_id = 0; }
2415 my $result;
2416 my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
2417 my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
2418 my $fai_classes;
2420 daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base",7);
2421 my $mesg= $ldap_handle->search(
2422 base => $fai_base,
2423 scope => 'sub',
2424 attrs => ['cn','objectClass','FAIstate'],
2425 filter => $fai_filter,
2426 );
2427 daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries",7);
2429 if($mesg->{'resultCode'} == 0 &&
2430 $mesg->count != 0) {
2431 foreach my $entry (@{$mesg->{entries}}) {
2432 if($entry->exists('cn')) {
2433 my $tmp_dn= $entry->dn();
2435 # Skip classname and ou dn parts for class
2436 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?),$fai_base$/;
2438 # Skip classes without releases
2439 if((!defined($tmp_release)) || length($tmp_release)==0) {
2440 next;
2441 }
2443 my $tmp_cn= $entry->get_value('cn');
2444 my $tmp_state= $entry->get_value('FAIstate');
2446 my $tmp_type;
2447 # Get FAI type
2448 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
2449 if(grep $_ eq $oclass, @possible_fai_classes) {
2450 $tmp_type= $oclass;
2451 last;
2452 }
2453 }
2455 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2456 # A Subrelease
2457 my @sub_releases = split(/,/, $tmp_release);
2459 # Walk through subreleases and build hash tree
2460 my $hash;
2461 while(my $tmp_sub_release = pop @sub_releases) {
2462 $hash .= "\{'$tmp_sub_release'\}->";
2463 }
2464 eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
2465 } else {
2466 # A branch, no subrelease
2467 push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
2468 }
2469 } elsif (!$entry->exists('cn')) {
2470 my $tmp_dn= $entry->dn();
2471 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?),$fai_base$/;
2473 # Skip classes without releases
2474 if((!defined($tmp_release)) || length($tmp_release)==0) {
2475 next;
2476 }
2478 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2479 # A Subrelease
2480 my @sub_releases= split(/,/, $tmp_release);
2482 # Walk through subreleases and build hash tree
2483 my $hash;
2484 while(my $tmp_sub_release = pop @sub_releases) {
2485 $hash .= "\{'$tmp_sub_release'\}->";
2486 }
2487 # Remove the last two characters
2488 chop($hash);
2489 chop($hash);
2491 eval('$fai_classes->'.$hash.'= {}');
2492 } else {
2493 # A branch, no subrelease
2494 if(!exists($fai_classes->{$tmp_release})) {
2495 $fai_classes->{$tmp_release} = {};
2496 }
2497 }
2498 }
2499 }
2501 # The hash is complete, now we can honor the copy-on-write based missing entries
2502 foreach my $release (keys %$fai_classes) {
2503 $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
2504 }
2505 }
2506 return $result;
2507 }
2509 sub apply_fai_inheritance {
2510 my $fai_classes = shift || return {};
2511 my $tmp_classes;
2513 # Get the classes from the branch
2514 foreach my $class (keys %{$fai_classes}) {
2515 # Skip subreleases
2516 if($class =~ /^ou=.*$/) {
2517 next;
2518 } else {
2519 $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
2520 }
2521 }
2523 # Apply to each subrelease
2524 foreach my $subrelease (keys %{$fai_classes}) {
2525 if($subrelease =~ /ou=/) {
2526 foreach my $tmp_class (keys %{$tmp_classes}) {
2527 if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
2528 $fai_classes->{$subrelease}->{$tmp_class} =
2529 deep_copy($tmp_classes->{$tmp_class});
2530 } else {
2531 foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
2532 if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
2533 $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
2534 deep_copy($tmp_classes->{$tmp_class}->{$type});
2535 }
2536 }
2537 }
2538 }
2539 }
2540 }
2542 # Find subreleases in deeper levels
2543 foreach my $subrelease (keys %{$fai_classes}) {
2544 if($subrelease =~ /ou=/) {
2545 foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
2546 if($subsubrelease =~ /ou=/) {
2547 apply_fai_inheritance($fai_classes->{$subrelease});
2548 }
2549 }
2550 }
2551 }
2553 return $fai_classes;
2554 }
2556 sub get_fai_release_entries {
2557 my $tmp_classes = shift || return;
2558 my $parent = shift || "";
2559 my @result = shift || ();
2561 foreach my $entry (keys %{$tmp_classes}) {
2562 if(defined($entry)) {
2563 if($entry =~ /^ou=.*$/) {
2564 my $release_name = $entry;
2565 $release_name =~ s/ou=//g;
2566 if(length($parent)>0) {
2567 $release_name = $parent."/".$release_name;
2568 }
2569 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2570 foreach my $bufentry(@bufentries) {
2571 push @result, $bufentry;
2572 }
2573 } else {
2574 my @types = get_fai_types($tmp_classes->{$entry});
2575 foreach my $type (@types) {
2576 push @result,
2577 {
2578 'class' => $entry,
2579 'type' => $type->{'type'},
2580 'release' => $parent,
2581 'state' => $type->{'state'},
2582 };
2583 }
2584 }
2585 }
2586 }
2588 return @result;
2589 }
2591 sub deep_copy {
2592 my $this = shift;
2593 if (not ref $this) {
2594 $this;
2595 } elsif (ref $this eq "ARRAY") {
2596 [map deep_copy($_), @$this];
2597 } elsif (ref $this eq "HASH") {
2598 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2599 } else { die "what type is $_?" }
2600 }
2603 sub session_run_result {
2604 my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];
2605 $kernel->sig(CHLD => "child_reap");
2606 }
2608 sub session_run_debug {
2609 my $result = $_[ARG0];
2610 print STDERR "$result\n";
2611 }
2613 sub session_run_done {
2614 my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2615 delete $heap->{task}->{$task_id};
2616 }
2619 sub create_sources_list {
2620 my $session_id = shift;
2621 my $ldap_handle = &main::get_ldap_handle;
2622 my $result="/tmp/gosa_si_tmp_sources_list";
2624 # Remove old file
2625 if(stat($result)) {
2626 unlink($result);
2627 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7);
2628 }
2630 my $fh;
2631 open($fh, ">$result");
2632 if (not defined $fh) {
2633 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7);
2634 return undef;
2635 }
2636 if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2637 my $mesg=$ldap_handle->search(
2638 base => $main::ldap_server_dn,
2639 scope => 'base',
2640 attrs => 'FAIrepository',
2641 filter => 'objectClass=FAIrepositoryServer'
2642 );
2643 if($mesg->count) {
2644 foreach my $entry(@{$mesg->{'entries'}}) {
2645 foreach my $value(@{$entry->get_value('FAIrepository', asref => 1)}) {
2646 my ($server, $tag, $release, $sections)= split /\|/, $value;
2647 my $line = "deb $server $release";
2648 $sections =~ s/,/ /g;
2649 $line.= " $sections";
2650 print $fh $line."\n";
2651 }
2652 }
2653 }
2654 } else {
2655 if (defined $main::ldap_server_dn){
2656 &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1);
2657 } else {
2658 &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2659 }
2660 }
2661 close($fh);
2663 return $result;
2664 }
2667 sub run_create_packages_list_db {
2668 my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2669 my $session_id = $session->ID;
2671 my $task = POE::Wheel::Run->new(
2672 Priority => +20,
2673 Program => sub {&create_packages_list_db(undef, undef, $session_id)},
2674 StdoutEvent => "session_run_result",
2675 StderrEvent => "session_run_debug",
2676 CloseEvent => "session_run_done",
2677 );
2678 $heap->{task}->{ $task->ID } = $task;
2679 }
2682 sub create_packages_list_db {
2683 my ($ldap_handle, $sources_file, $session_id) = @_;
2685 # it should not be possible to trigger a recreation of packages_list_db
2686 # while packages_list_db is under construction, so set flag packages_list_under_construction
2687 # which is tested befor recreation can be started
2688 if (-r $packages_list_under_construction) {
2689 daemon_log("$session_id WARNING: packages_list_db is right now under construction, please wait until this process is finished", 3);
2690 return;
2691 } else {
2692 daemon_log("$session_id INFO: create_packages_list_db: start", 5);
2693 # set packages_list_under_construction to true
2694 system("touch $packages_list_under_construction");
2695 @packages_list_statements=();
2696 }
2698 if (not defined $session_id) { $session_id = 0; }
2699 if (not defined $ldap_handle) {
2700 $ldap_handle= &get_ldap_handle();
2702 if (not defined $ldap_handle) {
2703 daemon_log("$session_id ERROR: no ldap_handle available to create_packages_list_db", 1);
2704 unlink($packages_list_under_construction);
2705 return;
2706 }
2707 }
2708 if (not defined $sources_file) {
2709 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5);
2710 $sources_file = &create_sources_list($session_id);
2711 }
2713 if (not defined $sources_file) {
2714 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1);
2715 unlink($packages_list_under_construction);
2716 return;
2717 }
2719 my $line;
2721 open(CONFIG, "<$sources_file") or do {
2722 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2723 unlink($packages_list_under_construction);
2724 return;
2725 };
2727 # Read lines
2728 while ($line = <CONFIG>){
2729 # Unify
2730 chop($line);
2731 $line =~ s/^\s+//;
2732 $line =~ s/^\s+/ /;
2734 # Strip comments
2735 $line =~ s/#.*$//g;
2737 # Skip empty lines
2738 if ($line =~ /^\s*$/){
2739 next;
2740 }
2742 # Interpret deb line
2743 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2744 my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2745 my $section;
2746 foreach $section (split(' ', $sections)){
2747 &parse_package_info( $baseurl, $dist, $section, $session_id );
2748 }
2749 }
2750 }
2752 close (CONFIG);
2755 find(\&cleanup_and_extract, keys( %repo_dirs ));
2756 &main::strip_packages_list_statements();
2757 unshift @packages_list_statements, "VACUUM";
2758 $packages_list_db->exec_statementlist(\@packages_list_statements);
2759 unlink($packages_list_under_construction);
2760 daemon_log("$session_id INFO: create_packages_list_db: finished", 5);
2761 return;
2762 }
2764 # This function should do some intensive task to minimize the db-traffic
2765 sub strip_packages_list_statements {
2766 my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2767 my @new_statement_list=();
2768 my $hash;
2769 my $insert_hash;
2770 my $update_hash;
2771 my $delete_hash;
2772 my $local_timestamp=get_time();
2774 foreach my $existing_entry (@existing_entries) {
2775 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
2776 }
2778 foreach my $statement (@packages_list_statements) {
2779 if($statement =~ /^INSERT/i) {
2780 # Assign the values from the insert statement
2781 my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~
2782 /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
2783 if(exists($hash->{$distribution}->{$package}->{$version})) {
2784 # If section or description has changed, update the DB
2785 if(
2786 (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or
2787 (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
2788 ) {
2789 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
2790 }
2791 } else {
2792 # Insert a non-existing entry to db
2793 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2794 }
2795 } elsif ($statement =~ /^UPDATE/i) {
2796 my ($template,$package,$version) = ($1,$2,$3) if $statement =~
2797 /^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;
2798 foreach my $distribution (keys %{$hash}) {
2799 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
2800 # update the insertion hash to execute only one query per package (insert instead insert+update)
2801 @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
2802 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
2803 if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
2804 my $section;
2805 my $description;
2806 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
2807 length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
2808 $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
2809 }
2810 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2811 $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
2812 }
2813 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2814 }
2815 }
2816 }
2817 }
2818 }
2820 # TODO: Check for orphaned entries
2822 # unroll the insert_hash
2823 foreach my $distribution (keys %{$insert_hash}) {
2824 foreach my $package (keys %{$insert_hash->{$distribution}}) {
2825 foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
2826 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
2827 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
2828 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
2829 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
2830 ."'$local_timestamp')";
2831 }
2832 }
2833 }
2835 # unroll the update hash
2836 foreach my $distribution (keys %{$update_hash}) {
2837 foreach my $package (keys %{$update_hash->{$distribution}}) {
2838 foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
2839 my $set = "";
2840 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
2841 $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
2842 }
2843 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2844 $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
2845 }
2846 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
2847 $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
2848 }
2849 if(defined($set) and length($set) > 0) {
2850 $set .= "timestamp = '$local_timestamp'";
2851 } else {
2852 next;
2853 }
2854 push @new_statement_list,
2855 "UPDATE $main::packages_list_tn SET $set WHERE"
2856 ." distribution = '$distribution'"
2857 ." AND package = '$package'"
2858 ." AND version = '$version'";
2859 }
2860 }
2861 }
2863 @packages_list_statements = @new_statement_list;
2864 }
2867 sub parse_package_info {
2868 my ($baseurl, $dist, $section, $session_id)= @_;
2869 my ($package);
2870 if (not defined $session_id) { $session_id = 0; }
2871 my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
2872 $repo_dirs{ "${repo_path}/pool" } = 1;
2874 foreach $package ("Packages.gz"){
2875 daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
2876 get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
2877 parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
2878 }
2880 }
2883 sub get_package {
2884 my ($url, $dest, $session_id)= @_;
2885 if (not defined $session_id) { $session_id = 0; }
2887 my $tpath = dirname($dest);
2888 -d "$tpath" || mkpath "$tpath";
2890 # This is ugly, but I've no time to take a look at "how it works in perl"
2891 if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
2892 system("gunzip -cd '$dest' > '$dest.in'");
2893 daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 5);
2894 unlink($dest);
2895 daemon_log("$session_id DEBUG: delete file '$dest'", 5);
2896 } else {
2897 daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' failed!", 1);
2898 }
2899 return 0;
2900 }
2903 sub parse_package {
2904 my ($path, $dist, $srv_path, $session_id)= @_;
2905 if (not defined $session_id) { $session_id = 0;}
2906 my ($package, $version, $section, $description);
2907 my $PACKAGES;
2908 my $timestamp = &get_time();
2910 if(not stat("$path.in")) {
2911 daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
2912 return;
2913 }
2915 open($PACKAGES, "<$path.in");
2916 if(not defined($PACKAGES)) {
2917 daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1);
2918 return;
2919 }
2921 # Read lines
2922 while (<$PACKAGES>){
2923 my $line = $_;
2924 # Unify
2925 chop($line);
2927 # Use empty lines as a trigger
2928 if ($line =~ /^\s*$/){
2929 my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
2930 push(@packages_list_statements, $sql);
2931 $package = "none";
2932 $version = "none";
2933 $section = "none";
2934 $description = "none";
2935 next;
2936 }
2938 # Trigger for package name
2939 if ($line =~ /^Package:\s/){
2940 ($package)= ($line =~ /^Package: (.*)$/);
2941 next;
2942 }
2944 # Trigger for version
2945 if ($line =~ /^Version:\s/){
2946 ($version)= ($line =~ /^Version: (.*)$/);
2947 next;
2948 }
2950 # Trigger for description
2951 if ($line =~ /^Description:\s/){
2952 ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
2953 next;
2954 }
2956 # Trigger for section
2957 if ($line =~ /^Section:\s/){
2958 ($section)= ($line =~ /^Section: (.*)$/);
2959 next;
2960 }
2962 # Trigger for filename
2963 if ($line =~ /^Filename:\s/){
2964 my ($filename) = ($line =~ /^Filename: (.*)$/);
2965 store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
2966 next;
2967 }
2968 }
2970 close( $PACKAGES );
2971 unlink( "$path.in" );
2972 }
2975 sub store_fileinfo {
2976 my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
2978 my %fileinfo = (
2979 'package' => $package,
2980 'dist' => $dist,
2981 'version' => $vers,
2982 );
2984 $repo_files{ "${srvdir}/$file" } = \%fileinfo;
2985 }
2988 sub cleanup_and_extract {
2989 my $fileinfo = $repo_files{ $File::Find::name };
2991 if( defined $fileinfo ) {
2993 my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
2994 my $sql;
2995 my $package = $fileinfo->{ 'package' };
2996 my $newver = $fileinfo->{ 'version' };
2998 mkpath($dir);
2999 system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
3001 if( -f "$dir/DEBIAN/templates" ) {
3003 daemon_log("DEBUG: Found debconf templates in '$package' - $newver", 7);
3005 my $tmpl= "";
3006 {
3007 local $/=undef;
3008 open FILE, "$dir/DEBIAN/templates";
3009 $tmpl = &encode_base64(<FILE>);
3010 close FILE;
3011 }
3012 rmtree("$dir/DEBIAN/templates");
3014 $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
3015 push @packages_list_statements, $sql;
3016 }
3017 }
3019 return;
3020 }
3023 sub register_at_foreign_servers {
3024 my ($kernel) = $_[KERNEL];
3026 # hole alle bekannten server aus known_server_db
3027 my $server_sql = "SELECT * FROM $known_server_tn";
3028 my $server_res = $known_server_db->exec_statement($server_sql);
3030 # no entries in known_server_db
3031 if (not ref(@$server_res[0]) eq "ARRAY") {
3032 # TODO
3033 }
3035 # detect already connected clients
3036 my $client_sql = "SELECT * FROM $known_clients_tn";
3037 my $client_res = $known_clients_db->exec_statement($client_sql);
3039 # send my server details to all other gosa-si-server within the network
3040 foreach my $hit (@$server_res) {
3041 my $hostname = @$hit[0];
3042 my $hostkey = &create_passwd;
3044 # add already connected clients to registration message
3045 my $myhash = &create_xml_hash('new_server', $server_address, $hostname);
3046 &add_content2xml_hash($myhash, 'key', $hostkey);
3047 map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res);
3049 # add locally loaded gosa-si modules to registration message
3050 my $loaded_modules = {};
3051 while (my ($package, $pck_info) = each %$known_modules) {
3052 next if ((!defined(@$pck_info[2])) || (!(ref (@$pck_info[2]) eq 'HASH')));
3053 foreach my $act_module (keys(%{@$pck_info[2]})) {
3054 $loaded_modules->{$act_module} = "";
3055 }
3056 }
3058 map(&add_content2xml_hash($myhash, "loaded_modules", $_), keys(%$loaded_modules));
3060 # add macaddress to registration message
3061 my ($host_ip, $host_port) = split(/:/, $hostname);
3062 my $local_ip = &get_local_ip_for_remote_ip($host_ip);
3063 my $network_interface= &get_interface_for_ip($local_ip);
3064 my $host_mac = &get_mac_for_interface($network_interface);
3065 &add_content2xml_hash($myhash, 'macaddress', $host_mac);
3067 # build registration message and send it
3068 my $foreign_server_msg = &create_xml_string($myhash);
3069 my $error = &send_msg_to_target($foreign_server_msg, $hostname, $ServerPackages_key, "new_server", 0);
3070 }
3072 $kernel->delay_set("register_at_foreign_servers", $foreign_servers_register_delay);
3073 return;
3074 }
3077 #==== MAIN = main ==============================================================
3078 # parse commandline options
3079 Getopt::Long::Configure( "bundling" );
3080 GetOptions("h|help" => \&usage,
3081 "c|config=s" => \$cfg_file,
3082 "f|foreground" => \$foreground,
3083 "v|verbose+" => \$verbose,
3084 "no-arp+" => \$no_arp,
3085 );
3087 # read and set config parameters
3088 &check_cmdline_param ;
3089 &read_configfile($cfg_file, %cfg_defaults);
3090 &check_pid;
3092 $SIG{CHLD} = 'IGNORE';
3094 # forward error messages to logfile
3095 if( ! $foreground ) {
3096 open( STDIN, '+>/dev/null' );
3097 open( STDOUT, '+>&STDIN' );
3098 open( STDERR, '+>&STDIN' );
3099 }
3101 # Just fork, if we are not in foreground mode
3102 if( ! $foreground ) {
3103 chdir '/' or die "Can't chdir to /: $!";
3104 $pid = fork;
3105 setsid or die "Can't start a new session: $!";
3106 umask 0;
3107 } else {
3108 $pid = $$;
3109 }
3111 # Do something useful - put our PID into the pid_file
3112 if( 0 != $pid ) {
3113 open( LOCK_FILE, ">$pid_file" );
3114 print LOCK_FILE "$pid\n";
3115 close( LOCK_FILE );
3116 if( !$foreground ) {
3117 exit( 0 )
3118 };
3119 }
3121 # parse head url and revision from svn
3122 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
3123 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
3124 $server_headURL = defined $1 ? $1 : 'unknown' ;
3125 $server_revision = defined $2 ? $2 : 'unknown' ;
3126 if ($server_headURL =~ /\/tag\// ||
3127 $server_headURL =~ /\/branches\// ) {
3128 $server_status = "stable";
3129 } else {
3130 $server_status = "developmental" ;
3131 }
3134 daemon_log(" ", 1);
3135 daemon_log("$0 started!", 1);
3136 daemon_log("status: $server_status", 1);
3137 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1);
3139 # connect to incoming_db
3140 unlink($incoming_file_name);
3141 $incoming_db = GOSA::DBsqlite->new($incoming_file_name);
3142 $incoming_db->create_table($incoming_tn, \@incoming_col_names);
3144 # connect to gosa-si job queue
3145 $job_db = GOSA::DBsqlite->new($job_queue_file_name);
3146 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
3148 # connect to known_clients_db
3149 $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
3150 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
3152 # connect to foreign_clients_db
3153 $foreign_clients_db = GOSA::DBsqlite->new($foreign_clients_file_name);
3154 $foreign_clients_db->create_table($foreign_clients_tn, \@foreign_clients_col_names);
3156 # connect to known_server_db
3157 unlink($known_server_file_name);
3158 $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
3159 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
3161 # connect to login_usr_db
3162 $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
3163 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
3165 # connect to fai_server_db and fai_release_db
3166 unlink($fai_server_file_name);
3167 $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
3168 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
3170 unlink($fai_release_file_name);
3171 $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
3172 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
3174 # connect to packages_list_db
3175 #unlink($packages_list_file_name);
3176 unlink($packages_list_under_construction);
3177 $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
3178 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
3180 # connect to messaging_db
3181 $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
3182 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
3185 # create xml object used for en/decrypting
3186 $xml = new XML::Simple();
3189 # foreign servers
3190 my @foreign_server_list;
3192 # add foreign server from cfg file
3193 if ($foreign_server_string ne "") {
3194 my @cfg_foreign_server_list = split(",", $foreign_server_string);
3195 foreach my $foreign_server (@cfg_foreign_server_list) {
3196 push(@foreign_server_list, $foreign_server);
3197 }
3198 }
3200 # add foreign server from dns
3201 my @tmp_servers;
3202 if ( !$server_domain) {
3203 # Try our DNS Searchlist
3204 for my $domain(get_dns_domains()) {
3205 chomp($domain);
3206 my ($tmp_domains, $error_string) = &get_server_addresses($domain);
3207 if(@$tmp_domains) {
3208 for my $tmp_server(@$tmp_domains) {
3209 push @tmp_servers, $tmp_server;
3210 }
3211 }
3212 }
3213 if(@tmp_servers && length(@tmp_servers)==0) {
3214 daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3215 }
3216 } else {
3217 @tmp_servers = &get_server_addresses($server_domain);
3218 if( 0 == @tmp_servers ) {
3219 daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3220 }
3221 }
3222 foreach my $server (@tmp_servers) {
3223 unshift(@foreign_server_list, $server);
3224 }
3225 # eliminate duplicate entries
3226 @foreign_server_list = &del_doubles(@foreign_server_list);
3227 my $all_foreign_server = join(", ", @foreign_server_list);
3228 daemon_log("0 INFO: found foreign server in config file and DNS: $all_foreign_server", 5);
3230 # add all found foreign servers to known_server
3231 my $act_timestamp = &get_time();
3232 foreach my $foreign_server (@foreign_server_list) {
3234 # do not add myself to known_server_db
3235 if (&is_local($foreign_server)) { next; }
3236 ######################################
3238 my $res = $known_server_db->add_dbentry( {table=>$known_server_tn,
3239 primkey=>['hostname'],
3240 hostname=>$foreign_server,
3241 macaddress=>"",
3242 status=>'not_jet_registered',
3243 hostkey=>"none",
3244 loaded_modules => "none",
3245 timestamp=>$act_timestamp,
3246 } );
3247 }
3250 # Import all modules
3251 &import_modules;
3253 # Check wether all modules are gosa-si valid passwd check
3254 &password_check;
3256 # Prepare for using Opsi
3257 if ($opsi_enabled eq "true") {
3258 use JSON::RPC::Client;
3259 use XML::Quote qw(:all);
3260 $opsi_url= "https://".$opsi_admin.":".$opsi_password."@".$opsi_server.":4447/rpc";
3261 $opsi_client = new JSON::RPC::Client;
3262 }
3265 POE::Component::Server::TCP->new(
3266 Alias => "TCP_SERVER",
3267 Port => $server_port,
3268 ClientInput => sub {
3269 my ($kernel, $input) = @_[KERNEL, ARG0];
3270 push(@tasks, $input);
3271 push(@msgs_to_decrypt, $input);
3272 $kernel->yield("msg_to_decrypt");
3273 },
3274 InlineStates => {
3275 msg_to_decrypt => \&msg_to_decrypt,
3276 next_task => \&next_task,
3277 task_result => \&handle_task_result,
3278 task_done => \&handle_task_done,
3279 task_debug => \&handle_task_debug,
3280 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!" },
3281 }
3282 );
3284 daemon_log("start socket for incoming xml messages at port '$server_port' ", 1);
3286 # create session for repeatedly checking the job queue for jobs
3287 POE::Session->create(
3288 inline_states => {
3289 _start => \&session_start,
3290 register_at_foreign_servers => \®ister_at_foreign_servers,
3291 sig_handler => \&sig_handler,
3292 next_task => \&next_task,
3293 task_result => \&handle_task_result,
3294 task_done => \&handle_task_done,
3295 task_debug => \&handle_task_debug,
3296 watch_for_next_tasks => \&watch_for_next_tasks,
3297 watch_for_new_messages => \&watch_for_new_messages,
3298 watch_for_delivery_messages => \&watch_for_delivery_messages,
3299 watch_for_done_messages => \&watch_for_done_messages,
3300 watch_for_new_jobs => \&watch_for_new_jobs,
3301 watch_for_modified_jobs => \&watch_for_modified_jobs,
3302 watch_for_done_jobs => \&watch_for_done_jobs,
3303 watch_for_opsi_jobs => \&watch_for_opsi_jobs,
3304 watch_for_old_known_clients => \&watch_for_old_known_clients,
3305 create_packages_list_db => \&run_create_packages_list_db,
3306 create_fai_server_db => \&run_create_fai_server_db,
3307 create_fai_release_db => \&run_create_fai_release_db,
3308 recreate_packages_db => \&run_recreate_packages_db,
3309 session_run_result => \&session_run_result,
3310 session_run_debug => \&session_run_debug,
3311 session_run_done => \&session_run_done,
3312 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!" },
3313 }
3314 );
3317 POE::Kernel->run();
3318 exit;