d391e2de71a3bd1a3f384d333094e4095ff5265a
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 #===============================================================================
23 my $server_version = '$HeadURL: https://oss.gonicus.de/repositories/gosa/trunk/gosa-si/gosa-si-server $:$Rev$';
25 use strict;
26 use warnings;
27 use Getopt::Long;
28 use Config::IniFiles;
29 use POSIX;
31 use Fcntl qw/:flock/;
32 use IO::Socket::INET;
33 use IO::Handle;
34 use IO::Select;
35 use Symbol qw(qualify_to_ref);
36 use Crypt::Rijndael;
37 use MIME::Base64;
38 use Digest::MD5 qw(md5 md5_hex md5_base64);
39 use XML::Simple;
40 use Data::Dumper;
41 use Sys::Syslog qw( :DEFAULT setlogsock);
42 use Time::HiRes qw( usleep);
43 use Cwd;
44 use File::Spec;
45 use File::Basename;
46 use File::Find;
47 use File::Copy;
48 use File::Path;
49 use GOSA::GosaSupportDaemon;
50 use POE qw(Component::Server::TCP Wheel::Run Filter::Reference);
51 use Net::LDAP;
52 use Net::LDAP::Util qw(:escape);
54 # revision number of server and program name
55 my $server_headURL;
56 my $server_revision;
57 my $server_status;
58 our $prg= basename($0);
59 our $verbose= 0;
61 my $db_module = "DBsqlite";
62 {
63 no strict "refs";
64 require ("GOSA/".$db_module.".pm");
65 ("GOSA/".$db_module)->import;
66 #daemon_log("0 INFO: importing database module '$db_module'", 1);
67 }
69 my $modules_path = "/usr/lib/gosa-si/modules";
70 use lib "/usr/lib/gosa-si/modules";
72 our $global_kernel;
73 my ($foreground, $ping_timeout);
74 my ($server);
75 my ($gosa_server, $job_queue_timeout, $job_queue_loop_delay);
76 my ($messaging_db_loop_delay);
77 my ($procid, $pid);
78 my $arp_fifo;
79 my $debug_parts = 0;
80 my $debug_parts_bitstring;
81 my ($xml);
82 my $sources_list;
83 my $max_clients;
84 my %repo_files=();
85 my $repo_path;
86 my %repo_dirs=();
88 # Variables declared in config file are always set to 'our'
89 our (%cfg_defaults, $log_file, $pid_file,
90 $server_ip, $server_port, $ClientPackages_key, $dns_lookup,
91 $arp_activ, $gosa_unit_tag,
92 $GosaPackages_key, $gosa_timeout,
93 $foreign_server_string, $server_domain, $ServerPackages_key, $foreign_servers_register_delay,
94 $wake_on_lan_passwd, $job_synchronization, $modified_jobs_loop_delay,
95 $arp_enabled, $arp_interface,
96 $opsi_enabled, $opsi_server, $opsi_admin, $opsi_password,
97 $new_systems_ou,
98 );
100 # additional variable which should be globaly accessable
101 our $server_address;
102 our $server_mac_address;
103 our $gosa_address;
104 our $no_arp;
105 our $forground;
106 our $cfg_file;
107 our ($ldap_uri, $ldap_base, $ldap_admin_dn, $ldap_admin_password, $ldap_server_dn, $ldap_version);
108 our ($mysql_username, $mysql_password, $mysql_database, $mysql_host);
109 our $known_modules;
110 our $known_functions;
111 our $root_uid;
112 our $adm_gid;
114 # if foreground is not null, script will be not forked to background
115 $foreground = 0 ;
117 # specifies the timeout seconds while checking the online status of a registrating client
118 $ping_timeout = 5;
120 $no_arp = 0;
121 my $packages_list_under_construction = "/tmp/packages_list_creation_in_progress";
122 my @packages_list_statements;
123 my $watch_for_new_jobs_in_progress = 0;
125 # holds all incoming decrypted messages
126 our $incoming_db;
127 our $incoming_tn = 'incoming';
128 my $incoming_file_name;
129 my @incoming_col_names = ("id INTEGER PRIMARY KEY",
130 "timestamp VARCHAR(14) DEFAULT 'none'",
131 "headertag VARCHAR(255) DEFAULT 'none'",
132 "targettag VARCHAR(255) DEFAULT 'none'",
133 "xmlmessage TEXT",
134 "module VARCHAR(255) DEFAULT 'none'",
135 "sessionid VARCHAR(255) DEFAULT '0'",
136 );
138 # holds all gosa jobs
139 our $job_db;
140 our $job_queue_tn = 'jobs';
141 my $job_queue_file_name;
142 my @job_queue_col_names = ("id INTEGER PRIMARY KEY",
143 "timestamp VARCHAR(14) DEFAULT 'none'",
144 "status VARCHAR(255) DEFAULT 'none'",
145 "result TEXT",
146 "progress VARCHAR(255) DEFAULT 'none'",
147 "headertag VARCHAR(255) DEFAULT 'none'",
148 "targettag VARCHAR(255) DEFAULT 'none'",
149 "xmlmessage TEXT",
150 "macaddress VARCHAR(17) DEFAULT 'none'",
151 "plainname VARCHAR(255) DEFAULT 'none'",
152 "siserver VARCHAR(255) DEFAULT 'none'",
153 "modified INTEGER DEFAULT '0'",
154 );
156 # holds all other gosa-si-server
157 our $known_server_db;
158 our $known_server_tn = "known_server";
159 my $known_server_file_name;
160 my @known_server_col_names = ("hostname VARCHAR(255)", "macaddress VARCHAR(17)", "status VARCHAR(255)", "hostkey VARCHAR(255)", "loaded_modules TEXT", "timestamp VARCHAR(14)", "update_time VARCHAR(14)");
162 # holds all registrated clients
163 our $known_clients_db;
164 our $known_clients_tn = "known_clients";
165 my $known_clients_file_name;
166 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)");
168 # holds all registered clients at a foreign server
169 our $foreign_clients_db;
170 our $foreign_clients_tn = "foreign_clients";
171 my $foreign_clients_file_name;
172 my @foreign_clients_col_names = ("hostname VARCHAR(255)", "macaddress VARCHAR(17)", "regserver VARCHAR(255)", "timestamp VARCHAR(14)");
174 # holds all logged in user at each client
175 our $login_users_db;
176 our $login_users_tn = "login_users";
177 my $login_users_file_name;
178 my @login_users_col_names = ("client VARCHAR(255)", "user VARCHAR(255)", "timestamp VARCHAR(14)", "regserver VARCHAR(255) DEFAULT 'localhost'");
180 # holds all fai server, the debian release and tag
181 our $fai_server_db;
182 our $fai_server_tn = "fai_server";
183 my $fai_server_file_name;
184 our @fai_server_col_names = ("timestamp VARCHAR(14)", "server VARCHAR(255)", "fai_release VARCHAR(255)", "sections VARCHAR(255)", "tag VARCHAR(255)");
186 our $fai_release_db;
187 our $fai_release_tn = "fai_release";
188 my $fai_release_file_name;
189 our @fai_release_col_names = ("timestamp VARCHAR(14)", "fai_release VARCHAR(255)", "class VARCHAR(255)", "type VARCHAR(255)", "state VARCHAR(255)");
191 # holds all packages available from different repositories
192 our $packages_list_db;
193 our $packages_list_tn = "packages_list";
194 my $packages_list_file_name;
195 our @packages_list_col_names = ("distribution VARCHAR(255)", "package VARCHAR(255)", "version VARCHAR(255)", "section VARCHAR(255)", "description TEXT", "template LONGBLOB", "timestamp VARCHAR(14)");
196 my $outdir = "/tmp/packages_list_db";
197 my $arch = "i386";
199 # holds all messages which should be delivered to a user
200 our $messaging_db;
201 our $messaging_tn = "messaging";
202 our @messaging_col_names = ("id INTEGER", "subject TEXT", "message_from VARCHAR(255)", "message_to VARCHAR(255)",
203 "flag VARCHAR(255)", "direction VARCHAR(255)", "delivery_time VARCHAR(255)", "message TEXT", "timestamp VARCHAR(14)" );
204 my $messaging_file_name;
206 # path to directory to store client install log files
207 our $client_fai_log_dir = "/var/log/fai";
209 # queue which stores taskes until one of the $max_children children are ready to process the task
210 #my @tasks = qw();
211 my @msgs_to_decrypt = qw();
212 my $max_children = 2;
215 # loop delay for job queue to look for opsi jobs
216 my $job_queue_opsi_delay = 10;
217 our $opsi_client;
218 our $opsi_url;
220 # Lifetime of logged in user information. If no update information comes after n seconds,
221 # the user is expeceted to be no longer logged in or the host is no longer running. Because
222 # of this, the user is deleted from login_users_db
223 our $logged_in_user_date_of_expiry = 600;
225 # List of month names, used in function daemon_log
226 my @monthnames = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
228 %cfg_defaults = (
229 "general" => {
230 "log-file" => [\$log_file, "/var/run/".$prg.".log"],
231 "pid-file" => [\$pid_file, "/var/run/".$prg.".pid"],
232 },
233 "server" => {
234 "ip" => [\$server_ip, "0.0.0.0"],
235 "port" => [\$server_port, "20081"],
236 "known-clients" => [\$known_clients_file_name, '/var/lib/gosa-si/clients.db' ],
237 "known-servers" => [\$known_server_file_name, '/var/lib/gosa-si/servers.db'],
238 "incoming" => [\$incoming_file_name, '/var/lib/gosa-si/incoming.db'],
239 "login-users" => [\$login_users_file_name, '/var/lib/gosa-si/users.db'],
240 "fai-server" => [\$fai_server_file_name, '/var/lib/gosa-si/fai_server.db'],
241 "fai-release" => [\$fai_release_file_name, '/var/lib/gosa-si/fai_release.db'],
242 "packages-list" => [\$packages_list_file_name, '/var/lib/gosa-si/packages.db'],
243 "messaging" => [\$messaging_file_name, '/var/lib/gosa-si/messaging.db'],
244 "foreign-clients" => [\$foreign_clients_file_name, '/var/lib/gosa-si/foreign_clients.db'],
245 "source-list" => [\$sources_list, '/etc/apt/sources.list'],
246 "repo-path" => [\$repo_path, '/srv/www/repository'],
247 "ldap-uri" => [\$ldap_uri, ""],
248 "ldap-base" => [\$ldap_base, ""],
249 "ldap-admin-dn" => [\$ldap_admin_dn, ""],
250 "ldap-admin-password" => [\$ldap_admin_password, ""],
251 "ldap-version" => [\$ldap_version, 3],
252 "gosa-unit-tag" => [\$gosa_unit_tag, ""],
253 "max-clients" => [\$max_clients, 10],
254 "wol-password" => [\$wake_on_lan_passwd, ""],
255 "mysql-username" => [\$mysql_username, "gosa_si"],
256 "mysql-password" => [\$mysql_password, ""],
257 "mysql-database" => [\$mysql_database, "gosa_si"],
258 "mysql-host" => [\$mysql_host, "127.0.0.1"],
259 },
260 "GOsaPackages" => {
261 "job-queue" => [\$job_queue_file_name, '/var/lib/gosa-si/jobs.db'],
262 "job-queue-loop-delay" => [\$job_queue_loop_delay, 3],
263 "messaging-db-loop-delay" => [\$messaging_db_loop_delay, 3],
264 "key" => [\$GosaPackages_key, "none"],
265 "new-systems-ou" => [\$new_systems_ou, 'ou=workstations,ou=systems'],
266 },
267 "ClientPackages" => {
268 "key" => [\$ClientPackages_key, "none"],
269 "user-date-of-expiry" => [\$logged_in_user_date_of_expiry, 600],
270 },
271 "ServerPackages"=> {
272 "address" => [\$foreign_server_string, ""],
273 "dns-lookup" => [\$dns_lookup, "true"],
274 "domain" => [\$server_domain, ""],
275 "key" => [\$ServerPackages_key, "none"],
276 "key-lifetime" => [\$foreign_servers_register_delay, 120],
277 "job-synchronization-enabled" => [\$job_synchronization, "true"],
278 "synchronization-loop" => [\$modified_jobs_loop_delay, 5],
279 },
280 "ArpHandler" => {
281 "enabled" => [\$arp_enabled, "true"],
282 "interface" => [\$arp_interface, "all"],
283 },
284 "Opsi" => {
285 "enabled" => [\$opsi_enabled, "false"],
286 "server" => [\$opsi_server, "localhost"],
287 "admin" => [\$opsi_admin, "opsi-admin"],
288 "password" => [\$opsi_password, "secret"],
289 },
291 );
294 #=== FUNCTION ================================================================
295 # NAME: usage
296 # PARAMETERS: nothing
297 # RETURNS: nothing
298 # DESCRIPTION: print out usage text to STDERR
299 #===============================================================================
300 sub usage {
301 print STDERR << "EOF" ;
302 usage: $prg [-hvf] [-c config] [-d number]
304 -h : this (help) message
305 -c <file> : config file
306 -f : foreground, process will not be forked to background
307 -v : be verbose (multiple to increase verbosity)
308 'v': error logs
309 'vvv': warning plus error logs
310 'vvvvv': info plus warning logs
311 'vvvvvvv': debug plus info logs
312 -no-arp : starts $prg without connection to arp module
313 -d <int> : if verbose level is higher than 7x 'v' specified parts can be debugged
314 1 : receiving messages
315 2 : sending messages
316 4 : encrypting/decrypting messages
317 8 : verification if a message complies gosa-si requirements
318 16 :
320 EOF
321 print "\n" ;
322 }
325 #=== FUNCTION ================================================================
326 # NAME: logging
327 # PARAMETERS: level - string - default 'info'
328 # msg - string -
329 # facility - string - default 'LOG_DAEMON'
330 # RETURNS: nothing
331 # DESCRIPTION: function for logging
332 #===============================================================================
333 sub daemon_log {
334 my( $msg, $level ) = @_;
335 if (not defined $msg) { return }
336 if (not defined $level) { $level = 1 }
337 my $to_be_logged = 0;
339 # Write log line if line level is lower than verbosity given in commandline
340 if ($level <= $verbose)
341 {
342 $to_be_logged = 1 ;
343 }
345 # Write if debug flag is set and bitstring matches
346 if ($debug_parts > 0)
347 {
348 my $tmp_level = ($level - 10 >= 0) ? $level - 10 : 0 ;
349 my $tmp_level_bitstring = unpack("B32", pack("N", $tmp_level));
350 if (int($debug_parts_bitstring & $tmp_level_bitstring))
351 {
352 $to_be_logged = 1;
353 }
354 }
356 if ($to_be_logged)
357 {
358 if(defined $log_file){
359 my $open_log_fh = sysopen(LOG_HANDLE, $log_file, O_WRONLY | O_CREAT | O_APPEND , 0440);
360 if(not $open_log_fh) {
361 print STDERR "cannot open $log_file: $!";
362 return;
363 }
364 # Check owner and group of log_file and update settings if necessary
365 my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = stat($log_file);
366 if((not $uid eq $root_uid) || (not $gid eq $adm_gid)) {
367 chown($root_uid, $adm_gid, $log_file);
368 }
370 # Prepare time string for log message
371 my ($seconds,$minutes,$hours,$monthday,$month,$year,$weekday,$yearday,$sommertime) = localtime(time);
372 $hours = $hours < 10 ? $hours = "0".$hours : $hours;
373 $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
374 $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
375 $month = $monthnames[$month];
376 $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
377 $year+=1900;
380 # Build log message and write it to log file and commandline
381 chomp($msg);
382 my $log_msg = "$month $monthday $hours:$minutes:$seconds $prg $msg\n";
383 flock(LOG_HANDLE, LOCK_EX);
384 seek(LOG_HANDLE, 0, 2);
385 print LOG_HANDLE $log_msg;
386 flock(LOG_HANDLE, LOCK_UN);
387 if( $foreground )
388 {
389 print STDERR $log_msg;
390 }
391 close( LOG_HANDLE );
392 }
393 }
394 }
397 #=== FUNCTION ================================================================
398 # NAME: check_cmdline_param
399 # PARAMETERS: nothing
400 # RETURNS: nothing
401 # DESCRIPTION: validates commandline parameter
402 #===============================================================================
403 sub check_cmdline_param () {
404 my $err_counter = 0;
406 # Check configuration file
407 if(not defined($cfg_file)) {
408 $cfg_file = "/etc/gosa-si/server.conf";
409 if(! -r $cfg_file) {
410 print STDERR "Please specify a config file.\n";
411 $err_counter++;
412 }
413 }
415 # Prepare identification which gosa-si parts should be debugged and which not
416 if (defined $debug_parts)
417 {
418 if ($debug_parts =~ /^\d+$/)
419 {
420 $debug_parts_bitstring = unpack("B32", pack("N", $debug_parts));
421 }
422 else
423 {
424 print STDERR "Value '$debug_parts' invalid for option d (number expected)\n";
425 $err_counter++;
426 }
427 }
429 # Exit if an error occour
430 if( $err_counter > 0 )
431 {
432 &usage( "", 1 );
433 exit( -1 );
434 }
435 }
438 #=== FUNCTION ================================================================
439 # NAME: check_pid
440 # PARAMETERS: nothing
441 # RETURNS: nothing
442 # DESCRIPTION: handels pid processing
443 #===============================================================================
444 sub check_pid {
445 $pid = -1;
446 # Check, if we are already running
447 if( open(LOCK_FILE, "<$pid_file") ) {
448 $pid = <LOCK_FILE>;
449 if( defined $pid ) {
450 chomp( $pid );
451 if( -f "/proc/$pid/stat" ) {
452 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
453 if( $stat ) {
454 print STDERR "\nERROR: Already running!\n";
455 close( LOCK_FILE );
456 exit -1;
457 }
458 }
459 }
460 close( LOCK_FILE );
461 unlink( $pid_file );
462 }
464 # create a syslog msg if it is not to possible to open PID file
465 if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
466 my($msg) = "Couldn't obtain lockfile '$pid_file' ";
467 if (open(LOCK_FILE, '<', $pid_file)
468 && ($pid = <LOCK_FILE>))
469 {
470 chomp($pid);
471 $msg .= "(PID $pid)\n";
472 } else {
473 $msg .= "(unable to read PID)\n";
474 }
475 if( ! ($foreground) ) {
476 openlog( $0, "cons,pid", "daemon" );
477 syslog( "warning", $msg );
478 closelog();
479 }
480 else {
481 print( STDERR " $msg " );
482 }
483 exit( -1 );
484 }
485 }
487 #=== FUNCTION ================================================================
488 # NAME: import_modules
489 # PARAMETERS: module_path - string - abs. path to the directory the modules
490 # are stored
491 # RETURNS: nothing
492 # DESCRIPTION: each file in module_path which ends with '.pm' and activation
493 # state is on is imported by "require 'file';"
494 #===============================================================================
495 sub import_modules {
496 if (not -e $modules_path) {
497 daemon_log("0 ERROR: cannot find directory or directory is not readable: $modules_path", 1);
498 }
500 opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
502 while (defined (my $file = readdir (DIR))) {
503 if (not $file =~ /(\S*?).pm$/) {
504 next;
505 }
506 my $mod_name = $1;
508 # ArpHandler switch
509 if( $file =~ /ArpHandler.pm/ ) {
510 if( $arp_enabled eq "false" ) { next; }
511 }
513 eval { require $file; };
514 if ($@) {
515 daemon_log("0 ERROR: gosa-si-server could not load module $file", 1);
516 daemon_log("$@", 1);
517 exit;
518 } else {
519 my $info = eval($mod_name.'::get_module_info()');
520 # Only load module if get_module_info() returns a non-null object
521 if( $info ) {
522 my ($input_address, $input_key, $event_hash) = @{$info};
523 $known_modules->{$mod_name} = $info;
524 daemon_log("0 INFO: module $mod_name loaded", 5);
525 }
526 }
527 }
528 close (DIR);
529 }
531 #=== FUNCTION ================================================================
532 # NAME: password_check
533 # PARAMETERS: nothing
534 # RETURNS: nothing
535 # DESCRIPTION: escalates an critical error if two modules exist which are avaialable by
536 # the same password
537 #===============================================================================
538 sub password_check {
539 my $passwd_hash = {};
540 while (my ($mod_name, $mod_info) = each %$known_modules) {
541 my $mod_passwd = @$mod_info[1];
542 if (not defined $mod_passwd) { next; }
543 if (not exists $passwd_hash->{$mod_passwd}) {
544 $passwd_hash->{$mod_passwd} = $mod_name;
546 # escalates critical error
547 } else {
548 &daemon_log("0 ERROR: two loaded modules do have the same password. Please modify the 'key'-parameter in config file");
549 &daemon_log("0 ERROR: module='$mod_name' and module='".$passwd_hash->{$mod_passwd}."'");
550 exit( -1 );
551 }
552 }
554 }
557 #=== FUNCTION ================================================================
558 # NAME: sig_int_handler
559 # PARAMETERS: signal - string - signal arose from system
560 # RETURNS: nothing
561 # DESCRIPTION: handels tasks to be done befor signal becomes active
562 #===============================================================================
563 sub sig_int_handler {
564 my ($signal) = @_;
566 # if (defined($ldap_handle)) {
567 # $ldap_handle->disconnect;
568 # }
569 # TODO alle verbliebenden ldap verbindungen aus allen heaps beenden
572 daemon_log("shutting down gosa-si-server", 1);
573 system("kill `ps -C gosa-si-server -o pid=`");
574 }
575 $SIG{INT} = \&sig_int_handler;
578 sub check_key_and_xml_validity {
579 my ($crypted_msg, $module_key, $session_id) = @_;
580 my $msg;
581 my $msg_hash;
582 my $error_string;
583 eval{
584 $msg = &decrypt_msg($crypted_msg, $module_key);
586 if ($msg =~ /<xml>/i){
587 $msg =~ s/\s+/ /g; # just for better daemon_log
588 daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 18);
589 $msg_hash = $xml->XMLin($msg, ForceArray=>1);
591 ##############
592 # check header
593 if( not exists $msg_hash->{'header'} ) { die "no header specified"; }
594 my $header_l = $msg_hash->{'header'};
595 if( (1 > @{$header_l}) || ( ( 'HASH' eq ref @{$header_l}[0]) && (1 > keys %{@{$header_l}[0]}) ) ) { die 'empty header tag'; }
596 if( 1 < @{$header_l} ) { die 'more than one header specified'; }
597 my $header = @{$header_l}[0];
598 if( 0 == length $header) { die 'empty string in header tag'; }
600 ##############
601 # check source
602 if( not exists $msg_hash->{'source'} ) { die "no source specified"; }
603 my $source_l = $msg_hash->{'source'};
604 if( (1 > @{$source_l}) || ( ( 'HASH' eq ref @{$source_l}[0]) && (1 > keys %{@{$source_l}[0]}) ) ) { die 'empty source tag'; }
605 if( 1 < @{$source_l} ) { die 'more than one source specified'; }
606 my $source = @{$source_l}[0];
607 if( 0 == length $source) { die 'source error'; }
609 ##############
610 # check target
611 if( not exists $msg_hash->{'target'} ) { die "no target specified"; }
612 my $target_l = $msg_hash->{'target'};
613 if( (1 > @{$target_l}) || ( ('HASH' eq ref @{$target_l}[0]) && (1 > keys %{@{$target_l}[0]}) ) ) { die 'empty target tag'; }
614 }
615 };
616 if($@) {
617 daemon_log("$session_id ERROR: do not understand the message: $@", 1);
618 $msg = undef;
619 $msg_hash = undef;
620 }
622 return ($msg, $msg_hash);
623 }
626 sub check_outgoing_xml_validity {
627 my ($msg, $session_id) = @_;
629 my $msg_hash;
630 eval{
631 $msg_hash = $xml->XMLin($msg, ForceArray=>1);
633 ##############
634 # check header
635 my $header_l = $msg_hash->{'header'};
636 if( 1 != @{$header_l} ) {
637 die 'no or more than one headers specified';
638 }
639 my $header = @{$header_l}[0];
640 if( 0 == length $header) {
641 die 'header has length 0';
642 }
644 ##############
645 # check source
646 my $source_l = $msg_hash->{'source'};
647 if( 1 != @{$source_l} ) {
648 die 'no or more than 1 sources specified';
649 }
650 my $source = @{$source_l}[0];
651 if( 0 == length $source) {
652 die 'source has length 0';
653 }
655 # Check if source contains hostname instead of ip address
656 if($source =~ /^[a-z][a-z0-9\.]+:\d+$/i) {
657 my ($hostname,$port) = split(/:/, $source);
658 my $ip_address = inet_ntoa(scalar gethostbyname($hostname));
659 if(defined($ip_address) && $ip_address =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/ && $port =~ /^\d+$/) {
660 # Write ip address to $source variable
661 $source = "$ip_address:$port";
662 }
663 }
664 unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
665 $source =~ /^GOSA$/i) {
666 die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
667 }
669 ##############
670 # check target
671 my $target_l = $msg_hash->{'target'};
672 if( 0 == @{$target_l} ) {
673 die "no targets specified";
674 }
675 foreach my $target (@$target_l) {
676 if( 0 == length $target) {
677 die "target has length 0";
678 }
679 unless( $target =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
680 $target =~ /^GOSA$/i ||
681 $target =~ /^\*$/ ||
682 $target =~ /KNOWN_SERVER/i ||
683 $target =~ /JOBDB/i ||
684 $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 ){
685 die "target '$target' is not a complete ip-address with port or a valid target name or a mac-address";
686 }
687 }
688 };
689 if($@) {
690 daemon_log("$session_id ERROR: outgoing msg is not gosa-si envelope conform: $@", 1);
691 daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 1);
692 $msg_hash = undef;
693 }
695 return ($msg_hash);
696 }
699 sub input_from_known_server {
700 my ($input, $remote_ip, $session_id) = @_ ;
701 my ($msg, $msg_hash, $module);
703 my $sql_statement= "SELECT * FROM known_server";
704 my $query_res = $known_server_db->select_dbentry( $sql_statement );
706 while( my ($hit_num, $hit) = each %{ $query_res } ) {
707 my $host_name = $hit->{hostname};
708 if( not $host_name =~ "^$remote_ip") {
709 next;
710 }
711 my $host_key = $hit->{hostkey};
712 daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 14);
713 daemon_log("$session_id DEBUG: input_from_known_server: host_key: $host_key", 14);
715 # check if module can open msg envelope with module key
716 my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
717 if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
718 daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 14);
719 daemon_log("$@", 14);
720 next;
721 }
722 else {
723 $msg = $tmp_msg;
724 $msg_hash = $tmp_msg_hash;
725 $module = "ServerPackages";
726 daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 14);
727 last;
728 }
729 }
731 if( (!$msg) || (!$msg_hash) || (!$module) ) {
732 daemon_log("$session_id DEBUG: Incoming message is not from a known server", 14);
733 }
735 return ($msg, $msg_hash, $module);
736 }
739 sub input_from_known_client {
740 my ($input, $remote_ip, $session_id) = @_ ;
741 my ($msg, $msg_hash, $module);
743 my $sql_statement= "SELECT * FROM known_clients";
744 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
745 while( my ($hit_num, $hit) = each %{ $query_res } ) {
746 my $host_name = $hit->{hostname};
747 if( not $host_name =~ /^$remote_ip/) {
748 next;
749 }
750 my $host_key = $hit->{hostkey};
751 &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 14);
752 &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 14);
754 # check if module can open msg envelope with module key
755 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
757 if( (!$msg) || (!$msg_hash) ) {
758 &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 14);
759 next;
760 }
761 else {
762 $module = "ClientPackages";
763 daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 14);
764 last;
765 }
766 }
768 if( (!$msg) || (!$msg_hash) || (!$module) ) {
769 &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 14);
770 }
772 return ($msg, $msg_hash, $module);
773 }
776 sub input_from_unknown_host {
777 no strict "refs";
778 my ($input, $session_id) = @_ ;
779 my ($msg, $msg_hash, $module);
780 my $error_string;
782 my %act_modules = %$known_modules;
784 while( my ($mod, $info) = each(%act_modules)) {
786 # check a key exists for this module
787 my $module_key = ${$mod."_key"};
788 if( not defined $module_key ) {
789 if( $mod eq 'ArpHandler' ) {
790 next;
791 }
792 daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
793 next;
794 }
795 daemon_log("$session_id DEBUG: $mod: $module_key", 14);
797 # check if module can open msg envelope with module key
798 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
799 if( (not defined $msg) || (not defined $msg_hash) ) {
800 next;
801 } else {
802 $module = $mod;
803 daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 18);
804 last;
805 }
806 }
808 if( (!$msg) || (!$msg_hash) || (!$module)) {
809 daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 14);
810 }
812 return ($msg, $msg_hash, $module);
813 }
816 sub create_ciphering {
817 my ($passwd) = @_;
818 if((!defined($passwd)) || length($passwd)==0) {
819 $passwd = "";
820 }
821 $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
822 my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
823 my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
824 $my_cipher->set_iv($iv);
825 return $my_cipher;
826 }
829 sub encrypt_msg {
830 my ($msg, $key) = @_;
831 my $my_cipher = &create_ciphering($key);
832 my $len;
833 {
834 use bytes;
835 $len= 16-length($msg)%16;
836 }
837 $msg = "\0"x($len).$msg;
838 $msg = $my_cipher->encrypt($msg);
839 chomp($msg = &encode_base64($msg));
840 # there are no newlines allowed inside msg
841 $msg=~ s/\n//g;
842 return $msg;
843 }
846 sub decrypt_msg {
848 my ($msg, $key) = @_ ;
849 $msg = &decode_base64($msg);
850 my $my_cipher = &create_ciphering($key);
851 $msg = $my_cipher->decrypt($msg);
852 $msg =~ s/\0*//g;
853 return $msg;
854 }
857 sub get_encrypt_key {
858 my ($target) = @_ ;
859 my $encrypt_key;
860 my $error = 0;
862 # target can be in known_server
863 if( not defined $encrypt_key ) {
864 my $sql_statement= "SELECT * FROM known_server WHERE hostname='$target'";
865 my $query_res = $known_server_db->select_dbentry( $sql_statement );
866 while( my ($hit_num, $hit) = each %{ $query_res } ) {
867 my $host_name = $hit->{hostname};
868 if( $host_name ne $target ) {
869 next;
870 }
871 $encrypt_key = $hit->{hostkey};
872 last;
873 }
874 }
876 # target can be in known_client
877 if( not defined $encrypt_key ) {
878 my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$target'";
879 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
880 while( my ($hit_num, $hit) = each %{ $query_res } ) {
881 my $host_name = $hit->{hostname};
882 if( $host_name ne $target ) {
883 next;
884 }
885 $encrypt_key = $hit->{hostkey};
886 last;
887 }
888 }
890 return $encrypt_key;
891 }
894 #=== FUNCTION ================================================================
895 # NAME: open_socket
896 # PARAMETERS: PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
897 # [PeerPort] string necessary if port not appended by PeerAddr
898 # RETURNS: socket IO::Socket::INET
899 # DESCRIPTION: open a socket to PeerAddr
900 #===============================================================================
901 sub open_socket {
902 my ($PeerAddr, $PeerPort) = @_ ;
903 if(defined($PeerPort)){
904 $PeerAddr = $PeerAddr.":".$PeerPort;
905 }
906 my $socket;
907 $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
908 Porto => "tcp",
909 Type => SOCK_STREAM,
910 Timeout => 5,
911 );
912 if(not defined $socket) {
913 return;
914 }
915 # &daemon_log("DEBUG: open_socket: $PeerAddr", 7);
916 return $socket;
917 }
920 sub send_msg_to_target {
921 my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
922 my $error = 0;
923 my $header;
924 my $timestamp = &get_time();
925 my $new_status;
926 my $act_status;
927 my ($sql_statement, $res);
929 if( $msg_header ) {
930 $header = "'$msg_header'-";
931 } else {
932 $header = "";
933 }
935 # Memorize own source address
936 my $own_source_address = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
937 $own_source_address .= ":".$server_port;
939 # Patch 0.0.0.0 source to real address
940 $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$own_source_address<\/source>/s;
941 # Patch GOSA source to real address and add forward_to_gosa tag
942 $msg =~ s/<source>GOSA<\/source>/<source>$own_source_address<\/source> <forward_to_gosa>$own_source_address,$session_id<\/forward_to_gosa>/ ;
944 # encrypt xml msg
945 my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
947 # opensocket
948 my $socket = &open_socket($address);
949 if( !$socket ) {
950 daemon_log("$session_id ERROR: Cannot open socket to host '$address'. Message processing aborted!", 1);
951 $error++;
952 }
954 if( $error == 0 ) {
955 # send xml msg
956 print $socket $crypted_msg.";$own_source_address\n";
957 daemon_log("$session_id INFO: send ".$header."msg to $address", 5);
958 daemon_log("$session_id DEBUG: message:\n$msg", 12);
960 }
962 # close socket in any case
963 if( $socket ) {
964 close $socket;
965 }
967 if( $error > 0 ) { $new_status = "down"; }
968 else { $new_status = $msg_header; }
971 # known_clients
972 $sql_statement = "SELECT * FROM $known_clients_tn WHERE hostname='$address'";
973 $res = $known_clients_db->select_dbentry($sql_statement);
974 if( keys(%$res) == 1) {
975 $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
976 if ($act_status eq "down" && $new_status eq "down") {
977 $sql_statement = "DELETE FROM known_clients WHERE hostname='$address'";
978 $res = $known_clients_db->del_dbentry($sql_statement);
979 daemon_log("$session_id WARNING: failed 2x to send msg to host '$address', delete host from known_clients", 3);
980 } else {
981 $sql_statement = "UPDATE known_clients SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
982 $res = $known_clients_db->update_dbentry($sql_statement);
983 if($new_status eq "down"){
984 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
985 } else {
986 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
987 }
988 }
989 }
991 # known_server
992 $sql_statement = "SELECT * FROM $known_server_tn WHERE hostname='$address'";
993 $res = $known_server_db->select_dbentry($sql_statement);
994 if( keys(%$res) == 1) {
995 $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
996 if ($act_status eq "down" && $new_status eq "down") {
997 $sql_statement = "DELETE FROM known_server WHERE hostname='$address'";
998 $res = $known_server_db->del_dbentry($sql_statement);
999 daemon_log("$session_id WARNING: failed 2x to send a message to host '$address', delete host from known_server", 3);
1000 }
1001 else {
1002 $sql_statement = "UPDATE known_server SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
1003 $res = $known_server_db->update_dbentry($sql_statement);
1004 if($new_status eq "down"){
1005 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
1006 } else {
1007 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
1008 }
1009 }
1010 }
1011 return $error;
1012 }
1015 sub update_jobdb_status_for_send_msgs {
1016 my ($session_id, $answer, $error) = @_;
1017 &daemon_log("$session_id DEBUG: try to update job status", 7);
1018 if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
1019 my $jobdb_id = $1;
1021 $answer =~ /<header>(.*)<\/header>/;
1022 my $job_header = $1;
1024 $answer =~ /<target>(.*)<\/target>/;
1025 my $job_target = $1;
1027 # Sending msg failed
1028 if( $error ) {
1030 # Set jobs to done, jobs do not need to deliver their message in any case
1031 if (($job_header eq "trigger_action_localboot")
1032 ||($job_header eq "trigger_action_lock")
1033 ||($job_header eq "trigger_action_halt")
1034 ) {
1035 my $sql_statement = "UPDATE $job_queue_tn SET status='done' WHERE id=$jobdb_id";
1036 &daemon_log("$session_id DEBUG: $sql_statement", 7);
1037 my $res = $job_db->update_dbentry($sql_statement);
1039 # Reactivate jobs, jobs need to deliver their message
1040 } elsif (($job_header eq "trigger_action_activate")
1041 ||($job_header eq "trigger_action_update")
1042 ||($job_header eq "trigger_action_reinstall")
1043 ||($job_header eq "trigger_activate_new")
1044 ) {
1045 &reactivate_job_with_delay($session_id, $job_target, $job_header, 30 );
1047 # For all other messages
1048 } else {
1049 my $sql_statement = "UPDATE $job_queue_tn ".
1050 "SET status='error', result='can not deliver msg, please consult log file' ".
1051 "WHERE id=$jobdb_id";
1052 &daemon_log("$session_id DEBUG: $sql_statement", 7);
1053 my $res = $job_db->update_dbentry($sql_statement);
1054 }
1056 # Sending msg was successful
1057 } else {
1058 # Set jobs localboot, lock, activate, halt, reboot and wake to done
1059 # jobs reinstall, update, inst_update do themself setting to done
1060 if (($job_header eq "trigger_action_localboot")
1061 ||($job_header eq "trigger_action_lock")
1062 ||($job_header eq "trigger_action_activate")
1063 ||($job_header eq "trigger_action_halt")
1064 ||($job_header eq "trigger_action_reboot")
1065 ||($job_header eq "trigger_action_wake")
1066 ||($job_header eq "trigger_wake")
1067 ) {
1069 my $sql_statement = "UPDATE $job_queue_tn ".
1070 "SET status='done' ".
1071 "WHERE id=$jobdb_id AND status='processed'";
1072 &daemon_log("$session_id DEBUG: $sql_statement", 7);
1073 my $res = $job_db->update_dbentry($sql_statement);
1074 } else {
1075 &daemon_log("$session_id DEBUG: sending message succeed but cannot update job status.", 7);
1076 }
1077 }
1078 } else {
1079 &daemon_log("$session_id DEBUG: cannot update job status, msg has no jobdb_id-tag.", 7);
1080 }
1081 }
1083 sub reactivate_job_with_delay {
1084 my ($session_id, $target, $header, $delay) = @_ ;
1085 # Sometimes the client is still booting or does not wake up, in this case reactivate the job (if it exists) with a delay of n sec
1087 if (not defined $delay) { $delay = 30 } ;
1088 my $delay_timestamp = &calc_timestamp(&get_time(), "plus", $delay);
1090 my $sql = "UPDATE $job_queue_tn Set timestamp='$delay_timestamp', status='waiting' WHERE (macaddress LIKE 'target' AND headertag='$header')";
1091 my $res = $job_db->update_dbentry($sql);
1092 daemon_log("$session_id INFO: '$header'-job will be reactivated at '$delay_timestamp' ".
1093 "cause client '$target' is currently not available", 5);
1094 daemon_log("$session_id $sql", 7);
1095 return;
1096 }
1099 sub sig_handler {
1100 my ($kernel, $signal) = @_[KERNEL, ARG0] ;
1101 daemon_log("0 INFO got signal '$signal'", 1);
1102 $kernel->sig_handled();
1103 return;
1104 }
1107 sub msg_to_decrypt {
1108 my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
1109 my $session_id = $session->ID;
1110 my ($msg, $msg_hash, $module);
1111 my $error = 0;
1113 # fetch new msg out of @msgs_to_decrypt
1114 my $tmp_next_msg = shift @msgs_to_decrypt;
1115 my ($next_msg, $msg_source) = split(/;/, $tmp_next_msg);
1117 # msg is from a new client or gosa
1118 ($msg, $msg_hash, $module) = &input_from_unknown_host($next_msg, $session_id);
1120 # msg is from a gosa-si-server
1121 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1122 if (not defined $msg_source)
1123 {
1124 # Only needed, to be compatible with older gosa-si-server versions
1125 ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $heap->{'remote_ip'}, $session_id);
1126 }
1127 else
1128 {
1129 ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $msg_source, $session_id);
1130 }
1131 }
1132 # msg is from a gosa-si-client
1133 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1134 ($msg, $msg_hash, $module) = &input_from_known_client($next_msg, $msg_source, $session_id);
1135 }
1136 # an error occurred
1137 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1138 # If an incoming msg could not be decrypted (maybe a wrong key), decide if msg comes from a client
1139 # or a server. In case of a client, send a ping. If the client could not understand a msg from its
1140 # server the client cause a re-registering process. In case of a server, decrease update_time in kown_server_db
1141 # and trigger a re-registering process for servers
1142 if (defined $msg_source && $msg_source =~ /:$server_port$/)
1143 {
1144 daemon_log("$session_id WARNING: Cannot understand incoming msg from server '$msg_source'. Cause re-registration process for servers.", 3);
1145 my $update_statement = "UPDATE $known_server_tn SET update_time='19700101000000' WHERE hostname='$msg_source'";
1146 daemon_log("$session_id DEBUG: $update_statement", 7);
1147 my $upadte_res = $known_server_db->exec_statement($update_statement);
1148 $kernel->yield("register_at_foreign_servers");
1149 }
1150 elsif (defined $msg_source)
1151 {
1152 daemon_log("$session_id WARNING: Cannot understand incoming msg from client '$msg_source'. Send ping-msg to cause a re-registering of the client if necessary", 3);
1153 #my $remote_ip = $heap->{'remote_ip'};
1154 #my $remote_port = $heap->{'remote_port'};
1155 my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source><target>$msg_source</target></xml>";
1156 my ($test_error, $test_error_string) = &send_msg_to_target($ping_msg, "$msg_source", "dummy-key", "gosa_ping", $session_id);
1157 daemon_log("$session_id WARNING: sending msg to cause re-registering: $ping_msg", 3);
1158 }
1159 else
1160 {
1161 my $foreign_host = defined $msg_source ? $msg_source : $heap->{'remote_ip'};
1162 daemon_log("$session_id ERROR: incoming message from host '$foreign_host' cannot be understood. Processing aborted: $tmp_next_msg", 1);
1163 }
1165 $error++
1166 }
1169 my $header;
1170 my $target;
1171 my $source;
1172 my $done = 0;
1173 my $sql;
1174 my $res;
1176 # check whether this message should be processed here
1177 if ($error == 0) {
1178 $header = @{$msg_hash->{'header'}}[0];
1179 $target = @{$msg_hash->{'target'}}[0];
1180 $source = @{$msg_hash->{'source'}}[0];
1181 my $not_found_in_known_clients_db = 0;
1182 my $not_found_in_known_server_db = 0;
1183 my $not_found_in_foreign_clients_db = 0;
1184 my $local_address;
1185 my $local_mac;
1186 my ($target_ip, $target_port) = split(':', $target);
1188 # Determine the local ip address if target is an ip address
1189 if ($target =~ /^\d+\.\d+\.\d+\.\d+:\d+$/) {
1190 $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1191 } else {
1192 $local_address = $server_address;
1193 }
1195 # Determine the local mac address if target is a mac address
1196 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) {
1197 my $loc_ip = &get_local_ip_for_remote_ip($heap->{'remote_ip'});
1198 my $network_interface= &get_interface_for_ip($loc_ip);
1199 $local_mac = &get_mac_for_interface($network_interface);
1200 } else {
1201 $local_mac = $server_mac_address;
1202 }
1204 # target and source is equal to GOSA -> process here
1205 if (not $done) {
1206 if ($target eq "GOSA" && $source eq "GOSA") {
1207 $done = 1;
1208 &daemon_log("$session_id DEBUG: target and source is 'GOSA' -> process '$header' here", 11);
1209 }
1210 }
1212 # target is own address without forward_to_gosa-tag -> process here
1213 if (not $done) {
1214 #if ((($target eq $local_address) || ($target eq $local_mac) ) && (not exists $msg_hash->{'forward_to_gosa'})) {
1215 if (($target eq $local_address) && (not exists $msg_hash->{'forward_to_gosa'})) {
1216 $done = 1;
1217 if ($source eq "GOSA") {
1218 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1219 }
1220 &daemon_log("$session_id DEBUG: target is own address without forward_to_gosa-tag -> process '$header' here", 11);
1221 }
1222 }
1224 # target is own address with forward_to_gosa-tag not pointing to myself -> process here
1225 if (not $done) {
1226 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1227 my $gosa_at;
1228 my $gosa_session_id;
1229 if (($target eq $local_address) && (defined $forward_to_gosa)){
1230 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1231 if ($gosa_at ne $local_address) {
1232 $done = 1;
1233 &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag not pointing to myself -> process '$header' here", 11);
1234 }
1235 }
1236 }
1238 # Target is a client address and there is a processing function within a plugin -> process loaclly
1239 if (not $done)
1240 {
1241 # Check if target is a client address
1242 $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1243 $res = $known_clients_db->select_dbentry($sql);
1244 if ((keys(%$res) > 0) )
1245 {
1246 my $hostname = $res->{1}->{'hostname'};
1247 my $reduced_header = $header;
1248 $reduced_header =~ s/gosa_//;
1249 # Check if there is a processing function within a plugin
1250 if (exists $known_functions->{$reduced_header})
1251 {
1252 $msg =~ s/<target>\S*<\/target>/<target>$hostname<\/target>/;
1253 $done = 1;
1254 &daemon_log("$session_id DEBUG: Target is client address with processing function within a plugin -> process '$header' here", 11);
1255 }
1256 }
1257 }
1259 # If header has a 'job_' prefix, do always process message locally
1260 # which means put it into job queue
1261 if ((not $done) && ($header =~ /job_/))
1262 {
1263 $done = 1;
1264 &daemon_log("$session_id DEBUG: Header has a 'job_' prefix. Put it into job queue. -> process '$header' here", 11);
1265 }
1267 # if message should be processed here -> add message to incoming_db
1268 if ($done) {
1269 # if a 'job_' or a 'gosa_' message comes from a foreign server, fake module from
1270 # ServerPackages to GosaPackages so gosa-si-server knows how to process this kind of messages
1271 if ($header =~ /^gosa_/ || $header =~ /^job_/) {
1272 $module = "GosaPackages";
1273 }
1275 my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1276 primkey=>[],
1277 headertag=>$header,
1278 targettag=>$target,
1279 xmlmessage=>&encode_base64($msg),
1280 timestamp=>&get_time,
1281 module=>$module,
1282 sessionid=>$session_id,
1283 } );
1285 }
1287 # target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa
1288 if (not $done) {
1289 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1290 my $gosa_at;
1291 my $gosa_session_id;
1292 if (($target eq $local_address) && (defined $forward_to_gosa)){
1293 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1294 if ($gosa_at eq $local_address) {
1295 my $session_reference = $kernel->ID_id_to_session($gosa_session_id);
1296 if( defined $session_reference ) {
1297 $heap = $session_reference->get_heap();
1298 }
1299 if(exists $heap->{'client'}) {
1300 $msg = &encrypt_msg($msg, $GosaPackages_key);
1301 $heap->{'client'}->put($msg);
1302 &daemon_log("$session_id DEBUG: incoming '$header' message forwarded to GOsa", 11);
1303 }
1304 $done = 1;
1305 &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag pointing at myself -> forward '$header' to gosa", 11);
1306 }
1307 }
1309 }
1311 # target is a client address in known_clients -> forward to client
1312 if (not $done) {
1313 $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1314 $res = $known_clients_db->select_dbentry($sql);
1315 if (keys(%$res) > 0)
1316 {
1317 $done = 1;
1318 &daemon_log("$session_id DEBUG: target is a client address in known_clients -> forward '$header' to client", 11);
1319 my $hostkey = $res->{1}->{'hostkey'};
1320 my $hostname = $res->{1}->{'hostname'};
1321 $msg =~ s/<target>\S*<\/target>/<target>$hostname<\/target>/;
1322 $msg =~ s/<header>gosa_/<header>/;
1323 my $error= &send_msg_to_target($msg, $hostname, $hostkey, $header, $session_id);
1324 if ($error) {
1325 &daemon_log("$session_id ERROR: Some problems occurred while trying to send msg to client '$hostkey': $msg", 1);
1326 }
1327 }
1328 else
1329 {
1330 $not_found_in_known_clients_db = 1;
1331 }
1332 }
1334 # target is a client address in foreign_clients -> forward to registration server
1335 if (not $done) {
1336 $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1337 $res = $foreign_clients_db->select_dbentry($sql);
1338 if (keys(%$res) > 0) {
1339 my $hostname = $res->{1}->{'hostname'};
1340 my ($host_ip, $host_port) = split(/:/, $hostname);
1341 my $local_address = &get_local_ip_for_remote_ip($host_ip).":$server_port";
1342 my $regserver = $res->{1}->{'regserver'};
1343 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$regserver'";
1344 my $res = $known_server_db->select_dbentry($sql);
1345 if (keys(%$res) > 0) {
1346 my $regserver_key = $res->{1}->{'hostkey'};
1347 $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1348 $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1349 if ($source eq "GOSA") {
1350 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1351 }
1352 my $error= &send_msg_to_target($msg, $regserver, $regserver_key, $header, $session_id);
1353 if ($error) {
1354 &daemon_log("$session_id ERROR: some problems occurred while trying to send msg to registration server: $msg", 1);
1355 }
1356 }
1357 $done = 1;
1358 &daemon_log("$session_id DEBUG: target is a client address in foreign_clients -> forward '$header' to registration server", 11);
1359 } else {
1360 $not_found_in_foreign_clients_db = 1;
1361 }
1362 }
1364 # target is a server address -> forward to server
1365 if (not $done) {
1366 $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1367 $res = $known_server_db->select_dbentry($sql);
1368 if (keys(%$res) > 0) {
1369 my $hostkey = $res->{1}->{'hostkey'};
1371 if ($source eq "GOSA") {
1372 $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1373 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1375 }
1377 &send_msg_to_target($msg, $target, $hostkey, $header, $session_id);
1378 $done = 1;
1379 &daemon_log("$session_id DEBUG: target is a server address -> forward '$header' to server", 11);
1380 } else {
1381 $not_found_in_known_server_db = 1;
1382 }
1383 }
1386 # target is not in foreign_clients_db, known_server_db or known_clients_db, maybe it is a complete new one -> process here
1387 if ( $not_found_in_foreign_clients_db
1388 && $not_found_in_known_server_db
1389 && $not_found_in_known_clients_db) {
1390 &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 '$header' here", 11);
1391 if ($header =~ /^gosa_/ || $header =~ /^job_/) {
1392 $module = "GosaPackages";
1393 }
1394 my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1395 primkey=>[],
1396 headertag=>$header,
1397 targettag=>$target,
1398 xmlmessage=>&encode_base64($msg),
1399 timestamp=>&get_time,
1400 module=>$module,
1401 sessionid=>$session_id,
1402 } );
1403 $done = 1;
1404 }
1407 if (not $done) {
1408 daemon_log("$session_id ERROR: do not know what to do with this message: $msg", 1);
1409 if ($source eq "GOSA") {
1410 my %data = ('error_msg' => &encode_base64($msg), 'error_string' => "Do not know what to do with this message!");
1411 my $error_msg = &build_msg("error", $local_address, "GOSA", \%data );
1413 my $session_reference = $kernel->ID_id_to_session($session_id);
1414 if( defined $session_reference ) {
1415 $heap = $session_reference->get_heap();
1416 }
1417 if(exists $heap->{'client'}) {
1418 $error_msg = &encrypt_msg($error_msg, $GosaPackages_key);
1419 $heap->{'client'}->put($error_msg);
1420 }
1421 }
1422 }
1424 }
1426 return;
1427 }
1430 sub next_task {
1431 my ($session, $heap, $task) = @_[SESSION, HEAP, ARG0, ARG1];
1432 my $running_task = POE::Wheel::Run->new(
1433 Program => sub { process_task($session, $heap, $task) },
1434 StdioFilter => POE::Filter::Reference->new(),
1435 StdoutEvent => "task_result",
1436 StderrEvent => "task_debug",
1437 CloseEvent => "task_done",
1438 );
1439 $heap->{task}->{ $running_task->ID } = $running_task;
1440 }
1442 sub handle_task_result {
1443 my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
1444 my $client_answer = $result->{'answer'};
1445 if( $client_answer =~ s/session_id=(\d+)$// ) {
1446 my $session_id = $1;
1447 if( defined $session_id ) {
1448 my $session_reference = $kernel->ID_id_to_session($session_id);
1449 if( defined $session_reference ) {
1450 $heap = $session_reference->get_heap();
1451 }
1452 }
1454 if(exists $heap->{'client'}) {
1455 $heap->{'client'}->put($client_answer);
1456 }
1457 }
1458 $kernel->sig(CHLD => "child_reap");
1459 }
1461 sub handle_task_debug {
1462 my $result = $_[ARG0];
1463 print STDERR "$result\n";
1464 }
1466 sub handle_task_done {
1467 my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1468 delete $heap->{task}->{$task_id};
1469 if (exists $heap->{ldap_handle}->{$task_id}) {
1470 &release_ldap_handle($heap->{ldap_handle}->{$task_id});
1471 }
1472 }
1474 sub process_task {
1475 no strict "refs";
1476 #CHECK: Not @_[...]?
1477 my ($session, $heap, $task) = @_;
1478 my $error = 0;
1479 my $answer_l;
1480 my ($answer_header, @answer_target_l, $answer_source);
1481 my $client_answer = "";
1483 # prepare all variables needed to process message
1484 #my $msg = $task->{'xmlmessage'};
1485 my $msg = &decode_base64($task->{'xmlmessage'});
1486 my $incoming_id = $task->{'id'};
1487 my $module = $task->{'module'};
1488 my $header = $task->{'headertag'};
1489 my $session_id = $task->{'sessionid'};
1490 my $msg_hash;
1491 eval {
1492 $msg_hash = $xml->XMLin($msg, ForceArray=>1);
1493 };
1494 daemon_log("ERROR: XML failure '$@'") if ($@);
1495 my $source = @{$msg_hash->{'source'}}[0];
1497 # set timestamp of incoming client uptodate, so client will not
1498 # be deleted from known_clients because of expiration
1499 my $cur_time = &get_time();
1500 my $sql = "UPDATE $known_clients_tn SET timestamp='$cur_time' WHERE hostname='$source'";
1501 my $res = $known_clients_db->exec_statement($sql);
1503 ######################
1504 # process incoming msg
1505 if( $error == 0) {
1506 daemon_log("$session_id INFO: Incoming msg (session_id=$session_id) with header '".@{$msg_hash->{'header'}}[0]."'", 5);
1507 daemon_log("$session_id DEBUG: Processing module ".$module, 7);
1508 $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1510 if ( 0 < @{$answer_l} ) {
1511 my $answer_str = join("\n", @{$answer_l});
1512 while ($answer_str =~ /<header>(\w+)<\/header>/g) {
1513 daemon_log("$session_id INFO: got answer message with header '$1'", 5);
1514 }
1515 daemon_log("$session_id DEBUG: $module: got answer from module: \n".$answer_str,9);
1516 } else {
1517 daemon_log("$session_id DEBUG: $module: got no answer from module!" ,7);
1518 }
1520 }
1521 if( !$answer_l ) { $error++ };
1523 ########
1524 # answer
1525 if( $error == 0 ) {
1527 foreach my $answer ( @{$answer_l} ) {
1528 # check outgoing msg to xml validity
1529 my $answer_hash = &check_outgoing_xml_validity($answer, $session_id);
1530 if( not defined $answer_hash ) { next; }
1532 $answer_header = @{$answer_hash->{'header'}}[0];
1533 @answer_target_l = @{$answer_hash->{'target'}};
1534 $answer_source = @{$answer_hash->{'source'}}[0];
1536 # deliver msg to all targets
1537 foreach my $answer_target ( @answer_target_l ) {
1539 # targets of msg are all gosa-si-clients in known_clients_db
1540 if( $answer_target eq "*" ) {
1541 # answer is for all clients
1542 my $sql_statement= "SELECT * FROM known_clients";
1543 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1544 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1545 my $host_name = $hit->{hostname};
1546 my $host_key = $hit->{hostkey};
1547 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1548 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1549 }
1550 }
1552 # targets of msg are all gosa-si-server in known_server_db
1553 elsif( $answer_target eq "KNOWN_SERVER" ) {
1554 # answer is for all server in known_server
1555 my $sql_statement= "SELECT * FROM $known_server_tn";
1556 my $query_res = $known_server_db->select_dbentry( $sql_statement );
1557 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1558 my $host_name = $hit->{hostname};
1559 my $host_key = $hit->{hostkey};
1560 $answer =~ s/<target>\S+<\/target>/<target>$host_name<\/target>/g;
1561 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1562 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1563 }
1564 }
1566 # target of msg is GOsa
1567 elsif( $answer_target eq "GOSA" ) {
1568 my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1569 my $add_on = "";
1570 if( defined $session_id ) {
1571 $add_on = ".session_id=$session_id";
1572 }
1573 # answer is for GOSA and has to returned to connected client
1574 my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1575 $client_answer = $gosa_answer.$add_on;
1576 }
1578 # target of msg is job queue at this host
1579 elsif( $answer_target eq "JOBDB") {
1580 $answer =~ /<header>(\S+)<\/header>/;
1581 my $header;
1582 if( defined $1 ) { $header = $1; }
1583 my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1584 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1585 }
1587 # Target of msg is a mac address
1588 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 ) {
1589 daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients and foreign_clients", 5);
1591 # Looking for macaddress in known_clients
1592 my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1593 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1594 my $found_ip_flag = 0;
1595 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1596 my $host_name = $hit->{hostname};
1597 my $host_key = $hit->{hostkey};
1598 $answer =~ s/$answer_target/$host_name/g;
1599 daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1600 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1601 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1602 $found_ip_flag++ ;
1603 }
1605 # Looking for macaddress in foreign_clients
1606 if ($found_ip_flag == 0) {
1607 my $sql = "SELECT * FROM $foreign_clients_tn WHERE macaddress LIKE '$answer_target'";
1608 my $res = $foreign_clients_db->select_dbentry($sql);
1609 while( my ($hit_num, $hit) = each %{ $res } ) {
1610 my $host_name = $hit->{hostname};
1611 my $reg_server = $hit->{regserver};
1612 daemon_log("$session_id INFO: found host '$host_name' with mac '$answer_target', registered at '$reg_server'", 5);
1614 # Fetch key for reg_server
1615 my $reg_server_key;
1616 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$reg_server'";
1617 my $res = $known_server_db->select_dbentry($sql);
1618 if (exists $res->{1}) {
1619 $reg_server_key = $res->{1}->{'hostkey'};
1620 } else {
1621 daemon_log("$session_id ERROR: cannot find hostkey for '$host_name' in '$known_server_tn'", 1);
1622 daemon_log("$session_id ERROR: unable to forward answer to correct registration server, processing is aborted!", 1);
1623 $reg_server_key = undef;
1624 }
1626 # Send answer to server where client is registered
1627 if (defined $reg_server_key) {
1628 $answer =~ s/$answer_target/$host_name/g;
1629 my $error = &send_msg_to_target($answer, $reg_server, $reg_server_key, $answer_header, $session_id);
1630 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1631 $found_ip_flag++ ;
1632 }
1633 }
1634 }
1636 # No mac to ip matching found
1637 if( $found_ip_flag == 0) {
1638 daemon_log("$session_id WARNING: no host found in known_clients or foreign_clients with mac address '$answer_target'", 3);
1639 &reactivate_job_with_delay($session_id, $answer_target, $answer_header, 30);
1640 }
1642 # Answer is for one specific host
1643 } else {
1644 # get encrypt_key
1645 my $encrypt_key = &get_encrypt_key($answer_target);
1646 if( not defined $encrypt_key ) {
1647 # unknown target
1648 daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1649 next;
1650 }
1651 my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1652 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1653 }
1654 }
1655 }
1656 }
1658 my $filter = POE::Filter::Reference->new();
1659 my %result = (
1660 status => "seems ok to me",
1661 answer => $client_answer,
1662 );
1664 my $output = $filter->put( [ \%result ] );
1665 print @$output;
1668 }
1670 sub session_start {
1671 my ($kernel) = $_[KERNEL];
1672 $global_kernel = $kernel;
1673 $kernel->yield('register_at_foreign_servers');
1674 $kernel->yield('create_fai_server_db', $fai_server_tn );
1675 $kernel->yield('create_fai_release_db', $fai_release_tn );
1676 $kernel->yield('watch_for_next_tasks');
1677 $kernel->sig(USR1 => "sig_handler");
1678 $kernel->sig(USR2 => "recreate_packages_db");
1679 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1680 $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay);
1681 $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay);
1682 $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1683 $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1684 $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1685 $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1687 # Start opsi check
1688 if ($opsi_enabled eq "true") {
1689 $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay);
1690 }
1692 }
1695 sub watch_for_done_jobs {
1696 #CHECK: $heap for what?
1697 my ($kernel,$heap) = @_[KERNEL, HEAP];
1699 my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((status='done') AND (modified='0'))";
1700 my $res = $job_db->select_dbentry( $sql_statement );
1702 while( my ($id, $hit) = each %{$res} ) {
1703 my $jobdb_id = $hit->{id};
1704 my $sql_statement = "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1705 my $res = $job_db->del_dbentry($sql_statement);
1706 }
1708 $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1709 }
1712 sub watch_for_opsi_jobs {
1713 my ($kernel) = $_[KERNEL];
1715 # 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
1716 # opsi install job is to parse the xml message. There is still the correct header.
1717 my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((xmlmessage LIKE '%opsi_install_client</header>%') AND (status='processing') AND (siserver='localhost'))";
1718 my $res = $job_db->select_dbentry( $sql_statement );
1720 # Ask OPSI for an update of the running jobs
1721 while (my ($id, $hit) = each %$res ) {
1722 # Determine current parameters of the job
1723 my $hostId = $hit->{'plainname'};
1724 my $macaddress = $hit->{'macaddress'};
1725 my $progress = $hit->{'progress'};
1727 my $result= {};
1729 # For hosts, only return the products that are or get installed
1730 my $callobj;
1731 $callobj = {
1732 method => 'getProductStates_hash',
1733 params => [ $hostId ],
1734 id => 1,
1735 };
1737 my $hres = $opsi_client->call($opsi_url, $callobj);
1738 #my ($hres_err, $hres_err_string) = &check_opsi_res($hres);
1739 if (not &check_opsi_res($hres)) {
1740 my $htmp= $hres->result->{$hostId};
1742 # Check state != not_installed or action == setup -> load and add
1743 my $products= 0;
1744 my $installed= 0;
1745 my $installing = 0;
1746 my $error= 0;
1747 my @installed_list;
1748 my @error_list;
1749 my $act_status = "none";
1750 foreach my $product (@{$htmp}){
1752 if ($product->{'installationStatus'} ne "not_installed" or
1753 $product->{'actionRequest'} eq "setup"){
1755 # Increase number of products for this host
1756 $products++;
1758 if ($product->{'installationStatus'} eq "failed"){
1759 $result->{$product->{'productId'}}= "error";
1760 unshift(@error_list, $product->{'productId'});
1761 $error++;
1762 }
1763 if ($product->{'installationStatus'} eq "installed" && $product->{'actionRequest'} eq "none"){
1764 $result->{$product->{'productId'}}= "installed";
1765 unshift(@installed_list, $product->{'productId'});
1766 $installed++;
1767 }
1768 if ($product->{'installationStatus'} eq "installing"){
1769 $result->{$product->{'productId'}}= "installing";
1770 $installing++;
1771 $act_status = "installing - ".$product->{'productId'};
1772 }
1773 }
1774 }
1776 # Estimate "rough" progress, avoid division by zero
1777 if ($products == 0) {
1778 $result->{'progress'}= 0;
1779 } else {
1780 $result->{'progress'}= int($installed * 100 / $products);
1781 }
1783 # Set updates in job queue
1784 if ((not $error) && (not $installing) && ($installed)) {
1785 $act_status = "installed - ".join(", ", @installed_list);
1786 }
1787 if ($error) {
1788 $act_status = "error - ".join(", ", @error_list);
1789 }
1790 if ($progress ne $result->{'progress'} ) {
1791 # Updating progress and result
1792 my $update_statement = "UPDATE $job_queue_tn SET modified='1', progress='".$result->{'progress'}."', result='$act_status' WHERE macaddress LIKE '$macaddress' AND siserver='localhost'";
1793 my $update_res = $job_db->update_dbentry($update_statement);
1794 }
1795 if ($progress eq 100) {
1796 # Updateing status
1797 my $done_statement = "UPDATE $job_queue_tn SET modified='1', ";
1798 if ($error) {
1799 $done_statement .= "status='error'";
1800 } else {
1801 $done_statement .= "status='done'";
1802 }
1803 $done_statement .= " WHERE macaddress LIKE '$macaddress' AND siserver='localhost'";
1804 my $done_res = $job_db->update_dbentry($done_statement);
1805 }
1808 }
1809 }
1811 $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay);
1812 }
1815 # If a job got an update or was modified anyway, send to all other si-server an update message of this jobs.
1816 sub watch_for_modified_jobs {
1817 my ($kernel,$heap) = @_[KERNEL, HEAP];
1819 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE (modified='1')";
1820 my $res = $job_db->select_dbentry( $sql_statement );
1822 # if db contains no jobs which should be update, do nothing
1823 if (keys %$res != 0) {
1825 if ($job_synchronization eq "true") {
1826 # make out of the db result a gosa-si message
1827 my $update_msg = &db_res2si_msg ($res, "foreign_job_updates", "KNOWN_SERVER", "MY_LOCAL_ADDRESS");
1829 # update all other SI-server
1830 &inform_all_other_si_server($update_msg);
1831 }
1833 # set jobs all jobs to modified = 0, wait until the next modification for updates of other si-server
1834 $sql_statement = "UPDATE $job_queue_tn SET modified='0' ";
1835 $res = $job_db->update_dbentry($sql_statement);
1836 }
1838 $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay);
1839 }
1842 sub watch_for_new_jobs {
1843 if($watch_for_new_jobs_in_progress == 0) {
1844 $watch_for_new_jobs_in_progress = 1;
1845 my ($kernel,$heap) = @_[KERNEL, HEAP];
1847 # check gosa job queue for jobs with executable timestamp
1848 my $timestamp = &get_time();
1849 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE siserver='localhost' AND status='waiting' AND (CAST(timestamp AS UNSIGNED)) < $timestamp ORDER BY timestamp";
1850 my $res = $job_db->exec_statement( $sql_statement );
1852 # Merge all new jobs that would do the same actions
1853 my @drops;
1854 my $hits;
1855 foreach my $hit (reverse @{$res} ) {
1856 my $macaddress= lc @{$hit}[8];
1857 my $headertag= @{$hit}[5];
1858 if(
1859 defined($hits->{$macaddress}) &&
1860 defined($hits->{$macaddress}->{$headertag}) &&
1861 defined($hits->{$macaddress}->{$headertag}[0])
1862 ) {
1863 push @drops, "DELETE FROM $job_queue_tn WHERE id = $hits->{$macaddress}->{$headertag}[0]";
1864 }
1865 $hits->{$macaddress}->{$headertag}= $hit;
1866 }
1868 # Delete new jobs with a matching job in state 'processing'
1869 foreach my $macaddress (keys %{$hits}) {
1870 foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1871 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1872 if(defined($jobdb_id)) {
1873 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND headertag='$jobdb_headertag' AND status='processing'";
1874 my $res = $job_db->exec_statement( $sql_statement );
1875 foreach my $hit (@{$res}) {
1876 push @drops, "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1877 }
1878 } else {
1879 daemon_log("J ERROR: Job without id exists for macaddress $macaddress!", 1);
1880 }
1881 }
1882 }
1884 # Commit deletion
1885 $job_db->exec_statementlist(\@drops);
1887 # Look for new jobs that could be executed
1888 foreach my $macaddress (keys %{$hits}) {
1890 # Look if there is an executing job
1891 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='processing'";
1892 my $res = $job_db->exec_statement( $sql_statement );
1894 # Skip new jobs for host if there is a processing job
1895 if(defined($res) and defined @{$res}[0]) {
1896 # Prevent race condition if there is a trigger_activate job waiting and a goto-activation job processing
1897 my $row = @{$res}[0] if (ref $res eq 'ARRAY');
1898 if(@{$row}[5] eq 'trigger_action_reinstall') {
1899 my $sql_statement_2 = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='waiting' AND headertag = 'trigger_activate_new'";
1900 my $res_2 = $job_db->exec_statement( $sql_statement_2 );
1901 if(defined($res_2) and defined @{$res_2}[0]) {
1902 # Set status from goto-activation to 'waiting' and update timestamp
1903 $job_db->exec_statement("UPDATE $job_queue_tn SET status='waiting', timestamp='".&calc_timestamp(&get_time(), 'plus', 30)."' WHERE macaddress LIKE '$macaddress' AND headertag = 'trigger_action_reinstall'");
1904 }
1905 }
1906 next;
1907 }
1909 foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1910 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1911 if(defined($jobdb_id)) {
1912 my $job_msg = @{$hits->{$macaddress}->{$jobdb_headertag}}[7];
1914 daemon_log("J DEBUG: its time to execute $job_msg", 7);
1915 my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1916 my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1918 # expect macaddress is unique!!!!!!
1919 my $target = $res_hash->{1}->{hostname};
1921 # change header
1922 $job_msg =~ s/<header>job_/<header>gosa_/;
1924 # add sqlite_id
1925 $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1927 $job_msg =~ /<header>(\S+)<\/header>/;
1928 my $header = $1 ;
1929 my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");
1931 # update status in job queue to ...
1932 # ... 'processing', for jobs: 'reinstall', 'update'
1933 if (($header =~ /gosa_trigger_action_reinstall/)
1934 || ($header =~ /gosa_trigger_activate_new/)
1935 || ($header =~ /gosa_trigger_action_update/)) {
1936 my $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
1937 my $dbres = $job_db->update_dbentry($sql_statement);
1938 }
1940 # ... 'done', for all other jobs, they are no longer needed in the jobqueue
1941 else {
1942 my $sql_statement = "UPDATE $job_queue_tn SET status='done' WHERE id=$jobdb_id";
1943 my $dbres = $job_db->update_dbentry($sql_statement);
1944 }
1947 # We don't want parallel processing
1948 last;
1949 }
1950 }
1951 }
1953 $watch_for_new_jobs_in_progress = 0;
1954 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1955 }
1956 }
1959 sub watch_for_new_messages {
1960 my ($kernel,$heap) = @_[KERNEL, HEAP];
1961 my @coll_user_msg; # collection list of outgoing messages
1963 # check messaging_db for new incoming messages with executable timestamp
1964 my $timestamp = &get_time();
1965 my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS UNSIGNED))<$timestamp AND flag='n' AND direction='in' )";
1966 my $res = $messaging_db->exec_statement( $sql_statement );
1967 foreach my $hit (@{$res}) {
1969 # create outgoing messages
1970 my $message_to = @{$hit}[3];
1971 # translate message_to to plain login name
1972 my @message_to_l = split(/,/, $message_to);
1973 my %receiver_h;
1974 foreach my $receiver (@message_to_l) {
1975 if ($receiver =~ /^u_([\s\S]*)$/) {
1976 $receiver_h{$1} = 0;
1977 } elsif ($receiver =~ /^g_([\s\S]*)$/) {
1978 my $group_name = $1;
1979 # fetch all group members from ldap and add them to receiver hash
1980 my $ldap_handle = &get_ldap_handle();
1981 if (defined $ldap_handle) {
1982 my $mesg = $ldap_handle->search(
1983 base => $ldap_base,
1984 scope => 'sub',
1985 attrs => ['memberUid'],
1986 filter => "cn=$group_name",
1987 );
1988 if ($mesg->count) {
1989 my @entries = $mesg->entries;
1990 foreach my $entry (@entries) {
1991 my @receivers= $entry->get_value("memberUid");
1992 foreach my $receiver (@receivers) {
1993 $receiver_h{$receiver} = 0;
1994 }
1995 }
1996 }
1997 # translating errors ?
1998 if ($mesg->code) {
1999 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: $mesg->error", 1);
2000 }
2001 &release_ldap_handle($ldap_handle);
2002 # ldap handle error ?
2003 } else {
2004 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: no ldap handle available", 1);
2005 }
2006 } else {
2007 my $sbjct = &encode_base64(@{$hit}[1]);
2008 my $msg = &encode_base64(@{$hit}[7]);
2009 &daemon_log("M WARNING: unknown receiver '$receiver' for a user-message '$sbjct - $msg'", 3);
2010 }
2011 }
2012 my @receiver_l = keys(%receiver_h);
2014 my $message_id = @{$hit}[0];
2016 #add each outgoing msg to messaging_db
2017 my $receiver;
2018 foreach $receiver (@receiver_l) {
2019 my $sql_statement = "INSERT INTO $messaging_tn (id, subject, message_from, message_to, flag, direction, delivery_time, message, timestamp) ".
2020 "VALUES ('".
2021 $message_id."', '". # id
2022 @{$hit}[1]."', '". # subject
2023 @{$hit}[2]."', '". # message_from
2024 $receiver."', '". # message_to
2025 "none"."', '". # flag
2026 "out"."', '". # direction
2027 @{$hit}[6]."', '". # delivery_time
2028 @{$hit}[7]."', '". # message
2029 $timestamp."'". # timestamp
2030 ")";
2031 &daemon_log("M DEBUG: $sql_statement", 1);
2032 my $res = $messaging_db->exec_statement($sql_statement);
2033 &daemon_log("M INFO: message '".@{$hit}[0]."' is prepared for delivery to receiver '$receiver'", 5);
2034 }
2036 # set incoming message to flag d=deliverd
2037 $sql_statement = "UPDATE $messaging_tn SET flag='p' WHERE id='$message_id'";
2038 &daemon_log("M DEBUG: $sql_statement", 7);
2039 $res = $messaging_db->update_dbentry($sql_statement);
2040 &daemon_log("M INFO: message '$message_id' is set to flag 'p' (processed)", 5);
2041 }
2043 $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
2044 return;
2045 }
2047 sub watch_for_delivery_messages {
2048 my ($kernel, $heap) = @_[KERNEL, HEAP];
2050 # select outgoing messages
2051 my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( flag='p' AND direction='out' )";
2052 #&daemon_log("0 DEBUG: $sql", 7);
2053 my $res = $messaging_db->exec_statement( $sql_statement );
2055 # build out msg for each usr
2056 foreach my $hit (@{$res}) {
2057 my $receiver = @{$hit}[3];
2058 my $msg_id = @{$hit}[0];
2059 my $subject = @{$hit}[1];
2060 my $message = @{$hit}[7];
2062 # resolve usr -> host where usr is logged in
2063 my $sql = "SELECT * FROM $login_users_tn WHERE (user='$receiver')";
2064 #&daemon_log("0 DEBUG: $sql", 7);
2065 my $res = $login_users_db->exec_statement($sql);
2067 # receiver is logged in nowhere
2068 if (not ref(@$res[0]) eq "ARRAY") { next; }
2070 # receiver ist logged in at a client registered at local server
2071 my $send_succeed = 0;
2072 foreach my $hit (@$res) {
2073 my $receiver_host = @$hit[0];
2074 my $delivered2host = 0;
2075 &daemon_log("M DEBUG: user '$receiver' is logged in at host '$receiver_host'", 7);
2077 # Looking for host in know_clients_db
2078 my $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$receiver_host')";
2079 my $res = $known_clients_db->exec_statement($sql);
2081 # Host is known in known_clients_db
2082 if (ref(@$res[0]) eq "ARRAY") {
2083 my $receiver_key = @{@{$res}[0]}[2];
2084 my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
2085 my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data );
2086 my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0);
2087 if ($error == 0 ) {
2088 $send_succeed++ ;
2089 $delivered2host++ ;
2090 &daemon_log("M DEBUG: send message for user '$receiver' to host '$receiver_host'", 7);
2091 } else {
2092 &daemon_log("M DEBUG: cannot send message for user '$receiver' to host '$receiver_host'", 7);
2093 }
2094 }
2096 # Message already send, do not need to do anything more, otherwise ...
2097 if ($delivered2host) { next;}
2099 # ...looking for host in foreign_clients_db
2100 $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$receiver_host')";
2101 $res = $foreign_clients_db->exec_statement($sql);
2103 # Host is known in foreign_clients_db
2104 if (ref(@$res[0]) eq "ARRAY") {
2105 my $registration_server = @{@{$res}[0]}[2];
2107 # Fetch encryption key for registration server
2108 my $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$registration_server')";
2109 my $res = $known_server_db->exec_statement($sql);
2110 if (ref(@$res[0]) eq "ARRAY") {
2111 my $registration_server_key = @{@{$res}[0]}[3];
2112 my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
2113 my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data );
2114 my $error = &send_msg_to_target($out_msg, $registration_server, $registration_server_key, "usr_msg", 0);
2115 if ($error == 0 ) {
2116 $send_succeed++ ;
2117 $delivered2host++ ;
2118 &daemon_log("M DEBUG: send message for user '$receiver' to server '$registration_server'", 7);
2119 } else {
2120 &daemon_log("M ERROR: cannot send message for user '$receiver' to server '$registration_server'", 1);
2121 }
2123 } else {
2124 &daemon_log("M ERROR: host '$receiver_host' is reported to be ".
2125 "registrated at server '$registration_server', ".
2126 "but no data available in known_server_db ", 1);
2127 }
2128 }
2130 if (not $delivered2host) {
2131 &daemon_log("M ERROR: unable to send user message to host '$receiver_host'", 1);
2132 }
2133 }
2135 if ($send_succeed) {
2136 # set outgoing msg at db to deliverd
2137 my $sql = "UPDATE $messaging_tn SET flag='d' WHERE (id='$msg_id' AND direction='out' AND message_to='$receiver')";
2138 my $res = $messaging_db->exec_statement($sql);
2139 &daemon_log("M INFO: send message for user '$receiver' to logged in hosts", 5);
2140 } else {
2141 &daemon_log("M WARNING: failed to deliver message for user '$receiver'", 3);
2142 }
2143 }
2145 $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
2146 return;
2147 }
2150 sub watch_for_done_messages {
2151 my ($kernel,$heap) = @_[KERNEL, HEAP];
2153 my $sql = "SELECT * FROM $messaging_tn WHERE (flag='p' AND direction='in')";
2154 #&daemon_log("0 DEBUG: $sql", 7);
2155 my $res = $messaging_db->exec_statement($sql);
2157 foreach my $hit (@{$res}) {
2158 my $msg_id = @{$hit}[0];
2160 my $sql = "SELECT * FROM $messaging_tn WHERE (id='$msg_id' AND direction='out' AND (NOT flag='s'))";
2161 #&daemon_log("0 DEBUG: $sql", 7);
2162 my $res = $messaging_db->exec_statement($sql);
2164 # not all usr msgs have been seen till now
2165 if ( ref(@$res[0]) eq "ARRAY") { next; }
2167 $sql = "DELETE FROM $messaging_tn WHERE (id='$msg_id')";
2168 #&daemon_log("0 DEBUG: $sql", 7);
2169 $res = $messaging_db->exec_statement($sql);
2171 }
2173 $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
2174 return;
2175 }
2178 sub watch_for_old_known_clients {
2179 my ($kernel,$heap) = @_[KERNEL, HEAP];
2181 my $sql_statement = "SELECT * FROM $known_clients_tn";
2182 my $res = $known_clients_db->select_dbentry( $sql_statement );
2184 my $cur_time = int(&get_time());
2186 while ( my ($hit_num, $hit) = each %$res) {
2187 my $expired_timestamp = int($hit->{'timestamp'});
2188 $expired_timestamp =~ /(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/;
2189 my $dt = DateTime->new( year => $1,
2190 month => $2,
2191 day => $3,
2192 hour => $4,
2193 minute => $5,
2194 second => $6,
2195 );
2197 $dt->add( seconds => 2 * int($hit->{'keylifetime'}) );
2198 $expired_timestamp = $dt->ymd('').$dt->hms('');
2199 if ($cur_time > $expired_timestamp) {
2200 my $hostname = $hit->{'hostname'};
2201 my $del_sql = "DELETE FROM $known_clients_tn WHERE hostname='$hostname'";
2202 my $del_res = $known_clients_db->exec_statement($del_sql);
2204 &main::daemon_log("0 INFO: timestamp '".$hit->{'timestamp'}."' of client '$hostname' is expired('$expired_timestamp'), client will be deleted from known_clients_db", 5);
2205 }
2207 }
2209 $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
2210 }
2213 sub watch_for_next_tasks {
2214 my ($kernel,$heap) = @_[KERNEL, HEAP];
2216 my $sql = "SELECT * FROM $incoming_tn";
2217 my $res = $incoming_db->select_dbentry($sql);
2219 while ( my ($hit_num, $hit) = each %$res) {
2220 my $headertag = $hit->{'headertag'};
2221 if ($headertag =~ /^answer_(\d+)/) {
2222 # do not start processing, this message is for a still running POE::Wheel
2223 next;
2224 }
2225 my $message_id = $hit->{'id'};
2226 my $session_id = $hit->{'sessionid'};
2227 &daemon_log("$session_id DEBUG: start processing for message with incoming id: '$message_id'", 7);
2229 $kernel->yield('next_task', $hit);
2231 my $sql = "DELETE FROM $incoming_tn WHERE id=$message_id";
2232 my $res = $incoming_db->exec_statement($sql);
2233 }
2235 $kernel->delay_set('watch_for_next_tasks', 1);
2236 }
2239 sub get_ldap_handle {
2240 my ($session_id) = @_;
2241 my $heap;
2243 if (not defined $session_id ) { $session_id = 0 };
2244 if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
2246 my ($package, $file, $row, $subroutine, $hasArgs, $wantArray, $evalText, $isRequire) = caller(1);
2247 my $caller_text = "subroutine $subroutine";
2248 if ($subroutine eq "(eval)") {
2249 $caller_text = "eval block within file '$file' for '$evalText'";
2250 }
2251 daemon_log("$session_id INFO: new ldap handle for '$caller_text' required!", 7);
2253 get_handle:
2254 my $ldap_handle = Net::LDAP->new( $ldap_uri );
2255 if (not ref $ldap_handle) {
2256 daemon_log("$session_id ERROR: Connection to LDAP URI '$ldap_uri' failed! Retrying!", 1);
2257 usleep(100000);
2258 goto get_handle;
2259 } else {
2260 daemon_log("$session_id DEBUG: Connection to LDAP URI '$ldap_uri' established.", 6);
2261 }
2263 $ldap_handle->bind($ldap_admin_dn, password => $ldap_admin_password) or &daemon_log("$session_id ERROR: Could not bind as '$ldap_admin_dn' to LDAP URI '$ldap_uri'!", 1);
2264 return $ldap_handle;
2265 }
2268 sub release_ldap_handle {
2269 my ($ldap_handle) = @_ ;
2270 if(ref $ldap_handle) {
2271 $ldap_handle->disconnect();
2272 }
2273 &main::daemon_log("0 DEBUG: Released a ldap handle!", 6);
2274 return;
2275 }
2278 sub change_fai_state {
2279 my ($st, $targets, $session_id) = @_;
2280 $session_id = 0 if not defined $session_id;
2281 # Set FAI state to localboot
2282 my %mapActions= (
2283 reboot => '',
2284 update => 'softupdate',
2285 localboot => 'localboot',
2286 reinstall => 'install',
2287 rescan => '',
2288 wake => '',
2289 memcheck => 'memcheck',
2290 sysinfo => 'sysinfo',
2291 install => 'install',
2292 );
2294 # Return if this is unknown
2295 if (!exists $mapActions{ $st }){
2296 daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1);
2297 return;
2298 }
2300 my $state= $mapActions{ $st };
2302 # Build search filter for hosts
2303 my $search= "(&(objectClass=GOhard)";
2304 foreach (@{$targets}){
2305 $search.= "(macAddress=$_)";
2306 }
2307 $search.= ")";
2309 # If there's any host inside of the search string, procress them
2310 if (!($search =~ /macAddress/)){
2311 daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);
2312 return;
2313 }
2315 my $ldap_handle = &get_ldap_handle($session_id);
2316 # Perform search for Unit Tag
2317 my $mesg = $ldap_handle->search(
2318 base => $ldap_base,
2319 scope => 'sub',
2320 attrs => ['dn', 'FAIstate', 'objectClass'],
2321 filter => "$search"
2322 );
2324 if ($mesg->count) {
2325 my @entries = $mesg->entries;
2326 if (0 == @entries) {
2327 daemon_log("$session_id ERROR: ldap search failed: ldap_base=$ldap_base, filter=$search", 1);
2328 }
2330 foreach my $entry (@entries) {
2331 # Only modify entry if it is not set to '$state'
2332 if ($entry->get_value("FAIstate") ne "$state"){
2333 daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
2334 my $result;
2335 my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
2336 if (exists $tmp{'FAIobject'}){
2337 if ($state eq ''){
2338 $result= $ldap_handle->modify($entry->dn, changes => [ delete => [ FAIstate => [] ] ]);
2339 } else {
2340 $result= $ldap_handle->modify($entry->dn, changes => [ replace => [ FAIstate => $state ] ]);
2341 }
2342 } elsif ($state ne ''){
2343 $result= $ldap_handle->modify($entry->dn, changes => [ add => [ objectClass => 'FAIobject' ], add => [ FAIstate => $state ] ]);
2344 }
2346 # Errors?
2347 if ($result->code){
2348 daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2349 }
2350 } else {
2351 daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 7);
2352 }
2353 }
2354 } else {
2355 daemon_log("$session_id ERROR: LDAP search failed: ldap_base=$ldap_base, filter=$search", 1);
2356 }
2357 &release_ldap_handle($ldap_handle);
2359 return;
2360 }
2363 sub change_goto_state {
2364 my ($st, $targets, $session_id) = @_;
2365 $session_id = 0 if not defined $session_id;
2367 # Switch on or off?
2368 my $state= $st eq 'active' ? 'active': 'locked';
2370 my $ldap_handle = &get_ldap_handle($session_id);
2371 if( defined($ldap_handle) ) {
2373 # Build search filter for hosts
2374 my $search= "(&(objectClass=GOhard)";
2375 foreach (@{$targets}){
2376 $search.= "(macAddress=$_)";
2377 }
2378 $search.= ")";
2380 # If there's any host inside of the search string, procress them
2381 if (!($search =~ /macAddress/)){
2382 &release_ldap_handle($ldap_handle);
2383 return;
2384 }
2386 # Perform search for Unit Tag
2387 my $mesg = $ldap_handle->search(
2388 base => $ldap_base,
2389 scope => 'sub',
2390 attrs => ['dn', 'gotoMode'],
2391 filter => "$search"
2392 );
2394 if ($mesg->count) {
2395 my @entries = $mesg->entries;
2396 foreach my $entry (@entries) {
2398 # Only modify entry if it is not set to '$state'
2399 if ($entry->get_value("gotoMode") ne $state){
2401 daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
2402 my $result;
2403 $result= $ldap_handle->modify($entry->dn, changes => [replace => [ gotoMode => $state ] ]);
2405 # Errors?
2406 if ($result->code){
2407 &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2408 }
2410 }
2411 }
2412 } else {
2413 daemon_log("$session_id ERROR: LDAP search failed in function change_goto_state: ldap_base=$ldap_base, filter=$search", 1);
2414 }
2416 }
2417 &release_ldap_handle($ldap_handle);
2418 return;
2419 }
2422 sub run_recreate_packages_db {
2423 my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2424 my $session_id = $session->ID;
2425 &main::daemon_log("$session_id INFO: Recreating FAI Packages DB ('$fai_release_tn', '$fai_server_tn', '$packages_list_tn')", 5);
2426 $kernel->yield('create_fai_release_db', $fai_release_tn);
2427 $kernel->yield('create_fai_server_db', $fai_server_tn);
2428 return;
2429 }
2432 sub run_create_fai_server_db {
2433 my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2434 my $session_id = $session->ID;
2435 my $task = POE::Wheel::Run->new(
2436 Program => sub { &create_fai_server_db($table_name,$kernel, undef, $session_id) },
2437 StdoutEvent => "session_run_result",
2438 StderrEvent => "session_run_debug",
2439 CloseEvent => "session_run_done",
2440 );
2442 $heap->{task}->{ $task->ID } = $task;
2443 return;
2444 }
2447 sub create_fai_server_db {
2448 my ($table_name, $kernel, $dont_create_packages_list, $session_id) = @_;
2449 my $result;
2451 if (not defined $session_id) { $session_id = 0; }
2452 my $ldap_handle = &get_ldap_handle($session_id);
2453 if(defined($ldap_handle)) {
2454 daemon_log("$session_id INFO: create_fai_server_db: start", 5);
2455 my $mesg= $ldap_handle->search(
2456 base => $ldap_base,
2457 scope => 'sub',
2458 attrs => ['FAIrepository', 'gosaUnitTag'],
2459 filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
2460 );
2461 if($mesg->{'resultCode'} == 0 &&
2462 $mesg->count != 0) {
2463 foreach my $entry (@{$mesg->{entries}}) {
2464 if($entry->exists('FAIrepository')) {
2465 # Add an entry for each Repository configured for server
2466 foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
2467 my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
2468 my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
2469 $result= $fai_server_db->add_dbentry( {
2470 table => $table_name,
2471 primkey => ['server', 'fai_release', 'tag'],
2472 server => $tmp_url,
2473 fai_release => $tmp_release,
2474 sections => $tmp_sections,
2475 tag => (length($tmp_tag)>0)?$tmp_tag:"",
2476 } );
2477 }
2478 }
2479 }
2480 }
2481 daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
2482 &release_ldap_handle($ldap_handle);
2484 # TODO: Find a way to post the 'create_packages_list_db' event
2485 if(not defined($dont_create_packages_list)) {
2486 &create_packages_list_db(undef, $session_id);
2487 }
2488 }
2490 return $result;
2491 }
2494 sub run_create_fai_release_db {
2495 my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2496 my $session_id = $session->ID;
2497 my $task = POE::Wheel::Run->new(
2498 Program => sub { &create_fai_release_db($table_name, $session_id) },
2499 StdoutEvent => "session_run_result",
2500 StderrEvent => "session_run_debug",
2501 CloseEvent => "session_run_done",
2502 );
2504 $heap->{task}->{ $task->ID } = $task;
2505 return;
2506 }
2509 sub create_fai_release_db {
2510 my ($table_name, $session_id) = @_;
2511 my $result;
2513 # used for logging
2514 if (not defined $session_id) { $session_id = 0; }
2516 my $ldap_handle = &get_ldap_handle($session_id);
2517 if(defined($ldap_handle)) {
2518 daemon_log("$session_id INFO: create_fai_release_db: start",5);
2519 my $mesg= $ldap_handle->search(
2520 base => $ldap_base,
2521 scope => 'sub',
2522 attrs => [],
2523 filter => "(&(objectClass=organizationalUnit)(ou=fai))",
2524 );
2525 if(($mesg->code == 0) && ($mesg->count != 0))
2526 {
2527 daemon_log("$session_id DEBUG: create_fai_release_db: count " . $mesg->count,8);
2529 # Walk through all possible FAI container ou's
2530 my @sql_list;
2531 my $timestamp= &get_time();
2532 foreach my $ou (@{$mesg->{entries}}) {
2533 my $tmp_classes= resolve_fai_classes($ou->dn, $ldap_handle, $session_id);
2534 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
2535 my @tmp_array=get_fai_release_entries($tmp_classes);
2536 if(@tmp_array) {
2537 foreach my $entry (@tmp_array) {
2538 if(defined($entry) && ref($entry) eq 'HASH') {
2539 my $sql=
2540 "INSERT INTO $table_name "
2541 ."(timestamp, fai_release, class, type, state) VALUES ("
2542 .$timestamp.","
2543 ."'".$entry->{'release'}."',"
2544 ."'".$entry->{'class'}."',"
2545 ."'".$entry->{'type'}."',"
2546 ."'".$entry->{'state'}."')";
2547 push @sql_list, $sql;
2548 }
2549 }
2550 }
2551 }
2552 }
2554 daemon_log("$session_id DEBUG: create_fai_release_db: Inserting ".scalar @sql_list." entries to DB",8);
2555 &release_ldap_handle($ldap_handle);
2556 if(@sql_list) {
2557 unshift @sql_list, "VACUUM";
2558 unshift @sql_list, "DELETE FROM $table_name";
2559 $fai_release_db->exec_statementlist(\@sql_list);
2560 }
2561 daemon_log("$session_id DEBUG: create_fai_release_db: Done with inserting",7);
2562 } else {
2563 daemon_log("$session_id INFO: create_fai_release_db: error: " . $mesg->code, 5);
2564 }
2565 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
2566 }
2567 return $result;
2568 }
2570 sub get_fai_types {
2571 my $tmp_classes = shift || return undef;
2572 my @result;
2574 foreach my $type(keys %{$tmp_classes}) {
2575 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
2576 my $entry = {
2577 type => $type,
2578 state => $tmp_classes->{$type}[0],
2579 };
2580 push @result, $entry;
2581 }
2582 }
2584 return @result;
2585 }
2587 sub get_fai_state {
2588 my $result = "";
2589 my $tmp_classes = shift || return $result;
2591 foreach my $type(keys %{$tmp_classes}) {
2592 if(defined($tmp_classes->{$type}[0])) {
2593 $result = $tmp_classes->{$type}[0];
2595 # State is equal for all types in class
2596 last;
2597 }
2598 }
2600 return $result;
2601 }
2603 sub resolve_fai_classes {
2604 my ($fai_base, $ldap_handle, $session_id) = @_;
2605 if (not defined $session_id) { $session_id = 0; }
2606 my $result;
2607 my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
2608 my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
2609 my $fai_classes;
2611 daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base",7);
2612 my $mesg= $ldap_handle->search(
2613 base => $fai_base,
2614 scope => 'sub',
2615 attrs => ['cn','objectClass','FAIstate'],
2616 filter => $fai_filter,
2617 );
2618 daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries",7);
2620 if($mesg->{'resultCode'} == 0 &&
2621 $mesg->count != 0) {
2622 foreach my $entry (@{$mesg->{entries}}) {
2623 if($entry->exists('cn')) {
2624 my $tmp_dn= $entry->dn();
2625 $tmp_dn= substr( $tmp_dn, 0, length($tmp_dn)
2626 - length($fai_base) - 1 );
2628 # Skip classname and ou dn parts for class
2629 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?)$/;
2631 # Skip classes without releases
2632 if((!defined($tmp_release)) || length($tmp_release)==0) {
2633 next;
2634 }
2636 my $tmp_cn= $entry->get_value('cn');
2637 my $tmp_state= $entry->get_value('FAIstate');
2639 my $tmp_type;
2640 # Get FAI type
2641 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
2642 if(grep $_ eq $oclass, @possible_fai_classes) {
2643 $tmp_type= $oclass;
2644 last;
2645 }
2646 }
2648 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2649 # A Subrelease
2650 my @sub_releases = split(/,/, $tmp_release);
2652 # Walk through subreleases and build hash tree
2653 my $hash;
2654 while(my $tmp_sub_release = pop @sub_releases) {
2655 $hash .= "\{'$tmp_sub_release'\}->";
2656 }
2657 eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
2658 } else {
2659 # A branch, no subrelease
2660 push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
2661 }
2662 } elsif (!$entry->exists('cn')) {
2663 my $tmp_dn= $entry->dn();
2664 $tmp_dn= substr( $tmp_dn, 0, length($tmp_dn)
2665 - length($fai_base) - 1 );
2666 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?)$/;
2668 # Skip classes without releases
2669 if((!defined($tmp_release)) || length($tmp_release)==0) {
2670 next;
2671 }
2673 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2674 # A Subrelease
2675 my @sub_releases= split(/,/, $tmp_release);
2677 # Walk through subreleases and build hash tree
2678 my $hash;
2679 while(my $tmp_sub_release = pop @sub_releases) {
2680 $hash .= "\{'$tmp_sub_release'\}->";
2681 }
2682 # Remove the last two characters
2683 chop($hash);
2684 chop($hash);
2686 eval('$fai_classes->'.$hash.'= {}');
2687 } else {
2688 # A branch, no subrelease
2689 if(!exists($fai_classes->{$tmp_release})) {
2690 $fai_classes->{$tmp_release} = {};
2691 }
2692 }
2693 }
2694 }
2696 # The hash is complete, now we can honor the copy-on-write based missing entries
2697 foreach my $release (keys %$fai_classes) {
2698 $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
2699 }
2700 }
2701 return $result;
2702 }
2704 sub apply_fai_inheritance {
2705 my $fai_classes = shift || return {};
2706 my $tmp_classes;
2708 # Get the classes from the branch
2709 foreach my $class (keys %{$fai_classes}) {
2710 # Skip subreleases
2711 if($class =~ /^ou=.*$/) {
2712 next;
2713 } else {
2714 $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
2715 }
2716 }
2718 # Apply to each subrelease
2719 foreach my $subrelease (keys %{$fai_classes}) {
2720 if($subrelease =~ /ou=/) {
2721 foreach my $tmp_class (keys %{$tmp_classes}) {
2722 if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
2723 $fai_classes->{$subrelease}->{$tmp_class} =
2724 deep_copy($tmp_classes->{$tmp_class});
2725 } else {
2726 foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
2727 if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
2728 $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
2729 deep_copy($tmp_classes->{$tmp_class}->{$type});
2730 }
2731 }
2732 }
2733 }
2734 }
2735 }
2737 # Find subreleases in deeper levels
2738 foreach my $subrelease (keys %{$fai_classes}) {
2739 if($subrelease =~ /ou=/) {
2740 foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
2741 if($subsubrelease =~ /ou=/) {
2742 apply_fai_inheritance($fai_classes->{$subrelease});
2743 }
2744 }
2745 }
2746 }
2748 return $fai_classes;
2749 }
2751 sub get_fai_release_entries {
2752 my $tmp_classes = shift || return;
2753 my $parent = shift || "";
2754 my @result = shift || ();
2756 foreach my $entry (keys %{$tmp_classes}) {
2757 if(defined($entry)) {
2758 if($entry =~ /^ou=.*$/) {
2759 my $release_name = $entry;
2760 $release_name =~ s/ou=//g;
2761 if(length($parent)>0) {
2762 $release_name = $parent."/".$release_name;
2763 }
2764 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2765 foreach my $bufentry(@bufentries) {
2766 push @result, $bufentry;
2767 }
2768 } else {
2769 my @types = get_fai_types($tmp_classes->{$entry});
2770 foreach my $type (@types) {
2771 push @result,
2772 {
2773 'class' => $entry,
2774 'type' => $type->{'type'},
2775 'release' => $parent,
2776 'state' => $type->{'state'},
2777 };
2778 }
2779 }
2780 }
2781 }
2783 return @result;
2784 }
2786 sub deep_copy {
2787 my $this = shift;
2788 if (not ref $this) {
2789 $this;
2790 } elsif (ref $this eq "ARRAY") {
2791 [map deep_copy($_), @$this];
2792 } elsif (ref $this eq "HASH") {
2793 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2794 } else { die "what type is $_?" }
2795 }
2798 sub session_run_result {
2799 my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];
2800 $kernel->sig(CHLD => "child_reap");
2801 }
2803 sub session_run_debug {
2804 my $result = $_[ARG0];
2805 print STDERR "$result\n";
2806 }
2808 sub session_run_done {
2809 my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2810 delete $heap->{task}->{$task_id};
2811 if (exists $heap->{ldap_handle}->{$task_id}) {
2812 &release_ldap_handle($heap->{ldap_handle}->{$task_id});
2813 }
2814 delete $heap->{ldap_handle}->{$task_id};
2815 }
2818 sub create_sources_list {
2819 my $session_id = shift || 0;
2820 my $result="/tmp/gosa_si_tmp_sources_list";
2822 # Remove old file
2823 if(stat($result)) {
2824 unlink($result);
2825 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7);
2826 }
2828 my $fh;
2829 open($fh, ">$result");
2830 if (not defined $fh) {
2831 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7);
2832 return undef;
2833 }
2834 if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2835 my $ldap_handle = &get_ldap_handle($session_id);
2836 my $mesg=$ldap_handle->search(
2837 base => $main::ldap_server_dn,
2838 scope => 'base',
2839 attrs => 'FAIrepository',
2840 filter => 'objectClass=FAIrepositoryServer'
2841 );
2842 if($mesg->count) {
2843 foreach my $entry(@{$mesg->{'entries'}}) {
2844 foreach my $value(@{$entry->get_value('FAIrepository', asref => 1)}) {
2845 my ($server, $tag, $release, $sections)= split /\|/, $value;
2846 my $line = "deb $server $release";
2847 $sections =~ s/,/ /g;
2848 $line.= " $sections";
2849 print $fh $line."\n";
2850 }
2851 }
2852 }
2853 &release_ldap_handle($ldap_handle);
2854 } else {
2855 if (defined $main::ldap_server_dn){
2856 &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1);
2857 } else {
2858 &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2859 }
2860 }
2861 close($fh);
2863 return $result;
2864 }
2867 sub run_create_packages_list_db {
2868 my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2869 my $session_id = $session->ID;
2870 my $task = POE::Wheel::Run->new(
2871 Priority => +20,
2872 Program => sub {&create_packages_list_db(undef, $session_id)},
2873 StdoutEvent => "session_run_result",
2874 StderrEvent => "session_run_debug",
2875 CloseEvent => "session_run_done",
2876 );
2877 $heap->{task}->{ $task->ID } = $task;
2878 }
2881 sub create_packages_list_db {
2882 my ($sources_file, $session_id) = @_;
2884 # it should not be possible to trigger a recreation of packages_list_db
2885 # while packages_list_db is under construction, so set flag packages_list_under_construction
2886 # which is tested befor recreation can be started
2887 if (-r $packages_list_under_construction) {
2888 daemon_log("$session_id WARNING: packages_list_db is right now under construction, please wait until this process is finished", 3);
2889 return;
2890 } else {
2891 daemon_log("$session_id INFO: create_packages_list_db: start", 5);
2892 # set packages_list_under_construction to true
2893 system("touch $packages_list_under_construction");
2894 @packages_list_statements=();
2895 }
2897 if (not defined $session_id) { $session_id = 0; }
2899 if (not defined $sources_file) {
2900 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5);
2901 $sources_file = &create_sources_list($session_id);
2902 }
2904 if (not defined $sources_file) {
2905 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1);
2906 unlink($packages_list_under_construction);
2907 return;
2908 }
2910 my $line;
2912 open(CONFIG, "<$sources_file") or do {
2913 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2914 unlink($packages_list_under_construction);
2915 return;
2916 };
2918 # Read lines
2919 while ($line = <CONFIG>){
2920 # Unify
2921 chop($line);
2922 $line =~ s/^\s+//;
2923 $line =~ s/^\s+/ /;
2925 # Strip comments
2926 $line =~ s/#.*$//g;
2928 # Skip empty lines
2929 if ($line =~ /^\s*$/){
2930 next;
2931 }
2933 # Interpret deb line
2934 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2935 my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2936 my $section;
2937 foreach $section (split(' ', $sections)){
2938 &parse_package_info( $baseurl, $dist, $section, $session_id );
2939 }
2940 }
2941 }
2943 close (CONFIG);
2945 if(keys(%repo_dirs)) {
2946 find(\&cleanup_and_extract, keys( %repo_dirs ));
2947 &main::strip_packages_list_statements();
2948 $packages_list_db->exec_statementlist(\@packages_list_statements);
2949 }
2950 unlink($packages_list_under_construction);
2951 daemon_log("$session_id INFO: create_packages_list_db: finished", 5);
2952 return;
2953 }
2955 # This function should do some intensive task to minimize the db-traffic
2956 sub strip_packages_list_statements {
2957 my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2958 my @new_statement_list=();
2959 my $hash;
2960 my $insert_hash;
2961 my $update_hash;
2962 my $delete_hash;
2963 my $known_packages_hash;
2964 my $local_timestamp=get_time();
2966 foreach my $existing_entry (@existing_entries) {
2967 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
2968 }
2970 foreach my $statement (@packages_list_statements) {
2971 if($statement =~ /^INSERT/i) {
2972 # Assign the values from the insert statement
2973 my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~
2974 /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
2975 if(exists($hash->{$distribution}->{$package}->{$version})) {
2976 # If section or description has changed, update the DB
2977 if(
2978 (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or
2979 (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
2980 ) {
2981 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
2982 } else {
2983 # package is already present in database. cache this knowledge for later use
2984 @{$known_packages_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2985 }
2986 } else {
2987 # Insert a non-existing entry to db
2988 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2989 }
2990 } elsif ($statement =~ /^UPDATE/i) {
2991 my ($template,$package,$version) = ($1,$2,$3) if $statement =~
2992 /^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;
2993 foreach my $distribution (keys %{$hash}) {
2994 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
2995 # update the insertion hash to execute only one query per package (insert instead insert+update)
2996 @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
2997 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
2998 if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
2999 my $section;
3000 my $description;
3001 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
3002 length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
3003 $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
3004 }
3005 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
3006 $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
3007 }
3008 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
3009 }
3010 }
3011 }
3012 }
3013 }
3015 # Check for orphaned entries
3016 foreach my $existing_entry (@existing_entries) {
3017 my $distribution= @{$existing_entry}[0];
3018 my $package= @{$existing_entry}[1];
3019 my $version= @{$existing_entry}[2];
3020 my $section= @{$existing_entry}[3];
3022 if(
3023 exists($insert_hash->{$distribution}->{$package}->{$version}) ||
3024 exists($update_hash->{$distribution}->{$package}->{$version}) ||
3025 exists($known_packages_hash->{$distribution}->{$package}->{$version})
3026 ) {
3027 next;
3028 } else {
3029 # Insert entry to delete hash
3030 @{$delete_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section);
3031 }
3032 }
3034 # unroll the insert hash
3035 foreach my $distribution (keys %{$insert_hash}) {
3036 foreach my $package (keys %{$insert_hash->{$distribution}}) {
3037 foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
3038 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
3039 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
3040 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
3041 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
3042 ."'$local_timestamp')";
3043 }
3044 }
3045 }
3047 # unroll the update hash
3048 foreach my $distribution (keys %{$update_hash}) {
3049 foreach my $package (keys %{$update_hash->{$distribution}}) {
3050 foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
3051 my $set = "";
3052 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
3053 $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
3054 }
3055 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
3056 $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
3057 }
3058 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
3059 $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
3060 }
3061 if(defined($set) and length($set) > 0) {
3062 $set .= "timestamp = '$local_timestamp'";
3063 } else {
3064 next;
3065 }
3066 push @new_statement_list,
3067 "UPDATE $main::packages_list_tn SET $set WHERE"
3068 ." distribution = '$distribution'"
3069 ." AND package = '$package'"
3070 ." AND version = '$version'";
3071 }
3072 }
3073 }
3075 # unroll the delete hash
3076 foreach my $distribution (keys %{$delete_hash}) {
3077 foreach my $package (keys %{$delete_hash->{$distribution}}) {
3078 foreach my $version (keys %{$delete_hash->{$distribution}->{$package}}) {
3079 my $section = @{$delete_hash->{$distribution}->{$package}->{$version}}[3];
3080 push @new_statement_list, "DELETE FROM $main::packages_list_tn WHERE distribution='$distribution' AND package='$package' AND version='$version' AND section='$section'";
3081 }
3082 }
3083 }
3085 unshift(@new_statement_list, "VACUUM");
3087 @packages_list_statements = @new_statement_list;
3088 }
3091 sub parse_package_info {
3092 my ($baseurl, $dist, $section, $session_id)= @_;
3093 my ($package);
3094 if (not defined $session_id) { $session_id = 0; }
3095 my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
3096 $repo_dirs{ "${repo_path}/pool" } = 1;
3098 foreach $package ("Packages.gz"){
3099 daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
3100 get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
3101 parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
3102 }
3104 }
3107 sub get_package {
3108 my ($url, $dest, $session_id)= @_;
3109 if (not defined $session_id) { $session_id = 0; }
3111 my $tpath = dirname($dest);
3112 -d "$tpath" || mkpath "$tpath";
3114 # This is ugly, but I've no time to take a look at "how it works in perl"
3115 if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
3116 system("gunzip -cd '$dest' > '$dest.in'");
3117 daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 7);
3118 unlink($dest);
3119 daemon_log("$session_id DEBUG: delete file '$dest'", 7);
3120 } else {
3121 daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' into '$dest' failed!", 1);
3122 }
3123 return 0;
3124 }
3127 sub parse_package {
3128 my ($path, $dist, $srv_path, $session_id)= @_;
3129 if (not defined $session_id) { $session_id = 0;}
3130 my ($package, $version, $section, $description);
3131 my $PACKAGES;
3132 my $timestamp = &get_time();
3134 if(not stat("$path.in")) {
3135 daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
3136 return;
3137 }
3139 open($PACKAGES, "<$path.in");
3140 if(not defined($PACKAGES)) {
3141 daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1);
3142 return;
3143 }
3145 # Read lines
3146 while (<$PACKAGES>){
3147 my $line = $_;
3148 # Unify
3149 chop($line);
3151 # Use empty lines as a trigger
3152 if ($line =~ /^\s*$/){
3153 my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
3154 push(@packages_list_statements, $sql);
3155 $package = "none";
3156 $version = "none";
3157 $section = "none";
3158 $description = "none";
3159 next;
3160 }
3162 # Trigger for package name
3163 if ($line =~ /^Package:\s/){
3164 ($package)= ($line =~ /^Package: (.*)$/);
3165 next;
3166 }
3168 # Trigger for version
3169 if ($line =~ /^Version:\s/){
3170 ($version)= ($line =~ /^Version: (.*)$/);
3171 next;
3172 }
3174 # Trigger for description
3175 if ($line =~ /^Description:\s/){
3176 ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
3177 next;
3178 }
3180 # Trigger for section
3181 if ($line =~ /^Section:\s/){
3182 ($section)= ($line =~ /^Section: (.*)$/);
3183 next;
3184 }
3186 # Trigger for filename
3187 if ($line =~ /^Filename:\s/){
3188 my ($filename) = ($line =~ /^Filename: (.*)$/);
3189 store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
3190 next;
3191 }
3192 }
3194 close( $PACKAGES );
3195 unlink( "$path.in" );
3196 }
3199 sub store_fileinfo {
3200 my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
3202 my %fileinfo = (
3203 'package' => $package,
3204 'dist' => $dist,
3205 'version' => $vers,
3206 );
3208 $repo_files{ "${srvdir}/$file" } = \%fileinfo;
3209 }
3212 sub cleanup_and_extract {
3213 my $fileinfo = $repo_files{ $File::Find::name };
3215 if( defined $fileinfo ) {
3216 my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
3217 my $sql;
3218 my $package = $fileinfo->{ 'package' };
3219 my $newver = $fileinfo->{ 'version' };
3221 mkpath($dir);
3222 system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
3224 if( -f "$dir/DEBIAN/templates" ) {
3226 daemon_log("0 DEBUG: Found debconf templates in '$package' - $newver", 7);
3228 my $tmpl= ""; {
3229 local $/=undef;
3230 open FILE, "$dir/DEBIAN/templates";
3231 $tmpl = &encode_base64(<FILE>);
3232 close FILE;
3233 }
3234 rmtree("$dir/DEBIAN/templates");
3236 $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
3237 push @packages_list_statements, $sql;
3238 }
3239 }
3241 return;
3242 }
3245 sub register_at_foreign_servers {
3246 my ($kernel) = $_[KERNEL];
3248 # Update status and update-time of all si-server with expired update_time and
3249 # block them for race conditional registration processes of other si-servers.
3250 my $act_time = &get_time();
3251 my $block_statement = "UPDATE $known_server_tn SET status='new_server',update_time='19700101000000' WHERE (CAST(update_time AS UNSIGNED))<$act_time ";
3252 &daemon_log("0 DEBUG: $block_statement", 7);
3253 my $block_res = $known_server_db->exec_statement($block_statement);
3255 # Fetch all si-server from db where update_time is younger than act_time
3256 my $fetch_statement = "SELECT * FROM $known_server_tn WHERE update_time='19700101000000'";
3257 &daemon_log("0 DEBUG: $fetch_statement", 7);
3258 my $fetch_res = $known_server_db->exec_statement($fetch_statement);
3260 # Detect already connected clients. Will be added to registration msg later.
3261 my $client_sql = "SELECT * FROM $known_clients_tn";
3262 my $client_res = $known_clients_db->exec_statement($client_sql);
3264 # Send registration messag to all fetched si-server
3265 foreach my $hit (@$fetch_res) {
3266 my $hostname = @$hit[0];
3267 my $hostkey = &create_passwd;
3269 # Add already connected clients to registration message
3270 my $myhash = &create_xml_hash('new_server', $server_address, $hostname);
3271 &add_content2xml_hash($myhash, 'key', $hostkey);
3272 map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res);
3274 # Add locally loaded gosa-si modules to registration message
3275 my $loaded_modules = {};
3276 while (my ($package, $pck_info) = each %$known_modules) {
3277 next if ((!defined(@$pck_info[2])) || (!(ref (@$pck_info[2]) eq 'HASH')));
3278 foreach my $act_module (keys(%{@$pck_info[2]})) {
3279 $loaded_modules->{$act_module} = "";
3280 }
3281 }
3282 map(&add_content2xml_hash($myhash, "loaded_modules", $_), keys(%$loaded_modules));
3284 # Add macaddress to registration message
3285 my ($host_ip, $host_port) = split(/:/, $hostname);
3286 my $local_ip = &get_local_ip_for_remote_ip($host_ip);
3287 my $network_interface= &get_interface_for_ip($local_ip);
3288 my $host_mac = &get_mac_for_interface($network_interface);
3289 &add_content2xml_hash($myhash, 'macaddress', $host_mac);
3291 # Build registration message and send it
3292 my $foreign_server_msg = &create_xml_string($myhash);
3293 my $error = &send_msg_to_target($foreign_server_msg, $hostname, $ServerPackages_key, "new_server", 0);
3294 }
3297 # After n sec perform a check of all server registration processes
3298 $kernel->delay_set("control_server_registration", 2);
3300 return;
3301 }
3304 sub control_server_registration {
3305 my ($kernel) = $_[KERNEL];
3307 # Check if all registration processes succeed or not
3308 my $select_statement = "SELECT * FROM $known_server_tn WHERE status='new_server'";
3309 &daemon_log("0 DEBUG $select_statement", 7);
3310 my $select_res = $known_server_db->exec_statement($select_statement);
3312 # If at least one registration process failed, maybe in case of a race condition
3313 # with a foreign registration process
3314 if (@$select_res > 0)
3315 {
3316 # Release block statement 'new_server' to make the server accessible
3317 # for foreign registration processes
3318 my $update_statement = "UPDATE $known_server_tn SET status='waiting' WHERE status='new_server'";
3319 &daemon_log("0 DEBUG: $update_statement", 7);
3320 my $update_res = $known_server_db->exec_statement($update_statement);
3322 # Set a random delay to avoid the registration race condition
3323 my $new_foreign_servers_register_delay = int(rand(4))+1;
3324 $kernel->delay_set("register_at_foreign_servers", $new_foreign_servers_register_delay);
3325 }
3326 # If all registration processes succeed
3327 else
3328 {
3329 $kernel->delay_set("register_at_foreign_servers", $foreign_servers_register_delay);
3330 }
3332 return;
3333 }
3336 #==== MAIN = main ==============================================================
3337 # parse commandline options
3338 Getopt::Long::Configure( "bundling" );
3339 GetOptions("h|help" => \&usage,
3340 "c|config=s" => \$cfg_file,
3341 "f|foreground" => \$foreground,
3342 "v|verbose+" => \$verbose,
3343 "no-arp+" => \$no_arp,
3344 "d=s" => \$debug_parts,
3345 ) or (&usage("", 1)&&(exit(-1)));
3347 # read and set config parameters
3348 &check_cmdline_param ;
3349 &read_configfile($cfg_file, %cfg_defaults);
3350 &check_pid;
3352 $SIG{CHLD} = 'IGNORE';
3354 # forward error messages to logfile
3355 if( ! $foreground ) {
3356 open( STDIN, '+>/dev/null' );
3357 open( STDOUT, '+>&STDIN' );
3358 open( STDERR, '+>&STDIN' );
3359 }
3361 # Just fork, if we are not in foreground mode
3362 if( ! $foreground ) {
3363 chdir '/' or die "Can't chdir to /: $!";
3364 $pid = fork;
3365 setsid or die "Can't start a new session: $!";
3366 umask 0;
3367 } else {
3368 $pid = $$;
3369 }
3371 # Do something useful - put our PID into the pid_file
3372 if( 0 != $pid ) {
3373 open( LOCK_FILE, ">$pid_file" );
3374 print LOCK_FILE "$pid\n";
3375 close( LOCK_FILE );
3376 if( !$foreground ) {
3377 exit( 0 )
3378 };
3379 }
3381 # parse head url and revision from svn
3382 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
3383 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
3384 $server_headURL = defined $1 ? $1 : 'unknown' ;
3385 $server_revision = defined $2 ? $2 : 'unknown' ;
3386 if ($server_headURL =~ /\/tag\// ||
3387 $server_headURL =~ /\/branches\// ) {
3388 $server_status = "stable";
3389 } else {
3390 $server_status = "developmental" ;
3391 }
3392 # Prepare log file and set permissions
3393 $root_uid = getpwnam('root');
3394 $adm_gid = getgrnam('adm');
3395 open(FH, ">>$log_file");
3396 close FH;
3397 chmod(0440, $log_file);
3398 chown($root_uid, $adm_gid, $log_file);
3399 chown($root_uid, $adm_gid, "/var/lib/gosa-si");
3401 daemon_log(" ", 1);
3402 daemon_log("$0 started!", 1);
3403 daemon_log("status: $server_status", 1);
3404 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1);
3406 # Buildup data bases
3407 {
3408 no strict "refs";
3410 if ($db_module eq "DBmysql") {
3411 # connect to incoming_db
3412 $incoming_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3414 # connect to gosa-si job queue
3415 $job_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3417 # connect to known_clients_db
3418 $known_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3420 # connect to foreign_clients_db
3421 $foreign_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3423 # connect to known_server_db
3424 $known_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3426 # connect to login_usr_db
3427 $login_users_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3429 # connect to fai_server_db
3430 $fai_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3432 # connect to fai_release_db
3433 $fai_release_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3435 # connect to packages_list_db
3436 $packages_list_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3438 # connect to messaging_db
3439 $messaging_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3441 } elsif ($db_module eq "DBsqlite") {
3442 # connect to incoming_db
3443 unlink($incoming_file_name);
3444 $incoming_db = GOSA::DBsqlite->new($incoming_file_name);
3445 chmod(0640, $incoming_file_name);
3446 chown($root_uid, $adm_gid, $incoming_file_name);
3448 # connect to gosa-si job queue
3449 $job_db = GOSA::DBsqlite->new($job_queue_file_name);
3450 chmod(0640, $job_queue_file_name);
3451 chown($root_uid, $adm_gid, $job_queue_file_name);
3453 # connect to known_clients_db
3454 #unlink($known_clients_file_name);
3455 $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
3456 chmod(0640, $known_clients_file_name);
3457 chown($root_uid, $adm_gid, $known_clients_file_name);
3459 # connect to foreign_clients_db
3460 #unlink($foreign_clients_file_name);
3461 $foreign_clients_db = GOSA::DBsqlite->new($foreign_clients_file_name);
3462 chmod(0640, $foreign_clients_file_name);
3463 chown($root_uid, $adm_gid, $foreign_clients_file_name);
3465 # connect to known_server_db
3466 #unlink($known_server_file_name);
3467 $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
3468 chmod(0640, $known_server_file_name);
3469 chown($root_uid, $adm_gid, $known_server_file_name);
3471 # connect to login_usr_db
3472 #unlink($login_users_file_name);
3473 $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
3474 chmod(0640, $login_users_file_name);
3475 chown($root_uid, $adm_gid, $login_users_file_name);
3477 # connect to fai_server_db
3478 unlink($fai_server_file_name);
3479 $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
3480 chmod(0640, $fai_server_file_name);
3481 chown($root_uid, $adm_gid, $fai_server_file_name);
3483 # connect to fai_release_db
3484 unlink($fai_release_file_name);
3485 $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
3486 chmod(0640, $fai_release_file_name);
3487 chown($root_uid, $adm_gid, $fai_release_file_name);
3489 # connect to packages_list_db
3490 unlink($packages_list_under_construction);
3491 $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
3492 chmod(0640, $packages_list_file_name);
3493 chown($root_uid, $adm_gid, $packages_list_file_name);
3495 # connect to messaging_db
3496 #unlink($messaging_file_name);
3497 $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
3498 chmod(0640, $messaging_file_name);
3499 chown($root_uid, $adm_gid, $messaging_file_name);
3500 }
3501 }
3504 # Creating tables
3505 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
3506 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
3507 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
3508 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
3509 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
3510 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
3511 $foreign_clients_db->create_table($foreign_clients_tn, \@foreign_clients_col_names);
3512 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
3513 $incoming_db->create_table($incoming_tn, \@incoming_col_names);
3514 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
3516 # create xml object used for en/decrypting
3517 $xml = new XML::Simple();
3520 # foreign servers
3521 my @foreign_server_list;
3523 # add foreign server from cfg file
3524 if ($foreign_server_string ne "") {
3525 my @cfg_foreign_server_list = split(",", $foreign_server_string);
3526 foreach my $foreign_server (@cfg_foreign_server_list) {
3527 push(@foreign_server_list, $foreign_server);
3528 }
3530 daemon_log("0 INFO: found foreign server in config file: ".join(", ", @foreign_server_list), 5);
3531 }
3533 # Perform a DNS lookup for server registration if flag is true
3534 if ($dns_lookup eq "true") {
3535 # Add foreign server from dns
3536 my @tmp_servers;
3537 if (not $server_domain) {
3538 # Try our DNS Searchlist
3539 for my $domain(get_dns_domains()) {
3540 chomp($domain);
3541 my ($tmp_domains, $error_string) = &get_server_addresses($domain);
3542 if(@$tmp_domains) {
3543 for my $tmp_server(@$tmp_domains) {
3544 push @tmp_servers, $tmp_server;
3545 }
3546 }
3547 }
3548 if(@tmp_servers && length(@tmp_servers)==0) {
3549 daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3550 }
3551 } else {
3552 @tmp_servers = &get_server_addresses($server_domain);
3553 if( 0 == @tmp_servers ) {
3554 daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3555 }
3556 }
3558 daemon_log("0 INFO: found foreign server via DNS ".join(", ", @tmp_servers), 5);
3560 foreach my $server (@tmp_servers) {
3561 unshift(@foreign_server_list, $server);
3562 }
3563 } else {
3564 daemon_log("0 INFO: DNS lookup for server registration is disabled", 5);
3565 }
3568 # eliminate duplicate entries
3569 @foreign_server_list = &del_doubles(@foreign_server_list);
3570 my $all_foreign_server = join(", ", @foreign_server_list);
3571 daemon_log("0 INFO: found foreign server in config file and DNS: '$all_foreign_server'", 5);
3573 # add all found foreign servers to known_server
3574 my $cur_timestamp = &get_time();
3575 foreach my $foreign_server (@foreign_server_list) {
3577 # do not add myself to known_server_db
3578 if (&is_local($foreign_server)) { next; }
3579 ######################################
3581 my $res = $known_server_db->add_dbentry( {table=>$known_server_tn,
3582 primkey=>['hostname'],
3583 hostname=>$foreign_server,
3584 macaddress=>"",
3585 status=>'not_yet_registered',
3586 hostkey=>"none",
3587 loaded_modules => "none",
3588 timestamp=>$cur_timestamp,
3589 update_time=>'19700101000000',
3590 } );
3591 }
3594 # Import all modules
3595 &import_modules;
3597 # Check wether all modules are gosa-si valid passwd check
3598 &password_check;
3600 # Create functions hash
3601 #print STDERR Dumper $known_modules;
3602 while (my ($module, @mod_info) = each %$known_modules)
3603 {
3604 #print STDERR Dumper $module;
3605 while (my ($plugin, $functions) = each %{$mod_info[0][2]})
3606 {
3607 #print STDERR Dumper $functions;
3608 while (my ($function, $nothing) = each %$functions )
3609 {
3610 $known_functions->{$function} = $nothing;
3611 }
3612 }
3613 }
3615 # Prepare for using Opsi
3616 if ($opsi_enabled eq "true") {
3617 use JSON::RPC::Client;
3618 use XML::Quote qw(:all);
3619 $opsi_url= "https://".$opsi_admin.":".$opsi_password."@".$opsi_server.":4447/rpc";
3620 $opsi_client = new JSON::RPC::Client;
3621 }
3624 POE::Component::Server::TCP->new(
3625 Alias => "TCP_SERVER",
3626 Port => $server_port,
3627 ClientInput => sub {
3628 my ($kernel, $input, $heap, $session) = @_[KERNEL, ARG0, HEAP, SESSION];
3629 my $session_id = $session->ID;
3630 if ($input =~ /;([\d\.]+:[\d]+)$/)
3631 {
3632 &daemon_log("$session_id DEBUG: incoming message from '$1'", 11);
3633 }
3634 else
3635 {
3636 my $remote_ip = $heap->{'remote_ip'};
3637 &daemon_log("$session_id DEBUG: incoming message from '$remote_ip'", 11);
3638 }
3639 push(@msgs_to_decrypt, $input);
3640 $kernel->yield("msg_to_decrypt");
3641 },
3642 InlineStates => {
3643 msg_to_decrypt => \&msg_to_decrypt,
3644 next_task => \&next_task,
3645 task_result => \&handle_task_result,
3646 task_done => \&handle_task_done,
3647 task_debug => \&handle_task_debug,
3648 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!" },
3649 }
3650 );
3652 daemon_log("0 INFO: start socket for incoming xml messages at port '$server_port' ", 1);
3654 # create session for repeatedly checking the job queue for jobs
3655 POE::Session->create(
3656 inline_states => {
3657 _start => \&session_start,
3658 register_at_foreign_servers => \®ister_at_foreign_servers,
3659 control_server_registration => \&control_server_registration,
3660 sig_handler => \&sig_handler,
3661 next_task => \&next_task,
3662 task_result => \&handle_task_result,
3663 task_done => \&handle_task_done,
3664 task_debug => \&handle_task_debug,
3665 watch_for_next_tasks => \&watch_for_next_tasks,
3666 watch_for_new_messages => \&watch_for_new_messages,
3667 watch_for_delivery_messages => \&watch_for_delivery_messages,
3668 watch_for_done_messages => \&watch_for_done_messages,
3669 watch_for_new_jobs => \&watch_for_new_jobs,
3670 watch_for_modified_jobs => \&watch_for_modified_jobs,
3671 watch_for_done_jobs => \&watch_for_done_jobs,
3672 watch_for_opsi_jobs => \&watch_for_opsi_jobs,
3673 watch_for_old_known_clients => \&watch_for_old_known_clients,
3674 create_packages_list_db => \&run_create_packages_list_db,
3675 create_fai_server_db => \&run_create_fai_server_db,
3676 create_fai_release_db => \&run_create_fai_release_db,
3677 recreate_packages_db => \&run_recreate_packages_db,
3678 session_run_result => \&session_run_result,
3679 session_run_debug => \&session_run_debug,
3680 session_run_done => \&session_run_done,
3681 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!" },
3682 }
3683 );
3686 POE::Kernel->run();
3687 exit;