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