8bc1100fd8952f69ad42795e76a594fea4ae8587
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 );
92 # additional variable which should be globaly accessable
93 our $server_address;
94 our $server_mac_address;
95 our $gosa_address;
96 our $no_arp;
97 our $verbose;
98 our $forground;
99 our $cfg_file;
100 our ($ldap_uri, $ldap_base, $ldap_admin_dn, $ldap_admin_password, $ldap_server_dn);
101 our $known_modules;
103 # specifies the verbosity of the daemon_log
104 $verbose = 0 ;
106 # if foreground is not null, script will be not forked to background
107 $foreground = 0 ;
109 # specifies the timeout seconds while checking the online status of a registrating client
110 $ping_timeout = 5;
112 $no_arp = 0;
113 my $packages_list_under_construction = "/tmp/packages_list_creation_in_progress";
114 my @packages_list_statements;
115 my $watch_for_new_jobs_in_progress = 0;
117 # holds all incoming decrypted messages
118 our $incoming_db;
119 our $incoming_tn = 'incoming';
120 my $incoming_file_name;
121 my @incoming_col_names = ("id INTEGER PRIMARY KEY",
122 "timestamp DEFAULT 'none'",
123 "headertag DEFAULT 'none'",
124 "targettag DEFAULT 'none'",
125 "xmlmessage DEFAULT 'none'",
126 "module DEFAULT 'none'",
127 "sessionid DEFAULT '0'",
128 );
130 # holds all gosa jobs
131 our $job_db;
132 our $job_queue_tn = 'jobs';
133 my $job_queue_file_name;
134 my @job_queue_col_names = ("id INTEGER PRIMARY KEY",
135 "timestamp DEFAULT 'none'",
136 "status DEFAULT 'none'",
137 "result DEFAULT 'none'",
138 "progress DEFAULT 'none'",
139 "headertag DEFAULT 'none'",
140 "targettag DEFAULT 'none'",
141 "xmlmessage DEFAULT 'none'",
142 "macaddress DEFAULT 'none'",
143 "plainname DEFAULT 'none'",
144 "siserver DEFAULT 'none'",
145 "modified DEFAULT '0'",
146 );
148 # holds all other gosa-si-server
149 our $known_server_db;
150 our $known_server_tn = "known_server";
151 my $known_server_file_name;
152 my @known_server_col_names = ("hostname", "macaddress", "status", "hostkey", "loaded_modules", "timestamp");
154 # holds all registrated clients
155 our $known_clients_db;
156 our $known_clients_tn = "known_clients";
157 my $known_clients_file_name;
158 my @known_clients_col_names = ("hostname", "status", "hostkey", "timestamp", "macaddress", "events", "keylifetime");
160 # holds all registered clients at a foreign server
161 our $foreign_clients_db;
162 our $foreign_clients_tn = "foreign_clients";
163 my $foreign_clients_file_name;
164 my @foreign_clients_col_names = ("hostname", "macaddress", "regserver", "timestamp");
166 # holds all logged in user at each client
167 our $login_users_db;
168 our $login_users_tn = "login_users";
169 my $login_users_file_name;
170 my @login_users_col_names = ("client", "user", "timestamp");
172 # holds all fai server, the debian release and tag
173 our $fai_server_db;
174 our $fai_server_tn = "fai_server";
175 my $fai_server_file_name;
176 our @fai_server_col_names = ("timestamp", "server", "release", "sections", "tag");
178 our $fai_release_db;
179 our $fai_release_tn = "fai_release";
180 my $fai_release_file_name;
181 our @fai_release_col_names = ("timestamp", "release", "class", "type", "state");
183 # holds all packages available from different repositories
184 our $packages_list_db;
185 our $packages_list_tn = "packages_list";
186 my $packages_list_file_name;
187 our @packages_list_col_names = ("distribution", "package", "version", "section", "description", "template", "timestamp");
188 my $outdir = "/tmp/packages_list_db";
189 my $arch = "i386";
191 # holds all messages which should be delivered to a user
192 our $messaging_db;
193 our $messaging_tn = "messaging";
194 our @messaging_col_names = ("id INTEGER", "subject", "message_from", "message_to",
195 "flag", "direction", "delivery_time", "message", "timestamp" );
196 my $messaging_file_name;
198 # path to directory to store client install log files
199 our $client_fai_log_dir = "/var/log/fai";
201 # queue which stores taskes until one of the $max_children children are ready to process the task
202 my @tasks = qw();
203 my @msgs_to_decrypt = qw();
204 my $max_children = 2;
207 # loop delay for job queue to look for opsi jobs
208 my $job_queue_opsi_delay = 10;
209 our $opsi_client;
210 our $opsi_url;
212 # Lifetime of logged in user information. If no update information comes after n seconds,
213 # the user is expeceted to be no longer logged in or the host is no longer running. Because
214 # of this, the user is deleted from login_users_db
215 our $logged_in_user_date_of_expiry = 600;
218 %cfg_defaults = (
219 "general" => {
220 "log-file" => [\$log_file, "/var/run/".$prg.".log"],
221 "pid-file" => [\$pid_file, "/var/run/".$prg.".pid"],
222 },
223 "server" => {
224 "ip" => [\$server_ip, "0.0.0.0"],
225 "port" => [\$server_port, "20081"],
226 "known-clients" => [\$known_clients_file_name, '/var/lib/gosa-si/clients.db' ],
227 "known-servers" => [\$known_server_file_name, '/var/lib/gosa-si/servers.db'],
228 "incoming" => [\$incoming_file_name, '/var/lib/gosa-si/incoming.db'],
229 "login-users" => [\$login_users_file_name, '/var/lib/gosa-si/users.db'],
230 "fai-server" => [\$fai_server_file_name, '/var/lib/gosa-si/fai_server.db'],
231 "fai-release" => [\$fai_release_file_name, '/var/lib/gosa-si/fai_release.db'],
232 "packages-list" => [\$packages_list_file_name, '/var/lib/gosa-si/packages.db'],
233 "messaging" => [\$messaging_file_name, '/var/lib/gosa-si/messaging.db'],
234 "foreign-clients" => [\$foreign_clients_file_name, '/var/lib/gosa-si/foreign_clients.db'],
235 "source-list" => [\$sources_list, '/etc/apt/sources.list'],
236 "repo-path" => [\$repo_path, '/srv/www/repository'],
237 "ldap-uri" => [\$ldap_uri, ""],
238 "ldap-base" => [\$ldap_base, ""],
239 "ldap-admin-dn" => [\$ldap_admin_dn, ""],
240 "ldap-admin-password" => [\$ldap_admin_password, ""],
241 "gosa-unit-tag" => [\$gosa_unit_tag, ""],
242 "max-clients" => [\$max_clients, 10],
243 "wol-password" => [\$wake_on_lan_passwd, ""],
244 },
245 "GOsaPackages" => {
246 "job-queue" => [\$job_queue_file_name, '/var/lib/gosa-si/jobs.db'],
247 "job-queue-loop-delay" => [\$job_queue_loop_delay, 3],
248 "messaging-db-loop-delay" => [\$messaging_db_loop_delay, 3],
249 "key" => [\$GosaPackages_key, "none"],
250 },
251 "ClientPackages" => {
252 "key" => [\$ClientPackages_key, "none"],
253 "user-date-of-expiry" => [\$logged_in_user_date_of_expiry, 600],
254 },
255 "ServerPackages"=> {
256 "address" => [\$foreign_server_string, ""],
257 "domain" => [\$server_domain, ""],
258 "key" => [\$ServerPackages_key, "none"],
259 "key-lifetime" => [\$foreign_servers_register_delay, 120],
260 "job-synchronization-enabled" => [\$job_synchronization, "true"],
261 "synchronization-loop" => [\$modified_jobs_loop_delay, 5],
262 },
263 "ArpHandler" => {
264 "enabled" => [\$arp_enabled, "true"],
265 "interface" => [\$arp_interface, "all"],
266 },
267 "Opsi" => {
268 "enabled" => [\$opsi_enabled, "false"],
269 "server" => [\$opsi_server, "localhost"],
270 "admin" => [\$opsi_admin, "opsi-admin"],
271 "password" => [\$opsi_password, "secret"],
272 },
274 );
277 #=== FUNCTION ================================================================
278 # NAME: usage
279 # PARAMETERS: nothing
280 # RETURNS: nothing
281 # DESCRIPTION: print out usage text to STDERR
282 #===============================================================================
283 sub usage {
284 print STDERR << "EOF" ;
285 usage: $prg [-hvf] [-c config]
287 -h : this (help) message
288 -c <file> : config file
289 -f : foreground, process will not be forked to background
290 -v : be verbose (multiple to increase verbosity)
291 -no-arp : starts $prg without connection to arp module
293 EOF
294 print "\n" ;
295 }
298 #=== FUNCTION ================================================================
299 # NAME: logging
300 # PARAMETERS: level - string - default 'info'
301 # msg - string -
302 # facility - string - default 'LOG_DAEMON'
303 # RETURNS: nothing
304 # DESCRIPTION: function for logging
305 #===============================================================================
306 sub daemon_log {
307 # log into log_file
308 my( $msg, $level ) = @_;
309 if(not defined $msg) { return }
310 if(not defined $level) { $level = 1 }
311 if(defined $log_file){
312 open(LOG_HANDLE, ">>$log_file");
313 chmod 0600, $log_file;
314 if(not defined open( LOG_HANDLE, ">>$log_file" )) {
315 print STDERR "cannot open $log_file: $!";
316 return
317 }
318 chomp($msg);
319 #$msg =~s/\n//g; # no newlines are allowed in log messages, this is important for later log parsing
320 if($level <= $verbose){
321 my ($seconds, $minutes, $hours, $monthday, $month,
322 $year, $weekday, $yearday, $sommertime) = localtime(time);
323 $hours = $hours < 10 ? $hours = "0".$hours : $hours;
324 $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
325 $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
326 my @monthnames = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
327 $month = $monthnames[$month];
328 $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
329 $year+=1900;
330 my $name = $prg;
332 my $log_msg = "$month $monthday $hours:$minutes:$seconds $name $msg\n";
333 print LOG_HANDLE $log_msg;
334 if( $foreground ) {
335 print STDERR $log_msg;
336 }
337 }
338 close( LOG_HANDLE );
339 }
340 }
343 #=== FUNCTION ================================================================
344 # NAME: check_cmdline_param
345 # PARAMETERS: nothing
346 # RETURNS: nothing
347 # DESCRIPTION: validates commandline parameter
348 #===============================================================================
349 sub check_cmdline_param () {
350 my $err_config;
351 my $err_counter = 0;
352 if(not defined($cfg_file)) {
353 $cfg_file = "/etc/gosa-si/server.conf";
354 if(! -r $cfg_file) {
355 $err_config = "please specify a config file";
356 $err_counter += 1;
357 }
358 }
359 if( $err_counter > 0 ) {
360 &usage( "", 1 );
361 if( defined( $err_config)) { print STDERR "$err_config\n"}
362 print STDERR "\n";
363 exit( -1 );
364 }
365 }
368 #=== FUNCTION ================================================================
369 # NAME: check_pid
370 # PARAMETERS: nothing
371 # RETURNS: nothing
372 # DESCRIPTION: handels pid processing
373 #===============================================================================
374 sub check_pid {
375 $pid = -1;
376 # Check, if we are already running
377 if( open(LOCK_FILE, "<$pid_file") ) {
378 $pid = <LOCK_FILE>;
379 if( defined $pid ) {
380 chomp( $pid );
381 if( -f "/proc/$pid/stat" ) {
382 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
383 if( $stat ) {
384 daemon_log("ERROR: Already running",1);
385 close( LOCK_FILE );
386 exit -1;
387 }
388 }
389 }
390 close( LOCK_FILE );
391 unlink( $pid_file );
392 }
394 # create a syslog msg if it is not to possible to open PID file
395 if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
396 my($msg) = "Couldn't obtain lockfile '$pid_file' ";
397 if (open(LOCK_FILE, '<', $pid_file)
398 && ($pid = <LOCK_FILE>))
399 {
400 chomp($pid);
401 $msg .= "(PID $pid)\n";
402 } else {
403 $msg .= "(unable to read PID)\n";
404 }
405 if( ! ($foreground) ) {
406 openlog( $0, "cons,pid", "daemon" );
407 syslog( "warning", $msg );
408 closelog();
409 }
410 else {
411 print( STDERR " $msg " );
412 }
413 exit( -1 );
414 }
415 }
417 #=== FUNCTION ================================================================
418 # NAME: import_modules
419 # PARAMETERS: module_path - string - abs. path to the directory the modules
420 # are stored
421 # RETURNS: nothing
422 # DESCRIPTION: each file in module_path which ends with '.pm' and activation
423 # state is on is imported by "require 'file';"
424 #===============================================================================
425 sub import_modules {
426 daemon_log(" ", 1);
428 if (not -e $modules_path) {
429 daemon_log("0 ERROR: cannot find directory or directory is not readable: $modules_path", 1);
430 }
432 opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
433 while (defined (my $file = readdir (DIR))) {
434 if (not $file =~ /(\S*?).pm$/) {
435 next;
436 }
437 my $mod_name = $1;
439 # ArpHandler switch
440 if( $file =~ /ArpHandler.pm/ ) {
441 if( $arp_enabled eq "false" ) { next; }
442 }
444 eval { require $file; };
445 if ($@) {
446 daemon_log("0 ERROR: gosa-si-server could not load module $file", 1);
447 daemon_log("$@", 5);
448 } else {
449 my $info = eval($mod_name.'::get_module_info()');
450 # Only load module if get_module_info() returns a non-null object
451 if( $info ) {
452 my ($input_address, $input_key, $event_hash) = @{$info};
453 $known_modules->{$mod_name} = $info;
454 daemon_log("0 INFO: module $mod_name loaded", 5);
455 }
456 }
457 }
459 close (DIR);
460 }
462 #=== FUNCTION ================================================================
463 # NAME: password_check
464 # PARAMETERS: nothing
465 # RETURNS: nothing
466 # DESCRIPTION: escalates an critical error if two modules exist which are avaialable by
467 # the same password
468 #===============================================================================
469 sub password_check {
470 my $passwd_hash = {};
471 while (my ($mod_name, $mod_info) = each %$known_modules) {
472 my $mod_passwd = @$mod_info[1];
473 if (not defined $mod_passwd) { next; }
474 if (not exists $passwd_hash->{$mod_passwd}) {
475 $passwd_hash->{$mod_passwd} = $mod_name;
477 # escalates critical error
478 } else {
479 &daemon_log("0 ERROR: two loaded modules do have the same password. Please modify the 'key'-parameter in config file");
480 &daemon_log("0 ERROR: module='$mod_name' and module='".$passwd_hash->{$mod_passwd}."'");
481 exit( -1 );
482 }
483 }
485 }
488 #=== FUNCTION ================================================================
489 # NAME: sig_int_handler
490 # PARAMETERS: signal - string - signal arose from system
491 # RETURNS: nothing
492 # DESCRIPTION: handels tasks to be done befor signal becomes active
493 #===============================================================================
494 sub sig_int_handler {
495 my ($signal) = @_;
497 # if (defined($ldap_handle)) {
498 # $ldap_handle->disconnect;
499 # }
500 # TODO alle verbliebenden ldap verbindungen aus allen heaps beenden
503 daemon_log("shutting down gosa-si-server", 1);
504 system("kill `ps -C gosa-si-server -o pid=`");
505 }
506 $SIG{INT} = \&sig_int_handler;
509 sub check_key_and_xml_validity {
510 my ($crypted_msg, $module_key, $session_id) = @_;
511 my $msg;
512 my $msg_hash;
513 my $error_string;
514 eval{
515 $msg = &decrypt_msg($crypted_msg, $module_key);
517 if ($msg =~ /<xml>/i){
518 $msg =~ s/\s+/ /g; # just for better daemon_log
519 daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 8);
520 $msg_hash = $xml->XMLin($msg, ForceArray=>1);
522 ##############
523 # check header
524 if( not exists $msg_hash->{'header'} ) { die "no header specified"; }
525 my $header_l = $msg_hash->{'header'};
526 if( 1 > @{$header_l} ) { die 'empty header tag'; }
527 if( 1 < @{$header_l} ) { die 'more than one header specified'; }
528 my $header = @{$header_l}[0];
529 if( 0 == length $header) { die 'empty string in header tag'; }
531 ##############
532 # check source
533 if( not exists $msg_hash->{'source'} ) { die "no source specified"; }
534 my $source_l = $msg_hash->{'source'};
535 if( 1 > @{$source_l} ) { die 'empty source tag'; }
536 if( 1 < @{$source_l} ) { die 'more than one source specified'; }
537 my $source = @{$source_l}[0];
538 if( 0 == length $source) { die 'source error'; }
540 ##############
541 # check target
542 if( not exists $msg_hash->{'target'} ) { die "no target specified"; }
543 my $target_l = $msg_hash->{'target'};
544 if( 1 > @{$target_l} ) { die 'empty target tag'; }
545 }
546 };
547 if($@) {
548 daemon_log("$session_id ERROR: do not understand the message: $@", 1);
549 $msg = undef;
550 $msg_hash = undef;
551 }
553 return ($msg, $msg_hash);
554 }
557 sub check_outgoing_xml_validity {
558 my ($msg, $session_id) = @_;
560 my $msg_hash;
561 eval{
562 $msg_hash = $xml->XMLin($msg, ForceArray=>1);
564 ##############
565 # check header
566 my $header_l = $msg_hash->{'header'};
567 if( 1 != @{$header_l} ) {
568 die 'no or more than one headers specified';
569 }
570 my $header = @{$header_l}[0];
571 if( 0 == length $header) {
572 die 'header has length 0';
573 }
575 ##############
576 # check source
577 my $source_l = $msg_hash->{'source'};
578 if( 1 != @{$source_l} ) {
579 die 'no or more than 1 sources specified';
580 }
581 my $source = @{$source_l}[0];
582 if( 0 == length $source) {
583 die 'source has length 0';
584 }
585 unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
586 $source =~ /^GOSA$/i ) {
587 die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
588 }
590 ##############
591 # check target
592 my $target_l = $msg_hash->{'target'};
593 if( 0 == @{$target_l} ) {
594 die "no targets specified";
595 }
596 foreach my $target (@$target_l) {
597 if( 0 == length $target) {
598 die "target has length 0";
599 }
600 unless( $target =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
601 $target =~ /^GOSA$/i ||
602 $target =~ /^\*$/ ||
603 $target =~ /KNOWN_SERVER/i ||
604 $target =~ /JOBDB/i ||
605 $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 ){
606 die "target '$target' is not a complete ip-address with port or a valid target name or a mac-address";
607 }
608 }
609 };
610 if($@) {
611 daemon_log("$session_id ERROR: outgoing msg is not gosa-si envelope conform: $@", 1);
612 daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 1);
613 $msg_hash = undef;
614 }
616 return ($msg_hash);
617 }
620 sub input_from_known_server {
621 my ($input, $remote_ip, $session_id) = @_ ;
622 my ($msg, $msg_hash, $module);
624 my $sql_statement= "SELECT * FROM known_server";
625 my $query_res = $known_server_db->select_dbentry( $sql_statement );
627 while( my ($hit_num, $hit) = each %{ $query_res } ) {
628 my $host_name = $hit->{hostname};
629 if( not $host_name =~ "^$remote_ip") {
630 next;
631 }
632 my $host_key = $hit->{hostkey};
633 daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 7);
634 daemon_log("$session_id DEBUG: input_from_known_server: host_key: $host_key", 7);
636 # check if module can open msg envelope with module key
637 my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
638 if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
639 daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 7);
640 daemon_log("$@", 8);
641 next;
642 }
643 else {
644 $msg = $tmp_msg;
645 $msg_hash = $tmp_msg_hash;
646 $module = "ServerPackages";
647 last;
648 }
649 }
651 if( (!$msg) || (!$msg_hash) || (!$module) ) {
652 daemon_log("$session_id DEBUG: Incoming message is not from a known server", 7);
653 }
655 return ($msg, $msg_hash, $module);
656 }
659 sub input_from_known_client {
660 my ($input, $remote_ip, $session_id) = @_ ;
661 my ($msg, $msg_hash, $module);
663 my $sql_statement= "SELECT * FROM known_clients";
664 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
665 while( my ($hit_num, $hit) = each %{ $query_res } ) {
666 my $host_name = $hit->{hostname};
667 if( not $host_name =~ /^$remote_ip:\d*$/) {
668 next;
669 }
670 my $host_key = $hit->{hostkey};
671 &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 7);
672 &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 7);
674 # check if module can open msg envelope with module key
675 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
677 if( (!$msg) || (!$msg_hash) ) {
678 &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 7);
679 &daemon_log("$@", 8);
680 next;
681 }
682 else {
683 $module = "ClientPackages";
684 last;
685 }
686 }
688 if( (!$msg) || (!$msg_hash) || (!$module) ) {
689 &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 7);
690 }
692 return ($msg, $msg_hash, $module);
693 }
696 sub input_from_unknown_host {
697 no strict "refs";
698 my ($input, $session_id) = @_ ;
699 my ($msg, $msg_hash, $module);
700 my $error_string;
702 my %act_modules = %$known_modules;
704 while( my ($mod, $info) = each(%act_modules)) {
706 # check a key exists for this module
707 my $module_key = ${$mod."_key"};
708 if( not defined $module_key ) {
709 if( $mod eq 'ArpHandler' ) {
710 next;
711 }
712 daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
713 next;
714 }
715 daemon_log("$session_id DEBUG: $mod: $module_key", 7);
717 # check if module can open msg envelope with module key
718 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
719 if( (not defined $msg) || (not defined $msg_hash) ) {
720 next;
721 }
722 else {
723 $module = $mod;
724 last;
725 }
726 }
728 if( (!$msg) || (!$msg_hash) || (!$module)) {
729 daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 7);
730 }
732 return ($msg, $msg_hash, $module);
733 }
736 sub create_ciphering {
737 my ($passwd) = @_;
738 if((!defined($passwd)) || length($passwd)==0) {
739 $passwd = "";
740 }
741 $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
742 my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
743 my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
744 $my_cipher->set_iv($iv);
745 return $my_cipher;
746 }
749 sub encrypt_msg {
750 my ($msg, $key) = @_;
751 my $my_cipher = &create_ciphering($key);
752 my $len;
753 {
754 use bytes;
755 $len= 16-length($msg)%16;
756 }
757 $msg = "\0"x($len).$msg;
758 $msg = $my_cipher->encrypt($msg);
759 chomp($msg = &encode_base64($msg));
760 # there are no newlines allowed inside msg
761 $msg=~ s/\n//g;
762 return $msg;
763 }
766 sub decrypt_msg {
768 my ($msg, $key) = @_ ;
769 $msg = &decode_base64($msg);
770 my $my_cipher = &create_ciphering($key);
771 $msg = $my_cipher->decrypt($msg);
772 $msg =~ s/\0*//g;
773 return $msg;
774 }
777 sub get_encrypt_key {
778 my ($target) = @_ ;
779 my $encrypt_key;
780 my $error = 0;
782 # target can be in known_server
783 if( not defined $encrypt_key ) {
784 my $sql_statement= "SELECT * FROM known_server WHERE hostname='$target'";
785 my $query_res = $known_server_db->select_dbentry( $sql_statement );
786 while( my ($hit_num, $hit) = each %{ $query_res } ) {
787 my $host_name = $hit->{hostname};
788 if( $host_name ne $target ) {
789 next;
790 }
791 $encrypt_key = $hit->{hostkey};
792 last;
793 }
794 }
796 # target can be in known_client
797 if( not defined $encrypt_key ) {
798 my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$target'";
799 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
800 while( my ($hit_num, $hit) = each %{ $query_res } ) {
801 my $host_name = $hit->{hostname};
802 if( $host_name ne $target ) {
803 next;
804 }
805 $encrypt_key = $hit->{hostkey};
806 last;
807 }
808 }
810 return $encrypt_key;
811 }
814 #=== FUNCTION ================================================================
815 # NAME: open_socket
816 # PARAMETERS: PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
817 # [PeerPort] string necessary if port not appended by PeerAddr
818 # RETURNS: socket IO::Socket::INET
819 # DESCRIPTION: open a socket to PeerAddr
820 #===============================================================================
821 sub open_socket {
822 my ($PeerAddr, $PeerPort) = @_ ;
823 if(defined($PeerPort)){
824 $PeerAddr = $PeerAddr.":".$PeerPort;
825 }
826 my $socket;
827 $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
828 Porto => "tcp",
829 Type => SOCK_STREAM,
830 Timeout => 5,
831 );
832 if(not defined $socket) {
833 return;
834 }
835 # &daemon_log("DEBUG: open_socket: $PeerAddr", 7);
836 return $socket;
837 }
840 #sub get_local_ip_for_remote_ip {
841 # my $remote_ip= shift;
842 # my $result="0.0.0.0";
843 #
844 # if($remote_ip =~ /^(\d\d?\d?\.){3}\d\d?\d?$/) {
845 # if($remote_ip eq "127.0.0.1") {
846 # $result = "127.0.0.1";
847 # } else {
848 # my $PROC_NET_ROUTE= ('/proc/net/route');
849 #
850 # open(PROC_NET_ROUTE, "<$PROC_NET_ROUTE")
851 # or die "Could not open $PROC_NET_ROUTE";
852 #
853 # my @ifs = <PROC_NET_ROUTE>;
854 #
855 # close(PROC_NET_ROUTE);
856 #
857 # # Eat header line
858 # shift @ifs;
859 # chomp @ifs;
860 # foreach my $line(@ifs) {
861 # my ($Iface,$Destination,$Gateway,$Flags,$RefCnt,$Use,$Metric,$Mask,$MTU,$Window,$IRTT)=split(/\s/, $line);
862 # my $destination;
863 # my $mask;
864 # my ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Destination);
865 # $destination= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
866 # ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Mask);
867 # $mask= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
868 # if(new NetAddr::IP($remote_ip)->within(new NetAddr::IP($destination, $mask))) {
869 # # destination matches route, save mac and exit
870 # $result= &get_ip($Iface);
871 # last;
872 # }
873 # }
874 # }
875 # } else {
876 # daemon_log("0 WARNING: get_local_ip_for_remote_ip() was called with a non-ip parameter: '$remote_ip'", 1);
877 # }
878 # return $result;
879 #}
882 sub send_msg_to_target {
883 my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
884 my $error = 0;
885 my $header;
886 my $timestamp = &get_time();
887 my $new_status;
888 my $act_status;
889 my ($sql_statement, $res);
891 if( $msg_header ) {
892 $header = "'$msg_header'-";
893 } else {
894 $header = "";
895 }
897 # Patch the source ip
898 if($msg =~ /<source>0\.0\.0\.0:\d*?<\/source>/) {
899 my $remote_ip = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
900 $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$remote_ip:$2<\/source>/s;
901 }
903 # encrypt xml msg
904 my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
906 # opensocket
907 my $socket = &open_socket($address);
908 if( !$socket ) {
909 daemon_log("$session_id WARNING: cannot send ".$header."msg to $address , host not reachable", 3);
910 $error++;
911 }
913 if( $error == 0 ) {
914 # send xml msg
915 print $socket $crypted_msg."\n";
917 daemon_log("$session_id INFO: send ".$header."msg to $address", 5);
918 daemon_log("$session_id DEBUG: message:\n$msg", 9);
920 }
922 # close socket in any case
923 if( $socket ) {
924 close $socket;
925 }
927 if( $error > 0 ) { $new_status = "down"; }
928 else { $new_status = $msg_header; }
931 # known_clients
932 $sql_statement = "SELECT * FROM $known_clients_tn WHERE hostname='$address'";
933 $res = $known_clients_db->select_dbentry($sql_statement);
934 if( keys(%$res) == 1) {
935 $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
936 if ($act_status eq "down" && $new_status eq "down") {
937 $sql_statement = "DELETE FROM known_clients WHERE hostname='$address'";
938 $res = $known_clients_db->del_dbentry($sql_statement);
939 daemon_log("$session_id WARNING: failed 2x to send msg to host '$address', delete host from known_clients", 3);
940 } else {
941 $sql_statement = "UPDATE known_clients SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
942 $res = $known_clients_db->update_dbentry($sql_statement);
943 if($new_status eq "down"){
944 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
945 } else {
946 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
947 }
948 }
949 }
951 # known_server
952 $sql_statement = "SELECT * FROM $known_server_tn WHERE hostname='$address'";
953 $res = $known_server_db->select_dbentry($sql_statement);
954 if( keys(%$res) == 1) {
955 $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
956 if ($act_status eq "down" && $new_status eq "down") {
957 $sql_statement = "DELETE FROM known_server WHERE hostname='$address'";
958 $res = $known_server_db->del_dbentry($sql_statement);
959 daemon_log("$session_id WARNING: failed 2x to send a message to host '$address', delete host from known_server", 3);
960 }
961 else {
962 $sql_statement = "UPDATE known_server SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
963 $res = $known_server_db->update_dbentry($sql_statement);
964 if($new_status eq "down"){
965 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
966 } else {
967 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
968 }
969 }
970 }
971 return $error;
972 }
975 sub update_jobdb_status_for_send_msgs {
976 my ($answer, $error) = @_;
977 if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
978 my $jobdb_id = $1;
980 # sending msg faild
981 if( $error ) {
982 if (not $answer =~ /<header>trigger_action_reinstall<\/header>/) {
983 my $sql_statement = "UPDATE $job_queue_tn ".
984 "SET status='error', result='can not deliver msg, please consult log file' ".
985 "WHERE id=$jobdb_id";
986 my $res = $job_db->update_dbentry($sql_statement);
987 }
989 # sending msg was successful
990 } else {
991 my $sql_statement = "UPDATE $job_queue_tn ".
992 "SET status='done' ".
993 "WHERE id=$jobdb_id AND status='processed'";
994 my $res = $job_db->update_dbentry($sql_statement);
995 }
996 }
997 }
1000 sub sig_handler {
1001 my ($kernel, $signal) = @_[KERNEL, ARG0] ;
1002 daemon_log("0 INFO got signal '$signal'", 1);
1003 $kernel->sig_handled();
1004 return;
1005 }
1008 sub msg_to_decrypt {
1009 my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
1010 my $session_id = $session->ID;
1011 my ($msg, $msg_hash, $module);
1012 my $error = 0;
1014 # hole neue msg aus @msgs_to_decrypt
1015 my $next_msg = shift @msgs_to_decrypt;
1017 # entschlüssle sie
1019 # msg is from a new client or gosa
1020 ($msg, $msg_hash, $module) = &input_from_unknown_host($next_msg, $session_id);
1021 # msg is from a gosa-si-server
1022 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1023 ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $heap->{'remote_ip'}, $session_id);
1024 }
1025 # msg is from a gosa-si-client
1026 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1027 ($msg, $msg_hash, $module) = &input_from_known_client($next_msg, $heap->{'remote_ip'}, $session_id);
1028 }
1029 # an error occurred
1030 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1031 # if an incoming msg could not be decrypted (maybe a wrong key), send client a ping. If the client
1032 # could not understand a msg from its server the client cause a re-registering process
1033 daemon_log("$session_id WARNING cannot understand incoming msg, send 'ping'-msg to all host with ip '".$heap->{remote_ip}.
1034 "' to cause a re-registering of the client if necessary", 3);
1035 my $sql_statement = "SELECT * FROM $main::known_clients_tn WHERE (hostname LIKE '".$heap->{'remote_ip'}."%')";
1036 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1037 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1038 my $host_name = $hit->{'hostname'};
1039 my $host_key = $hit->{'hostkey'};
1040 my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source> <target>$host_name</target></xml>";
1041 my $error = &send_msg_to_target($ping_msg, $host_name, $host_key, "gosa_ping", $session_id);
1042 &update_jobdb_status_for_send_msgs($ping_msg, $error);
1043 }
1044 $error++;
1045 }
1048 my $header;
1049 my $target;
1050 my $source;
1051 my $done = 0;
1052 my $sql;
1053 my $res;
1055 # check whether this message should be processed here
1056 if ($error == 0) {
1057 $header = @{$msg_hash->{'header'}}[0];
1058 $target = @{$msg_hash->{'target'}}[0];
1059 $source = @{$msg_hash->{'source'}}[0];
1060 my $not_found_in_known_clients_db = 0;
1061 my $not_found_in_known_server_db = 0;
1062 my $not_found_in_foreign_clients_db = 0;
1063 my $local_address;
1064 my $local_mac;
1065 my ($target_ip, $target_port) = split(':', $target);
1067 # Determine the local ip address if target is an ip address
1068 if ($target =~ /^\d+\.\d+\.\d+\.\d+:\d+$/) {
1069 $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1070 } else {
1071 $local_address = $server_address;
1072 }
1074 # Determine the local mac address if target is a mac address
1075 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) {
1076 my $loc_ip = &get_local_ip_for_remote_ip($heap->{'remote_ip'});
1077 my $network_interface= &get_interface_for_ip($loc_ip);
1078 $local_mac = &get_mac_for_interface($network_interface);
1079 } else {
1080 $local_mac = $server_mac_address;
1081 }
1083 # target and source is equal to GOSA -> process here
1084 if (not $done) {
1085 if ($target eq "GOSA" && $source eq "GOSA") {
1086 $done = 1;
1087 }
1088 }
1090 # target is own address without forward_to_gosa-tag -> process here
1091 if (not $done) {
1092 #if ((($target eq $local_address) || ($target eq $local_mac) ) && (not exists $msg_hash->{'forward_to_gosa'})) {
1093 if (($target eq $local_address) && (not exists $msg_hash->{'forward_to_gosa'})) {
1094 $done = 1;
1095 if ($source eq "GOSA") {
1096 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1097 }
1098 #print STDERR "target is own address without forward_to_gosa-tag -> process here\n";
1099 }
1100 }
1102 # target is a client address in known_clients -> process here
1103 if (not $done) {
1104 $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1105 $res = $known_clients_db->select_dbentry($sql);
1106 if (keys(%$res) > 0) {
1107 $done = 1;
1108 my $hostname = $res->{1}->{'hostname'};
1109 $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1110 #print STDERR "target is a client address in known_clients -> process here\n";
1111 my $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1112 if ($source eq "GOSA") {
1113 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1114 }
1116 } else {
1117 $not_found_in_known_clients_db = 1;
1118 }
1119 }
1121 # target ist own address with forward_to_gosa-tag not pointing to myself -> process here
1122 if (not $done) {
1123 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1124 my $gosa_at;
1125 my $gosa_session_id;
1126 if (($target eq $local_address) && (defined $forward_to_gosa)){
1127 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1128 if ($gosa_at ne $local_address) {
1129 $done = 1;
1130 #print STDERR "target is own address with forward_to_gosa-tag not pointing to myself -> process here\n";
1131 }
1132 }
1133 }
1135 # if message should be processed here -> add message to incoming_db
1136 if ($done) {
1137 # if a job or a gosa message comes from a foreign server, fake module to GosaPackages
1138 # so gosa-si-server knows how to process this kind of messages
1139 if ($header =~ /^gosa_/ || $header =~ /^job_/) {
1140 $module = "GosaPackages";
1141 }
1143 my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1144 primkey=>[],
1145 headertag=>$header,
1146 targettag=>$target,
1147 xmlmessage=>&encode_base64($msg),
1148 timestamp=>&get_time,
1149 module=>$module,
1150 sessionid=>$session_id,
1151 } );
1152 }
1154 # target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa
1155 if (not $done) {
1156 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1157 my $gosa_at;
1158 my $gosa_session_id;
1159 if (($target eq $local_address) && (defined $forward_to_gosa)){
1160 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1161 if ($gosa_at eq $local_address) {
1162 my $session_reference = $kernel->ID_id_to_session($gosa_session_id);
1163 if( defined $session_reference ) {
1164 $heap = $session_reference->get_heap();
1165 }
1166 if(exists $heap->{'client'}) {
1167 $msg = &encrypt_msg($msg, $GosaPackages_key);
1168 $heap->{'client'}->put($msg);
1169 &daemon_log("$session_id INFO: incoming '$header' message forwarded to GOsa", 5);
1170 }
1171 $done = 1;
1172 #print STDERR "target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa\n";
1173 }
1174 }
1176 }
1178 # target is a client address in foreign_clients -> forward to registration server
1179 if (not $done) {
1180 $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1181 $res = $foreign_clients_db->select_dbentry($sql);
1182 if (keys(%$res) > 0) {
1183 my $hostname = $res->{1}->{'hostname'};
1184 my ($host_ip, $host_port) = split(/:/, $hostname);
1185 my $local_address = &get_local_ip_for_remote_ip($host_ip).":$server_port";
1186 my $regserver = $res->{1}->{'regserver'};
1187 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$regserver'";
1188 my $res = $known_server_db->select_dbentry($sql);
1189 if (keys(%$res) > 0) {
1190 my $regserver_key = $res->{1}->{'hostkey'};
1191 $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1192 $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1193 if ($source eq "GOSA") {
1194 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1195 }
1196 &send_msg_to_target($msg, $regserver, $regserver_key, $header, $session_id);
1197 }
1198 $done = 1;
1199 #print STDERR "target is a client address in foreign_clients -> forward to registration server\n";
1200 } else {
1201 $not_found_in_foreign_clients_db = 1;
1202 }
1203 }
1205 # target is a server address -> forward to server
1206 if (not $done) {
1207 $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1208 $res = $known_server_db->select_dbentry($sql);
1209 if (keys(%$res) > 0) {
1210 my $hostkey = $res->{1}->{'hostkey'};
1212 if ($source eq "GOSA") {
1213 $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1214 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1216 }
1218 &send_msg_to_target($msg, $target, $hostkey, $header, $session_id);
1219 $done = 1;
1220 } else {
1221 $not_found_in_known_server_db = 1;
1222 }
1223 }
1226 # target is not in foreign_clients_db, known_server_db or known_clients_db, maybe it is a complete new one -> process here
1227 if ( $not_found_in_foreign_clients_db
1228 && $not_found_in_known_server_db
1229 && $not_found_in_known_clients_db) {
1230 my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1231 primkey=>[],
1232 headertag=>$header,
1233 targettag=>$target,
1234 xmlmessage=>&encode_base64($msg),
1235 timestamp=>&get_time,
1236 module=>$module,
1237 sessionid=>$session_id,
1238 } );
1239 $done = 1;
1240 }
1243 if (not $done) {
1244 daemon_log("$session_id ERROR: do not know what to do with this message: $msg", 1);
1245 if ($source eq "GOSA") {
1246 my %data = ('error_msg' => &encode_base64($msg), 'error_string' => "Do not know what to do with this message!");
1247 my $error_msg = &build_msg("error", $local_address, "GOSA", \%data );
1249 my $session_reference = $kernel->ID_id_to_session($session_id);
1250 if( defined $session_reference ) {
1251 $heap = $session_reference->get_heap();
1252 }
1253 if(exists $heap->{'client'}) {
1254 $error_msg = &encrypt_msg($error_msg, $GosaPackages_key);
1255 $heap->{'client'}->put($error_msg);
1256 }
1257 }
1258 }
1260 }
1262 return;
1263 }
1266 sub next_task {
1267 my ($session, $heap, $task) = @_[SESSION, HEAP, ARG0];
1268 my $running_task = POE::Wheel::Run->new(
1269 Program => sub { process_task($session, $heap, $task) },
1270 StdioFilter => POE::Filter::Reference->new(),
1271 StdoutEvent => "task_result",
1272 StderrEvent => "task_debug",
1273 CloseEvent => "task_done",
1274 );
1275 $heap->{task}->{ $running_task->ID } = $running_task;
1276 }
1278 sub handle_task_result {
1279 my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
1280 my $client_answer = $result->{'answer'};
1281 if( $client_answer =~ s/session_id=(\d+)$// ) {
1282 my $session_id = $1;
1283 if( defined $session_id ) {
1284 my $session_reference = $kernel->ID_id_to_session($session_id);
1285 if( defined $session_reference ) {
1286 $heap = $session_reference->get_heap();
1287 }
1288 }
1290 if(exists $heap->{'client'}) {
1291 $heap->{'client'}->put($client_answer);
1292 }
1293 }
1294 $kernel->sig(CHLD => "child_reap");
1295 }
1297 sub handle_task_debug {
1298 my $result = $_[ARG0];
1299 print STDERR "$result\n";
1300 }
1302 sub handle_task_done {
1303 my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1304 delete $heap->{task}->{$task_id};
1305 }
1307 sub process_task {
1308 no strict "refs";
1309 #CHECK: Not @_[...]?
1310 my ($session, $heap, $task) = @_;
1311 my $error = 0;
1312 my $answer_l;
1313 my ($answer_header, @answer_target_l, $answer_source);
1314 my $client_answer = "";
1316 # prepare all variables needed to process message
1317 #my $msg = $task->{'xmlmessage'};
1318 my $msg = &decode_base64($task->{'xmlmessage'});
1319 my $incoming_id = $task->{'id'};
1320 my $module = $task->{'module'};
1321 my $header = $task->{'headertag'};
1322 my $session_id = $task->{'sessionid'};
1323 my $msg_hash = $xml->XMLin($msg, ForceArray=>1);
1324 my $source = @{$msg_hash->{'source'}}[0];
1326 # set timestamp of incoming client uptodate, so client will not
1327 # be deleted from known_clients because of expiration
1328 my $act_time = &get_time();
1329 my $sql = "UPDATE $known_clients_tn SET timestamp='$act_time' WHERE hostname='$source'";
1330 my $res = $known_clients_db->exec_statement($sql);
1332 ######################
1333 # process incoming msg
1334 if( $error == 0) {
1335 daemon_log("$session_id INFO: Incoming msg (session_id=$session_id) with header '".@{$msg_hash->{'header'}}[0]."'", 5);
1336 daemon_log("$session_id DEBUG: Processing module ".$module, 7);
1337 $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1339 if ( 0 < @{$answer_l} ) {
1340 my $answer_str = join("\n", @{$answer_l});
1341 while ($answer_str =~ /<header>(\w+)<\/header>/g) {
1342 daemon_log("$session_id INFO: got answer message with header '$1'", 5);
1343 }
1344 daemon_log("$session_id DEBUG: $module: got answer from module: \n".$answer_str,9);
1345 } else {
1346 daemon_log("$session_id DEBUG: $module: got no answer from module!" ,7);
1347 }
1349 }
1350 if( !$answer_l ) { $error++ };
1352 ########
1353 # answer
1354 if( $error == 0 ) {
1356 foreach my $answer ( @{$answer_l} ) {
1357 # check outgoing msg to xml validity
1358 my $answer_hash = &check_outgoing_xml_validity($answer, $session_id);
1359 if( not defined $answer_hash ) { next; }
1361 $answer_header = @{$answer_hash->{'header'}}[0];
1362 @answer_target_l = @{$answer_hash->{'target'}};
1363 $answer_source = @{$answer_hash->{'source'}}[0];
1365 # deliver msg to all targets
1366 foreach my $answer_target ( @answer_target_l ) {
1368 # targets of msg are all gosa-si-clients in known_clients_db
1369 if( $answer_target eq "*" ) {
1370 # answer is for all clients
1371 my $sql_statement= "SELECT * FROM known_clients";
1372 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1373 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1374 my $host_name = $hit->{hostname};
1375 my $host_key = $hit->{hostkey};
1376 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1377 &update_jobdb_status_for_send_msgs($answer, $error);
1378 }
1379 }
1381 # targets of msg are all gosa-si-server in known_server_db
1382 elsif( $answer_target eq "KNOWN_SERVER" ) {
1383 # answer is for all server in known_server
1384 my $sql_statement= "SELECT * FROM $known_server_tn";
1385 my $query_res = $known_server_db->select_dbentry( $sql_statement );
1386 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1387 my $host_name = $hit->{hostname};
1388 my $host_key = $hit->{hostkey};
1389 $answer =~ s/<target>\S+<\/target>/<target>$host_name<\/target>/g;
1390 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1391 &update_jobdb_status_for_send_msgs($answer, $error);
1392 }
1393 }
1395 # target of msg is GOsa
1396 elsif( $answer_target eq "GOSA" ) {
1397 my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1398 my $add_on = "";
1399 if( defined $session_id ) {
1400 $add_on = ".session_id=$session_id";
1401 }
1402 # answer is for GOSA and has to returned to connected client
1403 my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1404 $client_answer = $gosa_answer.$add_on;
1405 }
1407 # target of msg is job queue at this host
1408 elsif( $answer_target eq "JOBDB") {
1409 $answer =~ /<header>(\S+)<\/header>/;
1410 my $header;
1411 if( defined $1 ) { $header = $1; }
1412 my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1413 &update_jobdb_status_for_send_msgs($answer, $error);
1414 }
1416 # Target of msg is a mac address
1417 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 ) {
1418 daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients and foreign_clients", 5);
1419 my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1420 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1421 my $found_ip_flag = 0;
1422 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1423 my $host_name = $hit->{hostname};
1424 my $host_key = $hit->{hostkey};
1425 $answer =~ s/$answer_target/$host_name/g;
1426 daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1427 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1428 &update_jobdb_status_for_send_msgs($answer, $error);
1429 $found_ip_flag++ ;
1430 }
1431 if ($found_ip_flag == 0) {
1432 my $sql = "SELECT * FROM $foreign_clients_tn WHERE macaddress LIKE '$answer_target'";
1433 my $res = $foreign_clients_db->select_dbentry($sql);
1434 while( my ($hit_num, $hit) = each %{ $res } ) {
1435 my $host_name = $hit->{hostname};
1436 my $reg_server = $hit->{regserver};
1437 daemon_log("$session_id INFO: found host '$host_name' with mac '$answer_target', registered at '$reg_server'", 5);
1439 # Fetch key for reg_server
1440 my $reg_server_key;
1441 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$reg_server'";
1442 my $res = $known_server_db->select_dbentry($sql);
1443 if (exists $res->{1}) {
1444 $reg_server_key = $res->{1}->{'hostkey'};
1445 } else {
1446 daemon_log("$session_id ERROR: cannot find hostkey for '$host_name' in '$known_server_tn'", 1);
1447 daemon_log("$session_id ERROR: unable to forward answer to correct registration server, processing is aborted!", 1);
1448 $reg_server_key = undef;
1449 }
1451 # Send answer to server where client is registered
1452 if (defined $reg_server_key) {
1453 $answer =~ s/$answer_target/$host_name/g;
1454 my $error = &send_msg_to_target($answer, $reg_server, $reg_server_key, $answer_header, $session_id);
1455 &update_jobdb_status_for_send_msgs($answer, $error);
1456 $found_ip_flag++ ;
1457 }
1458 }
1459 }
1460 if( $found_ip_flag == 0) {
1461 daemon_log("$session_id WARNING: no host found in known_clients with mac address '$answer_target'", 3);
1462 }
1464 # Answer is for one specific host
1465 } else {
1466 # get encrypt_key
1467 my $encrypt_key = &get_encrypt_key($answer_target);
1468 if( not defined $encrypt_key ) {
1469 # unknown target
1470 daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1471 next;
1472 }
1473 my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1474 &update_jobdb_status_for_send_msgs($answer, $error);
1475 }
1476 }
1477 }
1478 }
1480 my $filter = POE::Filter::Reference->new();
1481 my %result = (
1482 status => "seems ok to me",
1483 answer => $client_answer,
1484 );
1486 my $output = $filter->put( [ \%result ] );
1487 print @$output;
1490 }
1492 sub session_start {
1493 my ($kernel) = $_[KERNEL];
1494 $global_kernel = $kernel;
1495 $kernel->yield('register_at_foreign_servers');
1496 $kernel->yield('create_fai_server_db', $fai_server_tn );
1497 $kernel->yield('create_fai_release_db', $fai_release_tn );
1498 $kernel->yield('watch_for_next_tasks');
1499 $kernel->sig(USR1 => "sig_handler");
1500 $kernel->sig(USR2 => "recreate_packages_db");
1501 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1502 $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay);
1503 $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay);
1504 $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1505 $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1506 $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1507 $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1509 # Start opsi check
1510 if ($opsi_enabled eq "true") {
1511 $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay);
1512 }
1514 }
1517 sub watch_for_done_jobs {
1518 #CHECK: $heap for what?
1519 my ($kernel,$heap) = @_[KERNEL, HEAP];
1521 my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((status='done') AND (modified='0'))";
1522 my $res = $job_db->select_dbentry( $sql_statement );
1524 while( my ($id, $hit) = each %{$res} ) {
1525 my $jobdb_id = $hit->{id};
1526 my $sql_statement = "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1527 my $res = $job_db->del_dbentry($sql_statement);
1528 }
1530 $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1531 }
1534 sub watch_for_opsi_jobs {
1535 my ($kernel) = $_[KERNEL];
1537 # 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
1538 # opsi install job is to parse the xml message. There is still the correct header.
1539 my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((xmlmessage LIKE '%opsi_install_client</header>%') AND (status='processing') AND (siserver='localhost'))";
1540 my $res = $job_db->select_dbentry( $sql_statement );
1542 # Ask OPSI for an update of the running jobs
1543 while (my ($id, $hit) = each %$res ) {
1544 # Determine current parameters of the job
1545 my $hostId = $hit->{'plainname'};
1546 my $macaddress = $hit->{'macaddress'};
1547 my $progress = $hit->{'progress'};
1549 my $result= {};
1551 # For hosts, only return the products that are or get installed
1552 my $callobj;
1553 $callobj = {
1554 method => 'getProductStates_hash',
1555 params => [ $hostId ],
1556 id => 1,
1557 };
1559 my $hres = $opsi_client->call($opsi_url, $callobj);
1560 #my ($hres_err, $hres_err_string) = &check_opsi_res($hres);
1561 if (not &check_opsi_res($hres)) {
1562 my $htmp= $hres->result->{$hostId};
1564 # Check state != not_installed or action == setup -> load and add
1565 my $products= 0;
1566 my $installed= 0;
1567 my $installing = 0;
1568 my $error= 0;
1569 my @installed_list;
1570 my @error_list;
1571 my $act_status = "none";
1572 foreach my $product (@{$htmp}){
1574 if ($product->{'installationStatus'} ne "not_installed" or
1575 $product->{'actionRequest'} eq "setup"){
1577 # Increase number of products for this host
1578 $products++;
1580 if ($product->{'installationStatus'} eq "failed"){
1581 $result->{$product->{'productId'}}= "error";
1582 unshift(@error_list, $product->{'productId'});
1583 $error++;
1584 }
1585 if ($product->{'installationStatus'} eq "installed" && $product->{'actionRequest'} eq "none"){
1586 $result->{$product->{'productId'}}= "installed";
1587 unshift(@installed_list, $product->{'productId'});
1588 $installed++;
1589 }
1590 if ($product->{'installationStatus'} eq "installing"){
1591 $result->{$product->{'productId'}}= "installing";
1592 $installing++;
1593 $act_status = "installing - ".$product->{'productId'};
1594 }
1595 }
1596 }
1598 # Estimate "rough" progress, avoid division by zero
1599 if ($products == 0) {
1600 $result->{'progress'}= 0;
1601 } else {
1602 $result->{'progress'}= int($installed * 100 / $products);
1603 }
1605 # Set updates in job queue
1606 if ((not $error) && (not $installing) && ($installed)) {
1607 $act_status = "installed - ".join(", ", @installed_list);
1608 }
1609 if ($error) {
1610 $act_status = "error - ".join(", ", @error_list);
1611 }
1612 if ($progress ne $result->{'progress'} ) {
1613 # Updating progress and result
1614 my $update_statement = "UPDATE $job_queue_tn SET modified='1', progress='".$result->{'progress'}."', result='$act_status' WHERE macaddress='$macaddress' AND siserver='localhost'";
1615 my $update_res = $job_db->update_dbentry($update_statement);
1616 }
1617 if ($progress eq 100) {
1618 # Updateing status
1619 my $done_statement = "UPDATE $job_queue_tn SET modified='1', ";
1620 if ($error) {
1621 $done_statement .= "status='error'";
1622 } else {
1623 $done_statement .= "status='done'";
1624 }
1625 $done_statement .= " WHERE macaddress='$macaddress' AND siserver='localhost'";
1626 my $done_res = $job_db->update_dbentry($done_statement);
1627 }
1630 }
1631 }
1633 $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay);
1634 }
1637 # If a job got an update or was modified anyway, send to all other si-server an update message of this jobs.
1638 sub watch_for_modified_jobs {
1639 my ($kernel,$heap) = @_[KERNEL, HEAP];
1641 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE ((siserver='localhost') AND (modified='1'))";
1642 my $res = $job_db->select_dbentry( $sql_statement );
1644 # if db contains no jobs which should be update, do nothing
1645 if (keys %$res != 0) {
1647 if ($job_synchronization eq "true") {
1648 # make out of the db result a gosa-si message
1649 my $update_msg = &db_res2si_msg ($res, "foreign_job_updates", "KNOWN_SERVER", "MY_LOCAL_ADDRESS");
1651 # update all other SI-server
1652 &inform_all_other_si_server($update_msg);
1653 }
1655 # set jobs all jobs to modified = 0, wait until the next modification for updates of other si-server
1656 $sql_statement = "UPDATE $job_queue_tn SET modified='0' ";
1657 $res = $job_db->update_dbentry($sql_statement);
1658 }
1660 $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay);
1661 }
1664 sub watch_for_new_jobs {
1665 if($watch_for_new_jobs_in_progress == 0) {
1666 $watch_for_new_jobs_in_progress = 1;
1667 my ($kernel,$heap) = @_[KERNEL, HEAP];
1669 # check gosa job quaeue for jobs with executable timestamp
1670 my $timestamp = &get_time();
1671 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE status='waiting' AND (CAST (timestamp AS INTEGER)) < $timestamp ORDER BY timestamp";
1672 my $res = $job_db->exec_statement( $sql_statement );
1674 # Merge all new jobs that would do the same actions
1675 my @drops;
1676 my $hits;
1677 foreach my $hit (reverse @{$res} ) {
1678 my $macaddress= lc @{$hit}[8];
1679 my $headertag= @{$hit}[5];
1680 if(
1681 defined($hits->{$macaddress}) &&
1682 defined($hits->{$macaddress}->{$headertag}) &&
1683 defined($hits->{$macaddress}->{$headertag}[0])
1684 ) {
1685 push @drops, "DELETE FROM $job_queue_tn WHERE id = $hits->{$macaddress}->{$headertag}[0]";
1686 }
1687 $hits->{$macaddress}->{$headertag}= $hit;
1688 }
1690 # Delete new jobs with a matching job in state 'processing'
1691 foreach my $macaddress (keys %{$hits}) {
1692 foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1693 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1694 if(defined($jobdb_id)) {
1695 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND headertag='$jobdb_headertag' AND status='processing'";
1696 my $res = $job_db->exec_statement( $sql_statement );
1697 foreach my $hit (@{$res}) {
1698 push @drops, "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1699 }
1700 } else {
1701 daemon_log("J ERROR: Job without id exists for macaddress $macaddress!", 1);
1702 }
1703 }
1704 }
1706 # Commit deletion
1707 $job_db->exec_statementlist(\@drops);
1709 # Look for new jobs that could be executed
1710 foreach my $macaddress (keys %{$hits}) {
1712 # Look if there is an executing job
1713 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='processing'";
1714 my $res = $job_db->exec_statement( $sql_statement );
1716 # Skip new jobs for host if there is a processing job
1717 if(defined($res) and defined @{$res}[0]) {
1718 next;
1719 }
1721 foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1722 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1723 if(defined($jobdb_id)) {
1724 my $job_msg = @{$hits->{$macaddress}->{$jobdb_headertag}}[7];
1726 daemon_log("J DEBUG: its time to execute $job_msg", 7);
1727 my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1728 my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1730 # expect macaddress is unique!!!!!!
1731 my $target = $res_hash->{1}->{hostname};
1733 # change header
1734 $job_msg =~ s/<header>job_/<header>gosa_/;
1736 # add sqlite_id
1737 $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1739 $job_msg =~ /<header>(\S+)<\/header>/;
1740 my $header = $1 ;
1741 my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");
1743 # update status in job queue to 'processing'
1744 $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
1745 my $res = $job_db->update_dbentry($sql_statement);
1746 # TODO: abfangen ob alles in ordnung ist oder nicht, wenn nicht error schmeißen
1748 # We don't want parallel processing
1749 last;
1750 }
1751 }
1752 }
1754 $watch_for_new_jobs_in_progress = 0;
1755 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1756 }
1757 }
1760 sub watch_for_new_messages {
1761 my ($kernel,$heap) = @_[KERNEL, HEAP];
1762 my @coll_user_msg; # collection list of outgoing messages
1764 # check messaging_db for new incoming messages with executable timestamp
1765 my $timestamp = &get_time();
1766 my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS INTEGER))<$timestamp AND flag='n' AND direction='in' )";
1767 my $res = $messaging_db->exec_statement( $sql_statement );
1768 foreach my $hit (@{$res}) {
1770 # create outgoing messages
1771 my $message_to = @{$hit}[3];
1772 # translate message_to to plain login name
1773 my @message_to_l = split(/,/, $message_to);
1774 my %receiver_h;
1775 foreach my $receiver (@message_to_l) {
1776 if ($receiver =~ /^u_([\s\S]*)$/) {
1777 $receiver_h{$1} = 0;
1778 } elsif ($receiver =~ /^g_([\s\S]*)$/) {
1779 my $group_name = $1;
1780 # fetch all group members from ldap and add them to receiver hash
1781 my $ldap_handle = &get_ldap_handle();
1782 if (defined $ldap_handle) {
1783 my $mesg = $ldap_handle->search(
1784 base => $ldap_base,
1785 scope => 'sub',
1786 attrs => ['memberUid'],
1787 filter => "cn=$group_name",
1788 );
1789 if ($mesg->count) {
1790 my @entries = $mesg->entries;
1791 foreach my $entry (@entries) {
1792 my @receivers= $entry->get_value("memberUid");
1793 foreach my $receiver (@receivers) {
1794 $receiver_h{$1} = 0;
1795 }
1796 }
1797 }
1798 # translating errors ?
1799 if ($mesg->code) {
1800 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: $mesg->error", 1);
1801 }
1802 # ldap handle error ?
1803 } else {
1804 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: no ldap handle available", 1);
1805 }
1806 } else {
1807 my $sbjct = &encode_base64(@{$hit}[1]);
1808 my $msg = &encode_base64(@{$hit}[7]);
1809 &daemon_log("M WARNING: unknown receiver '$receiver' for a user-message '$sbjct - $msg'", 3);
1810 }
1811 }
1812 my @receiver_l = keys(%receiver_h);
1814 my $message_id = @{$hit}[0];
1816 #add each outgoing msg to messaging_db
1817 my $receiver;
1818 foreach $receiver (@receiver_l) {
1819 my $sql_statement = "INSERT INTO $messaging_tn (id, subject, message_from, message_to, flag, direction, delivery_time, message, timestamp) ".
1820 "VALUES ('".
1821 $message_id."', '". # id
1822 @{$hit}[1]."', '". # subject
1823 @{$hit}[2]."', '". # message_from
1824 $receiver."', '". # message_to
1825 "none"."', '". # flag
1826 "out"."', '". # direction
1827 @{$hit}[6]."', '". # delivery_time
1828 @{$hit}[7]."', '". # message
1829 $timestamp."'". # timestamp
1830 ")";
1831 &daemon_log("M DEBUG: $sql_statement", 1);
1832 my $res = $messaging_db->exec_statement($sql_statement);
1833 &daemon_log("M INFO: message '".@{$hit}[0]."' is prepared for delivery to receiver '$receiver'", 5);
1834 }
1836 # set incoming message to flag d=deliverd
1837 $sql_statement = "UPDATE $messaging_tn SET flag='p' WHERE id='$message_id'";
1838 &daemon_log("M DEBUG: $sql_statement", 7);
1839 $res = $messaging_db->update_dbentry($sql_statement);
1840 &daemon_log("M INFO: message '$message_id' is set to flag 'p' (processed)", 5);
1841 }
1843 $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1844 return;
1845 }
1847 sub watch_for_delivery_messages {
1848 my ($kernel, $heap) = @_[KERNEL, HEAP];
1850 # select outgoing messages
1851 my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( flag='p' AND direction='out' )";
1852 #&daemon_log("0 DEBUG: $sql", 7);
1853 my $res = $messaging_db->exec_statement( $sql_statement );
1855 # build out msg for each usr
1856 foreach my $hit (@{$res}) {
1857 my $receiver = @{$hit}[3];
1858 my $msg_id = @{$hit}[0];
1859 my $subject = @{$hit}[1];
1860 my $message = @{$hit}[7];
1862 # resolve usr -> host where usr is logged in
1863 my $sql = "SELECT * FROM $login_users_tn WHERE (user='$receiver')";
1864 #&daemon_log("0 DEBUG: $sql", 7);
1865 my $res = $login_users_db->exec_statement($sql);
1867 # reciver is logged in nowhere
1868 if (not ref(@$res[0]) eq "ARRAY") { next; }
1870 my $send_succeed = 0;
1871 foreach my $hit (@$res) {
1872 my $receiver_host = @$hit[0];
1873 my $delivered2host = 0;
1874 &daemon_log("M DEBUG: user '$receiver' is logged in at host '$receiver_host'", 7);
1876 # Looking for host in know_clients_db
1877 my $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$receiver_host')";
1878 my $res = $known_clients_db->exec_statement($sql);
1880 # Host is known in known_clients_db
1881 if (ref(@$res[0]) eq "ARRAY") {
1882 my $receiver_key = @{@{$res}[0]}[2];
1883 my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
1884 my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data );
1885 my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0);
1886 if ($error == 0 ) {
1887 $send_succeed++ ;
1888 $delivered2host++ ;
1889 &daemon_log("M DEBUG: send message for user '$receiver' to host '$receiver_host'", 7);
1890 } else {
1891 &daemon_log("M DEBUG: cannot send message for user '$receiver' to host '$receiver_host'", 7);
1892 }
1893 }
1895 # Message already send, do not need to do anything more, otherwise ...
1896 if ($delivered2host) { next;}
1898 # ...looking for host in foreign_clients_db
1899 $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$receiver_host')";
1900 $res = $foreign_clients_db->exec_statement($sql);
1902 # Host is known in foreign_clients_db
1903 if (ref(@$res[0]) eq "ARRAY") {
1904 my $registration_server = @{@{$res}[0]}[2];
1906 # Fetch encryption key for registration server
1907 my $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$registration_server')";
1908 my $res = $known_server_db->exec_statement($sql);
1909 if (ref(@$res[0]) eq "ARRAY") {
1910 my $registration_server_key = @{@{$res}[0]}[3];
1911 my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
1912 my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data );
1913 my $error = &send_msg_to_target($out_msg, $registration_server, $registration_server_key, "usr_msg", 0);
1914 if ($error == 0 ) {
1915 $send_succeed++ ;
1916 $delivered2host++ ;
1917 &daemon_log("M DEBUG: send message for user '$receiver' to server '$registration_server'", 7);
1918 } else {
1919 &daemon_log("M ERROR: cannot send message for user '$receiver' to server '$registration_server'", 1);
1920 }
1922 } else {
1923 &daemon_log("M ERROR: host '$receiver_host' is reported to be ".
1924 "registrated at server '$registration_server', ".
1925 "but no data available in known_server_db ", 1);
1926 }
1927 }
1929 if (not $delivered2host) {
1930 &daemon_log("M ERROR: unable to send user message to host '$receiver_host'", 1);
1931 }
1932 }
1934 if ($send_succeed) {
1935 # set outgoing msg at db to deliverd
1936 my $sql = "UPDATE $messaging_tn SET flag='d' WHERE (id='$msg_id' AND direction='out' AND message_to='$receiver')";
1937 my $res = $messaging_db->exec_statement($sql);
1938 &daemon_log("M INFO: send message for user '$receiver' to logged in hosts", 5);
1939 } else {
1940 &daemon_log("M WARNING: failed to deliver message for user '$receiver'", 3);
1941 }
1942 }
1944 $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1945 return;
1946 }
1949 sub watch_for_done_messages {
1950 my ($kernel,$heap) = @_[KERNEL, HEAP];
1952 my $sql = "SELECT * FROM $messaging_tn WHERE (flag='p' AND direction='in')";
1953 #&daemon_log("0 DEBUG: $sql", 7);
1954 my $res = $messaging_db->exec_statement($sql);
1956 foreach my $hit (@{$res}) {
1957 my $msg_id = @{$hit}[0];
1959 my $sql = "SELECT * FROM $messaging_tn WHERE (id='$msg_id' AND direction='out' AND (NOT flag='s'))";
1960 #&daemon_log("0 DEBUG: $sql", 7);
1961 my $res = $messaging_db->exec_statement($sql);
1963 # not all usr msgs have been seen till now
1964 if ( ref(@$res[0]) eq "ARRAY") { next; }
1966 $sql = "DELETE FROM $messaging_tn WHERE (id='$msg_id')";
1967 #&daemon_log("0 DEBUG: $sql", 7);
1968 $res = $messaging_db->exec_statement($sql);
1970 }
1972 $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1973 return;
1974 }
1977 sub watch_for_old_known_clients {
1978 my ($kernel,$heap) = @_[KERNEL, HEAP];
1980 my $sql_statement = "SELECT * FROM $known_clients_tn";
1981 my $res = $known_clients_db->select_dbentry( $sql_statement );
1983 my $act_time = int(&get_time());
1985 while ( my ($hit_num, $hit) = each %$res) {
1986 my $expired_timestamp = int($hit->{'timestamp'});
1987 $expired_timestamp =~ /(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/;
1988 my $dt = DateTime->new( year => $1,
1989 month => $2,
1990 day => $3,
1991 hour => $4,
1992 minute => $5,
1993 second => $6,
1994 );
1996 $dt->add( seconds => 2 * int($hit->{'keylifetime'}) );
1997 $expired_timestamp = $dt->ymd('').$dt->hms('');
1998 if ($act_time > $expired_timestamp) {
1999 my $hostname = $hit->{'hostname'};
2000 my $del_sql = "DELETE FROM $known_clients_tn WHERE hostname='$hostname'";
2001 my $del_res = $known_clients_db->exec_statement($del_sql);
2003 &main::daemon_log("0 INFO: timestamp '".$hit->{'timestamp'}."' of client '$hostname' is expired('$expired_timestamp'), client will be deleted from known_clients_db", 5);
2004 }
2006 }
2008 $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
2009 }
2012 sub watch_for_next_tasks {
2013 my ($kernel,$heap) = @_[KERNEL, HEAP];
2015 my $sql = "SELECT * FROM $incoming_tn";
2016 my $res = $incoming_db->select_dbentry($sql);
2018 while ( my ($hit_num, $hit) = each %$res) {
2019 my $headertag = $hit->{'headertag'};
2020 if ($headertag =~ /^answer_(\d+)/) {
2021 # do not start processing, this message is for a still running POE::Wheel
2022 next;
2023 }
2024 my $message_id = $hit->{'id'};
2025 $kernel->yield('next_task', $hit);
2027 my $sql = "DELETE FROM $incoming_tn WHERE id=$message_id";
2028 my $res = $incoming_db->exec_statement($sql);
2029 }
2031 $kernel->delay_set('watch_for_next_tasks', 0.1);
2032 }
2035 sub get_ldap_handle {
2036 my ($session_id) = @_;
2037 my $heap;
2038 my $ldap_handle;
2040 if (not defined $session_id ) { $session_id = 0 };
2041 if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
2043 if ($session_id == 0) {
2044 daemon_log("$session_id DEBUG: get_ldap_handle invoked without a session_id, create a new ldap_handle", 7);
2045 $ldap_handle = Net::LDAP->new( $ldap_uri );
2046 $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!");
2048 } else {
2049 my $session_reference = $global_kernel->ID_id_to_session($session_id);
2050 if( defined $session_reference ) {
2051 $heap = $session_reference->get_heap();
2052 }
2054 if (not defined $heap) {
2055 daemon_log("$session_id DEBUG: cannot get heap for session_id '$session_id'", 7);
2056 return;
2057 }
2059 # TODO: This "if" is nonsense, because it doesn't prove that the
2060 # used handle is still valid - or if we've to reconnect...
2061 #if (not exists $heap->{ldap_handle}) {
2062 $ldap_handle = Net::LDAP->new( $ldap_uri );
2063 $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!");
2064 $heap->{ldap_handle} = $ldap_handle;
2065 #}
2066 }
2067 return $ldap_handle;
2068 }
2071 sub change_fai_state {
2072 my ($st, $targets, $session_id) = @_;
2073 $session_id = 0 if not defined $session_id;
2074 # Set FAI state to localboot
2075 my %mapActions= (
2076 reboot => '',
2077 update => 'softupdate',
2078 localboot => 'localboot',
2079 reinstall => 'install',
2080 rescan => '',
2081 wake => '',
2082 memcheck => 'memcheck',
2083 sysinfo => 'sysinfo',
2084 install => 'install',
2085 );
2087 # Return if this is unknown
2088 if (!exists $mapActions{ $st }){
2089 daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1);
2090 return;
2091 }
2093 my $state= $mapActions{ $st };
2095 my $ldap_handle = &get_ldap_handle($session_id);
2096 if( defined($ldap_handle) ) {
2098 # Build search filter for hosts
2099 my $search= "(&(objectClass=GOhard)";
2100 foreach (@{$targets}){
2101 $search.= "(macAddress=$_)";
2102 }
2103 $search.= ")";
2105 # If there's any host inside of the search string, procress them
2106 if (!($search =~ /macAddress/)){
2107 daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);
2108 return;
2109 }
2111 # Perform search for Unit Tag
2112 my $mesg = $ldap_handle->search(
2113 base => $ldap_base,
2114 scope => 'sub',
2115 attrs => ['dn', 'FAIstate', 'objectClass'],
2116 filter => "$search"
2117 );
2119 if ($mesg->count) {
2120 my @entries = $mesg->entries;
2121 if (0 == @entries) {
2122 daemon_log("$session_id ERROR: ldap search failed: ldap_base=$ldap_base, filter=$search", 1);
2123 }
2125 foreach my $entry (@entries) {
2126 # Only modify entry if it is not set to '$state'
2127 if ($entry->get_value("FAIstate") ne "$state"){
2128 daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
2129 my $result;
2130 my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
2131 if (exists $tmp{'FAIobject'}){
2132 if ($state eq ''){
2133 $result= $ldap_handle->modify($entry->dn, changes => [
2134 delete => [ FAIstate => [] ] ]);
2135 } else {
2136 $result= $ldap_handle->modify($entry->dn, changes => [
2137 replace => [ FAIstate => $state ] ]);
2138 }
2139 } elsif ($state ne ''){
2140 $result= $ldap_handle->modify($entry->dn, changes => [
2141 add => [ objectClass => 'FAIobject' ],
2142 add => [ FAIstate => $state ] ]);
2143 }
2145 # Errors?
2146 if ($result->code){
2147 daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2148 }
2149 } else {
2150 daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 7);
2151 }
2152 }
2153 } else {
2154 daemon_log("$session_id ERROR: LDAP search failed: ldap_base=$ldap_base, filter=$search", 1);
2155 }
2157 # if no ldap handle defined
2158 } else {
2159 daemon_log("$session_id ERROR: no LDAP handle defined for update FAIstate", 1);
2160 }
2162 return;
2163 }
2166 sub change_goto_state {
2167 my ($st, $targets, $session_id) = @_;
2168 $session_id = 0 if not defined $session_id;
2170 # Switch on or off?
2171 my $state= $st eq 'active' ? 'active': 'locked';
2173 my $ldap_handle = &get_ldap_handle($session_id);
2174 if( defined($ldap_handle) ) {
2176 # Build search filter for hosts
2177 my $search= "(&(objectClass=GOhard)";
2178 foreach (@{$targets}){
2179 $search.= "(macAddress=$_)";
2180 }
2181 $search.= ")";
2183 # If there's any host inside of the search string, procress them
2184 if (!($search =~ /macAddress/)){
2185 return;
2186 }
2188 # Perform search for Unit Tag
2189 my $mesg = $ldap_handle->search(
2190 base => $ldap_base,
2191 scope => 'sub',
2192 attrs => ['dn', 'gotoMode'],
2193 filter => "$search"
2194 );
2196 if ($mesg->count) {
2197 my @entries = $mesg->entries;
2198 foreach my $entry (@entries) {
2200 # Only modify entry if it is not set to '$state'
2201 if ($entry->get_value("gotoMode") ne $state){
2203 daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
2204 my $result;
2205 $result= $ldap_handle->modify($entry->dn, changes => [
2206 replace => [ gotoMode => $state ] ]);
2208 # Errors?
2209 if ($result->code){
2210 &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2211 }
2213 }
2214 }
2215 } else {
2216 daemon_log("$session_id ERROR: LDAP search failed in function change_goto_state: ldap_base=$ldap_base, filter=$search", 1);
2217 }
2219 }
2220 }
2223 sub run_recreate_packages_db {
2224 my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2225 my $session_id = $session->ID;
2226 &main::daemon_log("$session_id INFO: Recreating FAI Packages DB ('$fai_release_tn', '$fai_server_tn', '$packages_list_tn')", 5);
2227 $kernel->yield('create_fai_release_db', $fai_release_tn);
2228 $kernel->yield('create_fai_server_db', $fai_server_tn);
2229 return;
2230 }
2233 sub run_create_fai_server_db {
2234 my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2235 my $session_id = $session->ID;
2236 my $task = POE::Wheel::Run->new(
2237 Program => sub { &create_fai_server_db($table_name,$kernel, undef, $session_id) },
2238 StdoutEvent => "session_run_result",
2239 StderrEvent => "session_run_debug",
2240 CloseEvent => "session_run_done",
2241 );
2243 $heap->{task}->{ $task->ID } = $task;
2244 return;
2245 }
2248 sub create_fai_server_db {
2249 my ($table_name, $kernel, $dont_create_packages_list, $session_id) = @_;
2250 my $result;
2252 if (not defined $session_id) { $session_id = 0; }
2253 my $ldap_handle = &get_ldap_handle();
2254 if(defined($ldap_handle)) {
2255 daemon_log("$session_id INFO: create_fai_server_db: start", 5);
2256 my $mesg= $ldap_handle->search(
2257 base => $ldap_base,
2258 scope => 'sub',
2259 attrs => ['FAIrepository', 'gosaUnitTag'],
2260 filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
2261 );
2262 if($mesg->{'resultCode'} == 0 &&
2263 $mesg->count != 0) {
2264 foreach my $entry (@{$mesg->{entries}}) {
2265 if($entry->exists('FAIrepository')) {
2266 # Add an entry for each Repository configured for server
2267 foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
2268 my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
2269 my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
2270 $result= $fai_server_db->add_dbentry( {
2271 table => $table_name,
2272 primkey => ['server', 'release', 'tag'],
2273 server => $tmp_url,
2274 release => $tmp_release,
2275 sections => $tmp_sections,
2276 tag => (length($tmp_tag)>0)?$tmp_tag:"",
2277 } );
2278 }
2279 }
2280 }
2281 }
2282 daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
2284 # TODO: Find a way to post the 'create_packages_list_db' event
2285 if(not defined($dont_create_packages_list)) {
2286 &create_packages_list_db(undef, undef, $session_id);
2287 }
2288 }
2290 $ldap_handle->disconnect;
2291 return $result;
2292 }
2295 sub run_create_fai_release_db {
2296 my ($session, $heap, $table_name) = @_[SESSION, HEAP, ARG0];
2297 my $session_id = $session->ID;
2298 my $task = POE::Wheel::Run->new(
2299 Program => sub { &create_fai_release_db($table_name, $session_id) },
2300 StdoutEvent => "session_run_result",
2301 StderrEvent => "session_run_debug",
2302 CloseEvent => "session_run_done",
2303 );
2305 $heap->{task}->{ $task->ID } = $task;
2306 return;
2307 }
2310 sub create_fai_release_db {
2311 my ($table_name, $session_id) = @_;
2312 my $result;
2314 # used for logging
2315 if (not defined $session_id) { $session_id = 0; }
2317 my $ldap_handle = &get_ldap_handle();
2318 if(defined($ldap_handle)) {
2319 daemon_log("$session_id INFO: create_fai_release_db: start",5);
2320 my $mesg= $ldap_handle->search(
2321 base => $ldap_base,
2322 scope => 'sub',
2323 attrs => [],
2324 filter => "(&(objectClass=organizationalUnit)(ou=fai))",
2325 );
2326 if($mesg->{'resultCode'} == 0 &&
2327 $mesg->count != 0) {
2328 # Walk through all possible FAI container ou's
2329 my @sql_list;
2330 my $timestamp= &get_time();
2331 foreach my $ou (@{$mesg->{entries}}) {
2332 my $tmp_classes= resolve_fai_classes($ou->dn, $ldap_handle, $session_id);
2333 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
2334 my @tmp_array=get_fai_release_entries($tmp_classes);
2335 if(@tmp_array) {
2336 foreach my $entry (@tmp_array) {
2337 if(defined($entry) && ref($entry) eq 'HASH') {
2338 my $sql=
2339 "INSERT INTO $table_name "
2340 ."(timestamp, release, class, type, state) VALUES ("
2341 .$timestamp.","
2342 ."'".$entry->{'release'}."',"
2343 ."'".$entry->{'class'}."',"
2344 ."'".$entry->{'type'}."',"
2345 ."'".$entry->{'state'}."')";
2346 push @sql_list, $sql;
2347 }
2348 }
2349 }
2350 }
2351 }
2353 daemon_log("$session_id DEBUG: Inserting ".scalar @sql_list." entries to DB",8);
2354 if(@sql_list) {
2355 unshift @sql_list, "VACUUM";
2356 unshift @sql_list, "DELETE FROM $table_name";
2357 $fai_release_db->exec_statementlist(\@sql_list);
2358 }
2359 daemon_log("$session_id DEBUG: Done with inserting",7);
2360 }
2361 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
2362 }
2363 $ldap_handle->disconnect;
2364 return $result;
2365 }
2367 sub get_fai_types {
2368 my $tmp_classes = shift || return undef;
2369 my @result;
2371 foreach my $type(keys %{$tmp_classes}) {
2372 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
2373 my $entry = {
2374 type => $type,
2375 state => $tmp_classes->{$type}[0],
2376 };
2377 push @result, $entry;
2378 }
2379 }
2381 return @result;
2382 }
2384 sub get_fai_state {
2385 my $result = "";
2386 my $tmp_classes = shift || return $result;
2388 foreach my $type(keys %{$tmp_classes}) {
2389 if(defined($tmp_classes->{$type}[0])) {
2390 $result = $tmp_classes->{$type}[0];
2392 # State is equal for all types in class
2393 last;
2394 }
2395 }
2397 return $result;
2398 }
2400 sub resolve_fai_classes {
2401 my ($fai_base, $ldap_handle, $session_id) = @_;
2402 if (not defined $session_id) { $session_id = 0; }
2403 my $result;
2404 my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
2405 my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
2406 my $fai_classes;
2408 daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base",7);
2409 my $mesg= $ldap_handle->search(
2410 base => $fai_base,
2411 scope => 'sub',
2412 attrs => ['cn','objectClass','FAIstate'],
2413 filter => $fai_filter,
2414 );
2415 daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries",7);
2417 if($mesg->{'resultCode'} == 0 &&
2418 $mesg->count != 0) {
2419 foreach my $entry (@{$mesg->{entries}}) {
2420 if($entry->exists('cn')) {
2421 my $tmp_dn= $entry->dn();
2423 # Skip classname and ou dn parts for class
2424 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?),$fai_base$/;
2426 # Skip classes without releases
2427 if((!defined($tmp_release)) || length($tmp_release)==0) {
2428 next;
2429 }
2431 my $tmp_cn= $entry->get_value('cn');
2432 my $tmp_state= $entry->get_value('FAIstate');
2434 my $tmp_type;
2435 # Get FAI type
2436 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
2437 if(grep $_ eq $oclass, @possible_fai_classes) {
2438 $tmp_type= $oclass;
2439 last;
2440 }
2441 }
2443 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2444 # A Subrelease
2445 my @sub_releases = split(/,/, $tmp_release);
2447 # Walk through subreleases and build hash tree
2448 my $hash;
2449 while(my $tmp_sub_release = pop @sub_releases) {
2450 $hash .= "\{'$tmp_sub_release'\}->";
2451 }
2452 eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
2453 } else {
2454 # A branch, no subrelease
2455 push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
2456 }
2457 } elsif (!$entry->exists('cn')) {
2458 my $tmp_dn= $entry->dn();
2459 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?),$fai_base$/;
2461 # Skip classes without releases
2462 if((!defined($tmp_release)) || length($tmp_release)==0) {
2463 next;
2464 }
2466 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2467 # A Subrelease
2468 my @sub_releases= split(/,/, $tmp_release);
2470 # Walk through subreleases and build hash tree
2471 my $hash;
2472 while(my $tmp_sub_release = pop @sub_releases) {
2473 $hash .= "\{'$tmp_sub_release'\}->";
2474 }
2475 # Remove the last two characters
2476 chop($hash);
2477 chop($hash);
2479 eval('$fai_classes->'.$hash.'= {}');
2480 } else {
2481 # A branch, no subrelease
2482 if(!exists($fai_classes->{$tmp_release})) {
2483 $fai_classes->{$tmp_release} = {};
2484 }
2485 }
2486 }
2487 }
2489 # The hash is complete, now we can honor the copy-on-write based missing entries
2490 foreach my $release (keys %$fai_classes) {
2491 $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
2492 }
2493 }
2494 return $result;
2495 }
2497 sub apply_fai_inheritance {
2498 my $fai_classes = shift || return {};
2499 my $tmp_classes;
2501 # Get the classes from the branch
2502 foreach my $class (keys %{$fai_classes}) {
2503 # Skip subreleases
2504 if($class =~ /^ou=.*$/) {
2505 next;
2506 } else {
2507 $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
2508 }
2509 }
2511 # Apply to each subrelease
2512 foreach my $subrelease (keys %{$fai_classes}) {
2513 if($subrelease =~ /ou=/) {
2514 foreach my $tmp_class (keys %{$tmp_classes}) {
2515 if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
2516 $fai_classes->{$subrelease}->{$tmp_class} =
2517 deep_copy($tmp_classes->{$tmp_class});
2518 } else {
2519 foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
2520 if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
2521 $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
2522 deep_copy($tmp_classes->{$tmp_class}->{$type});
2523 }
2524 }
2525 }
2526 }
2527 }
2528 }
2530 # Find subreleases in deeper levels
2531 foreach my $subrelease (keys %{$fai_classes}) {
2532 if($subrelease =~ /ou=/) {
2533 foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
2534 if($subsubrelease =~ /ou=/) {
2535 apply_fai_inheritance($fai_classes->{$subrelease});
2536 }
2537 }
2538 }
2539 }
2541 return $fai_classes;
2542 }
2544 sub get_fai_release_entries {
2545 my $tmp_classes = shift || return;
2546 my $parent = shift || "";
2547 my @result = shift || ();
2549 foreach my $entry (keys %{$tmp_classes}) {
2550 if(defined($entry)) {
2551 if($entry =~ /^ou=.*$/) {
2552 my $release_name = $entry;
2553 $release_name =~ s/ou=//g;
2554 if(length($parent)>0) {
2555 $release_name = $parent."/".$release_name;
2556 }
2557 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2558 foreach my $bufentry(@bufentries) {
2559 push @result, $bufentry;
2560 }
2561 } else {
2562 my @types = get_fai_types($tmp_classes->{$entry});
2563 foreach my $type (@types) {
2564 push @result,
2565 {
2566 'class' => $entry,
2567 'type' => $type->{'type'},
2568 'release' => $parent,
2569 'state' => $type->{'state'},
2570 };
2571 }
2572 }
2573 }
2574 }
2576 return @result;
2577 }
2579 sub deep_copy {
2580 my $this = shift;
2581 if (not ref $this) {
2582 $this;
2583 } elsif (ref $this eq "ARRAY") {
2584 [map deep_copy($_), @$this];
2585 } elsif (ref $this eq "HASH") {
2586 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2587 } else { die "what type is $_?" }
2588 }
2591 sub session_run_result {
2592 my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];
2593 $kernel->sig(CHLD => "child_reap");
2594 }
2596 sub session_run_debug {
2597 my $result = $_[ARG0];
2598 print STDERR "$result\n";
2599 }
2601 sub session_run_done {
2602 my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2603 delete $heap->{task}->{$task_id};
2604 }
2607 sub create_sources_list {
2608 my $session_id = shift;
2609 my $ldap_handle = &main::get_ldap_handle;
2610 my $result="/tmp/gosa_si_tmp_sources_list";
2612 # Remove old file
2613 if(stat($result)) {
2614 unlink($result);
2615 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7);
2616 }
2618 my $fh;
2619 open($fh, ">$result");
2620 if (not defined $fh) {
2621 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7);
2622 return undef;
2623 }
2624 if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2625 my $mesg=$ldap_handle->search(
2626 base => $main::ldap_server_dn,
2627 scope => 'base',
2628 attrs => 'FAIrepository',
2629 filter => 'objectClass=FAIrepositoryServer'
2630 );
2631 if($mesg->count) {
2632 foreach my $entry(@{$mesg->{'entries'}}) {
2633 foreach my $value(@{$entry->get_value('FAIrepository', asref => 1)}) {
2634 my ($server, $tag, $release, $sections)= split /\|/, $value;
2635 my $line = "deb $server $release";
2636 $sections =~ s/,/ /g;
2637 $line.= " $sections";
2638 print $fh $line."\n";
2639 }
2640 }
2641 }
2642 } else {
2643 if (defined $main::ldap_server_dn){
2644 &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1);
2645 } else {
2646 &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2647 }
2648 }
2649 close($fh);
2651 return $result;
2652 }
2655 sub run_create_packages_list_db {
2656 my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2657 my $session_id = $session->ID;
2659 my $task = POE::Wheel::Run->new(
2660 Priority => +20,
2661 Program => sub {&create_packages_list_db(undef, undef, $session_id)},
2662 StdoutEvent => "session_run_result",
2663 StderrEvent => "session_run_debug",
2664 CloseEvent => "session_run_done",
2665 );
2666 $heap->{task}->{ $task->ID } = $task;
2667 }
2670 sub create_packages_list_db {
2671 my ($ldap_handle, $sources_file, $session_id) = @_;
2673 # it should not be possible to trigger a recreation of packages_list_db
2674 # while packages_list_db is under construction, so set flag packages_list_under_construction
2675 # which is tested befor recreation can be started
2676 if (-r $packages_list_under_construction) {
2677 daemon_log("$session_id WARNING: packages_list_db is right now under construction, please wait until this process is finished", 3);
2678 return;
2679 } else {
2680 daemon_log("$session_id INFO: create_packages_list_db: start", 5);
2681 # set packages_list_under_construction to true
2682 system("touch $packages_list_under_construction");
2683 @packages_list_statements=();
2684 }
2686 if (not defined $session_id) { $session_id = 0; }
2687 if (not defined $ldap_handle) {
2688 $ldap_handle= &get_ldap_handle();
2690 if (not defined $ldap_handle) {
2691 daemon_log("$session_id ERROR: no ldap_handle available to create_packages_list_db", 1);
2692 unlink($packages_list_under_construction);
2693 return;
2694 }
2695 }
2696 if (not defined $sources_file) {
2697 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5);
2698 $sources_file = &create_sources_list($session_id);
2699 }
2701 if (not defined $sources_file) {
2702 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1);
2703 unlink($packages_list_under_construction);
2704 return;
2705 }
2707 my $line;
2709 open(CONFIG, "<$sources_file") or do {
2710 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2711 unlink($packages_list_under_construction);
2712 return;
2713 };
2715 # Read lines
2716 while ($line = <CONFIG>){
2717 # Unify
2718 chop($line);
2719 $line =~ s/^\s+//;
2720 $line =~ s/^\s+/ /;
2722 # Strip comments
2723 $line =~ s/#.*$//g;
2725 # Skip empty lines
2726 if ($line =~ /^\s*$/){
2727 next;
2728 }
2730 # Interpret deb line
2731 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2732 my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2733 my $section;
2734 foreach $section (split(' ', $sections)){
2735 &parse_package_info( $baseurl, $dist, $section, $session_id );
2736 }
2737 }
2738 }
2740 close (CONFIG);
2743 find(\&cleanup_and_extract, keys( %repo_dirs ));
2744 &main::strip_packages_list_statements();
2745 unshift @packages_list_statements, "VACUUM";
2746 $packages_list_db->exec_statementlist(\@packages_list_statements);
2747 unlink($packages_list_under_construction);
2748 daemon_log("$session_id INFO: create_packages_list_db: finished", 5);
2749 return;
2750 }
2752 # This function should do some intensive task to minimize the db-traffic
2753 sub strip_packages_list_statements {
2754 my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2755 my @new_statement_list=();
2756 my $hash;
2757 my $insert_hash;
2758 my $update_hash;
2759 my $delete_hash;
2760 my $local_timestamp=get_time();
2762 foreach my $existing_entry (@existing_entries) {
2763 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
2764 }
2766 foreach my $statement (@packages_list_statements) {
2767 if($statement =~ /^INSERT/i) {
2768 # Assign the values from the insert statement
2769 my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~
2770 /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
2771 if(exists($hash->{$distribution}->{$package}->{$version})) {
2772 # If section or description has changed, update the DB
2773 if(
2774 (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or
2775 (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
2776 ) {
2777 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
2778 }
2779 } else {
2780 # Insert a non-existing entry to db
2781 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2782 }
2783 } elsif ($statement =~ /^UPDATE/i) {
2784 my ($template,$package,$version) = ($1,$2,$3) if $statement =~
2785 /^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;
2786 foreach my $distribution (keys %{$hash}) {
2787 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
2788 # update the insertion hash to execute only one query per package (insert instead insert+update)
2789 @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
2790 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
2791 if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
2792 my $section;
2793 my $description;
2794 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
2795 length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
2796 $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
2797 }
2798 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2799 $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
2800 }
2801 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2802 }
2803 }
2804 }
2805 }
2806 }
2808 # TODO: Check for orphaned entries
2810 # unroll the insert_hash
2811 foreach my $distribution (keys %{$insert_hash}) {
2812 foreach my $package (keys %{$insert_hash->{$distribution}}) {
2813 foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
2814 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
2815 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
2816 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
2817 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
2818 ."'$local_timestamp')";
2819 }
2820 }
2821 }
2823 # unroll the update hash
2824 foreach my $distribution (keys %{$update_hash}) {
2825 foreach my $package (keys %{$update_hash->{$distribution}}) {
2826 foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
2827 my $set = "";
2828 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
2829 $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
2830 }
2831 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2832 $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
2833 }
2834 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
2835 $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
2836 }
2837 if(defined($set) and length($set) > 0) {
2838 $set .= "timestamp = '$local_timestamp'";
2839 } else {
2840 next;
2841 }
2842 push @new_statement_list,
2843 "UPDATE $main::packages_list_tn SET $set WHERE"
2844 ." distribution = '$distribution'"
2845 ." AND package = '$package'"
2846 ." AND version = '$version'";
2847 }
2848 }
2849 }
2851 @packages_list_statements = @new_statement_list;
2852 }
2855 sub parse_package_info {
2856 my ($baseurl, $dist, $section, $session_id)= @_;
2857 my ($package);
2858 if (not defined $session_id) { $session_id = 0; }
2859 my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
2860 $repo_dirs{ "${repo_path}/pool" } = 1;
2862 foreach $package ("Packages.gz"){
2863 daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
2864 get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
2865 parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
2866 }
2868 }
2871 sub get_package {
2872 my ($url, $dest, $session_id)= @_;
2873 if (not defined $session_id) { $session_id = 0; }
2875 my $tpath = dirname($dest);
2876 -d "$tpath" || mkpath "$tpath";
2878 # This is ugly, but I've no time to take a look at "how it works in perl"
2879 if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
2880 system("gunzip -cd '$dest' > '$dest.in'");
2881 daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 5);
2882 unlink($dest);
2883 daemon_log("$session_id DEBUG: delete file '$dest'", 5);
2884 } else {
2885 daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' failed!", 1);
2886 }
2887 return 0;
2888 }
2891 sub parse_package {
2892 my ($path, $dist, $srv_path, $session_id)= @_;
2893 if (not defined $session_id) { $session_id = 0;}
2894 my ($package, $version, $section, $description);
2895 my $PACKAGES;
2896 my $timestamp = &get_time();
2898 if(not stat("$path.in")) {
2899 daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
2900 return;
2901 }
2903 open($PACKAGES, "<$path.in");
2904 if(not defined($PACKAGES)) {
2905 daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1);
2906 return;
2907 }
2909 # Read lines
2910 while (<$PACKAGES>){
2911 my $line = $_;
2912 # Unify
2913 chop($line);
2915 # Use empty lines as a trigger
2916 if ($line =~ /^\s*$/){
2917 my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
2918 push(@packages_list_statements, $sql);
2919 $package = "none";
2920 $version = "none";
2921 $section = "none";
2922 $description = "none";
2923 next;
2924 }
2926 # Trigger for package name
2927 if ($line =~ /^Package:\s/){
2928 ($package)= ($line =~ /^Package: (.*)$/);
2929 next;
2930 }
2932 # Trigger for version
2933 if ($line =~ /^Version:\s/){
2934 ($version)= ($line =~ /^Version: (.*)$/);
2935 next;
2936 }
2938 # Trigger for description
2939 if ($line =~ /^Description:\s/){
2940 ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
2941 next;
2942 }
2944 # Trigger for section
2945 if ($line =~ /^Section:\s/){
2946 ($section)= ($line =~ /^Section: (.*)$/);
2947 next;
2948 }
2950 # Trigger for filename
2951 if ($line =~ /^Filename:\s/){
2952 my ($filename) = ($line =~ /^Filename: (.*)$/);
2953 store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
2954 next;
2955 }
2956 }
2958 close( $PACKAGES );
2959 unlink( "$path.in" );
2960 }
2963 sub store_fileinfo {
2964 my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
2966 my %fileinfo = (
2967 'package' => $package,
2968 'dist' => $dist,
2969 'version' => $vers,
2970 );
2972 $repo_files{ "${srvdir}/$file" } = \%fileinfo;
2973 }
2976 sub cleanup_and_extract {
2977 my $fileinfo = $repo_files{ $File::Find::name };
2979 if( defined $fileinfo ) {
2981 my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
2982 my $sql;
2983 my $package = $fileinfo->{ 'package' };
2984 my $newver = $fileinfo->{ 'version' };
2986 mkpath($dir);
2987 system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
2989 if( -f "$dir/DEBIAN/templates" ) {
2991 daemon_log("DEBUG: Found debconf templates in '$package' - $newver", 7);
2993 my $tmpl= "";
2994 {
2995 local $/=undef;
2996 open FILE, "$dir/DEBIAN/templates";
2997 $tmpl = &encode_base64(<FILE>);
2998 close FILE;
2999 }
3000 rmtree("$dir/DEBIAN/templates");
3002 $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
3003 push @packages_list_statements, $sql;
3004 }
3005 }
3007 return;
3008 }
3011 sub register_at_foreign_servers {
3012 my ($kernel) = $_[KERNEL];
3014 # hole alle bekannten server aus known_server_db
3015 my $server_sql = "SELECT * FROM $known_server_tn";
3016 my $server_res = $known_server_db->exec_statement($server_sql);
3018 # no entries in known_server_db
3019 if (not ref(@$server_res[0]) eq "ARRAY") {
3020 # TODO
3021 }
3023 # detect already connected clients
3024 my $client_sql = "SELECT * FROM $known_clients_tn";
3025 my $client_res = $known_clients_db->exec_statement($client_sql);
3027 # send my server details to all other gosa-si-server within the network
3028 foreach my $hit (@$server_res) {
3029 my $hostname = @$hit[0];
3030 my $hostkey = &create_passwd;
3032 # add already connected clients to registration message
3033 my $myhash = &create_xml_hash('new_server', $server_address, $hostname);
3034 &add_content2xml_hash($myhash, 'key', $hostkey);
3035 map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res);
3037 # add locally loaded gosa-si modules to registration message
3038 my $loaded_modules = {};
3039 while (my ($package, $pck_info) = each %$known_modules) {
3040 foreach my $act_module (keys(%{@$pck_info[2]})) {
3041 $loaded_modules->{$act_module} = "";
3042 }
3043 }
3044 map(&add_content2xml_hash($myhash, "loaded_modules", $_), keys(%$loaded_modules));
3046 # add macaddress to registration message
3047 my ($host_ip, $host_port) = split(/:/, $hostname);
3048 my $local_ip = &get_local_ip_for_remote_ip($host_ip);
3049 my $network_interface= &get_interface_for_ip($local_ip);
3050 my $host_mac = &get_mac_for_interface($network_interface);
3051 &add_content2xml_hash($myhash, 'macaddress', $host_mac);
3053 # build registration message and send it
3054 my $foreign_server_msg = &create_xml_string($myhash);
3055 my $error = &send_msg_to_target($foreign_server_msg, $hostname, $ServerPackages_key, "new_server", 0);
3056 }
3058 $kernel->delay_set("register_at_foreign_servers", $foreign_servers_register_delay);
3059 return;
3060 }
3063 #==== MAIN = main ==============================================================
3064 # parse commandline options
3065 Getopt::Long::Configure( "bundling" );
3066 GetOptions("h|help" => \&usage,
3067 "c|config=s" => \$cfg_file,
3068 "f|foreground" => \$foreground,
3069 "v|verbose+" => \$verbose,
3070 "no-arp+" => \$no_arp,
3071 );
3073 # read and set config parameters
3074 &check_cmdline_param ;
3075 &read_configfile($cfg_file, %cfg_defaults);
3076 &check_pid;
3078 $SIG{CHLD} = 'IGNORE';
3080 # forward error messages to logfile
3081 if( ! $foreground ) {
3082 open( STDIN, '+>/dev/null' );
3083 open( STDOUT, '+>&STDIN' );
3084 open( STDERR, '+>&STDIN' );
3085 }
3087 # Just fork, if we are not in foreground mode
3088 if( ! $foreground ) {
3089 chdir '/' or die "Can't chdir to /: $!";
3090 $pid = fork;
3091 setsid or die "Can't start a new session: $!";
3092 umask 0;
3093 } else {
3094 $pid = $$;
3095 }
3097 # Do something useful - put our PID into the pid_file
3098 if( 0 != $pid ) {
3099 open( LOCK_FILE, ">$pid_file" );
3100 print LOCK_FILE "$pid\n";
3101 close( LOCK_FILE );
3102 if( !$foreground ) {
3103 exit( 0 )
3104 };
3105 }
3107 # parse head url and revision from svn
3108 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
3109 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
3110 $server_headURL = defined $1 ? $1 : 'unknown' ;
3111 $server_revision = defined $2 ? $2 : 'unknown' ;
3112 if ($server_headURL =~ /\/tag\// ||
3113 $server_headURL =~ /\/branches\// ) {
3114 $server_status = "stable";
3115 } else {
3116 $server_status = "developmental" ;
3117 }
3120 daemon_log(" ", 1);
3121 daemon_log("$0 started!", 1);
3122 daemon_log("status: $server_status", 1);
3123 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1);
3125 # connect to incoming_db
3126 unlink($incoming_file_name);
3127 $incoming_db = GOSA::DBsqlite->new($incoming_file_name);
3128 $incoming_db->create_table($incoming_tn, \@incoming_col_names);
3130 # connect to gosa-si job queue
3131 $job_db = GOSA::DBsqlite->new($job_queue_file_name);
3132 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
3134 # connect to known_clients_db
3135 $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
3136 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
3138 # connect to foreign_clients_db
3139 $foreign_clients_db = GOSA::DBsqlite->new($foreign_clients_file_name);
3140 $foreign_clients_db->create_table($foreign_clients_tn, \@foreign_clients_col_names);
3142 # connect to known_server_db
3143 unlink($known_server_file_name);
3144 $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
3145 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
3147 # connect to login_usr_db
3148 $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
3149 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
3151 # connect to fai_server_db and fai_release_db
3152 unlink($fai_server_file_name);
3153 $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
3154 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
3156 unlink($fai_release_file_name);
3157 $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
3158 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
3160 # connect to packages_list_db
3161 #unlink($packages_list_file_name);
3162 unlink($packages_list_under_construction);
3163 $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
3164 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
3166 # connect to messaging_db
3167 $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
3168 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
3171 # create xml object used for en/decrypting
3172 $xml = new XML::Simple();
3175 # foreign servers
3176 my @foreign_server_list;
3178 # add foreign server from cfg file
3179 if ($foreign_server_string ne "") {
3180 my @cfg_foreign_server_list = split(",", $foreign_server_string);
3181 foreach my $foreign_server (@cfg_foreign_server_list) {
3182 push(@foreign_server_list, $foreign_server);
3183 }
3184 }
3186 # add foreign server from dns
3187 my @tmp_servers;
3188 if ( !$server_domain) {
3189 # Try our DNS Searchlist
3190 for my $domain(get_dns_domains()) {
3191 chomp($domain);
3192 my ($tmp_domains, $error_string) = &get_server_addresses($domain);
3193 if(@$tmp_domains) {
3194 for my $tmp_server(@$tmp_domains) {
3195 push @tmp_servers, $tmp_server;
3196 }
3197 }
3198 }
3199 if(@tmp_servers && length(@tmp_servers)==0) {
3200 daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3201 }
3202 } else {
3203 @tmp_servers = &get_server_addresses($server_domain);
3204 if( 0 == @tmp_servers ) {
3205 daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3206 }
3207 }
3208 foreach my $server (@tmp_servers) {
3209 unshift(@foreign_server_list, $server);
3210 }
3211 # eliminate duplicate entries
3212 @foreign_server_list = &del_doubles(@foreign_server_list);
3213 my $all_foreign_server = join(", ", @foreign_server_list);
3214 daemon_log("0 INFO: found foreign server in config file and DNS: $all_foreign_server", 5);
3216 # add all found foreign servers to known_server
3217 my $act_timestamp = &get_time();
3218 foreach my $foreign_server (@foreign_server_list) {
3220 # do not add myself to known_server_db
3221 if (&is_local($foreign_server)) { next; }
3222 ######################################
3224 my $res = $known_server_db->add_dbentry( {table=>$known_server_tn,
3225 primkey=>['hostname'],
3226 hostname=>$foreign_server,
3227 macaddress=>"",
3228 status=>'not_jet_registered',
3229 hostkey=>"none",
3230 loaded_modules => "none",
3231 timestamp=>$act_timestamp,
3232 } );
3233 }
3236 # Import all modules
3237 &import_modules;
3239 # Check wether all modules are gosa-si valid passwd check
3240 &password_check;
3242 # Prepare for using Opsi
3243 if ($opsi_enabled eq "true") {
3244 use JSON::RPC::Client;
3245 use XML::Quote qw(:all);
3246 $opsi_url= "https://".$opsi_admin.":".$opsi_password."@".$opsi_server.":4447/rpc";
3247 $opsi_client = new JSON::RPC::Client;
3248 }
3251 POE::Component::Server::TCP->new(
3252 Alias => "TCP_SERVER",
3253 Port => $server_port,
3254 ClientInput => sub {
3255 my ($kernel, $input) = @_[KERNEL, ARG0];
3256 push(@tasks, $input);
3257 push(@msgs_to_decrypt, $input);
3258 $kernel->yield("msg_to_decrypt");
3259 },
3260 InlineStates => {
3261 msg_to_decrypt => \&msg_to_decrypt,
3262 next_task => \&next_task,
3263 task_result => \&handle_task_result,
3264 task_done => \&handle_task_done,
3265 task_debug => \&handle_task_debug,
3266 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!" },
3267 }
3268 );
3270 daemon_log("start socket for incoming xml messages at port '$server_port' ", 1);
3272 # create session for repeatedly checking the job queue for jobs
3273 POE::Session->create(
3274 inline_states => {
3275 _start => \&session_start,
3276 register_at_foreign_servers => \®ister_at_foreign_servers,
3277 sig_handler => \&sig_handler,
3278 next_task => \&next_task,
3279 task_result => \&handle_task_result,
3280 task_done => \&handle_task_done,
3281 task_debug => \&handle_task_debug,
3282 watch_for_next_tasks => \&watch_for_next_tasks,
3283 watch_for_new_messages => \&watch_for_new_messages,
3284 watch_for_delivery_messages => \&watch_for_delivery_messages,
3285 watch_for_done_messages => \&watch_for_done_messages,
3286 watch_for_new_jobs => \&watch_for_new_jobs,
3287 watch_for_modified_jobs => \&watch_for_modified_jobs,
3288 watch_for_done_jobs => \&watch_for_done_jobs,
3289 watch_for_opsi_jobs => \&watch_for_opsi_jobs,
3290 watch_for_old_known_clients => \&watch_for_old_known_clients,
3291 create_packages_list_db => \&run_create_packages_list_db,
3292 create_fai_server_db => \&run_create_fai_server_db,
3293 create_fai_release_db => \&run_create_fai_release_db,
3294 recreate_packages_db => \&run_recreate_packages_db,
3295 session_run_result => \&session_run_result,
3296 session_run_debug => \&session_run_debug,
3297 session_run_done => \&session_run_done,
3298 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!" },
3299 }
3300 );
3303 POE::Kernel->run();
3304 exit;