e8dbfae9fd7d6d11c8fe316d60122ca2670c3f42
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 : message processing
319 32 : ldap connectivity
320 64 : database status and connectivity
321 128 : main process
323 EOF
324 print "\n" ;
325 }
328 #=== FUNCTION ================================================================
329 # NAME: logging
330 # PARAMETERS: level - string - default 'info'
331 # msg - string -
332 # facility - string - default 'LOG_DAEMON'
333 # RETURNS: nothing
334 # DESCRIPTION: function for logging
335 #===============================================================================
336 sub daemon_log {
337 my( $msg, $level ) = @_;
338 if (not defined $msg) { return }
339 if (not defined $level) { $level = 1 }
340 my $to_be_logged = 0;
342 # Write log line if line level is lower than verbosity given in commandline
343 if ($level <= $verbose)
344 {
345 $to_be_logged = 1 ;
346 }
348 # Write if debug flag is set and bitstring matches
349 if ($debug_parts > 0)
350 {
351 my $tmp_level = ($level - 10 >= 0) ? $level - 10 : 0 ;
352 my $tmp_level_bitstring = unpack("B32", pack("N", $tmp_level));
353 if (int($debug_parts_bitstring & $tmp_level_bitstring))
354 {
355 $to_be_logged = 1;
356 }
357 }
359 if ($to_be_logged)
360 {
361 if(defined $log_file){
362 my $open_log_fh = sysopen(LOG_HANDLE, $log_file, O_WRONLY | O_CREAT | O_APPEND , 0440);
363 if(not $open_log_fh) {
364 print STDERR "cannot open $log_file: $!";
365 return;
366 }
367 # Check owner and group of log_file and update settings if necessary
368 my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = stat($log_file);
369 if((not $uid eq $root_uid) || (not $gid eq $adm_gid)) {
370 chown($root_uid, $adm_gid, $log_file);
371 }
373 # Prepare time string for log message
374 my ($seconds,$minutes,$hours,$monthday,$month,$year,$weekday,$yearday,$sommertime) = localtime(time);
375 $hours = $hours < 10 ? $hours = "0".$hours : $hours;
376 $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
377 $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
378 $month = $monthnames[$month];
379 $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
380 $year+=1900;
383 # Build log message and write it to log file and commandline
384 chomp($msg);
385 my $log_msg = "$month $monthday $hours:$minutes:$seconds $prg $msg\n";
386 flock(LOG_HANDLE, LOCK_EX);
387 seek(LOG_HANDLE, 0, 2);
388 print LOG_HANDLE $log_msg;
389 flock(LOG_HANDLE, LOCK_UN);
390 if( $foreground )
391 {
392 print STDERR $log_msg;
393 }
394 close( LOG_HANDLE );
395 }
396 }
397 }
400 #=== FUNCTION ================================================================
401 # NAME: check_cmdline_param
402 # PARAMETERS: nothing
403 # RETURNS: nothing
404 # DESCRIPTION: validates commandline parameter
405 #===============================================================================
406 sub check_cmdline_param () {
407 my $err_counter = 0;
409 # Check configuration file
410 if(not defined($cfg_file)) {
411 $cfg_file = "/etc/gosa-si/server.conf";
412 if(! -r $cfg_file) {
413 print STDERR "Please specify a config file.\n";
414 $err_counter++;
415 }
416 }
418 # Prepare identification which gosa-si parts should be debugged and which not
419 if (defined $debug_parts)
420 {
421 if ($debug_parts =~ /^\d+$/)
422 {
423 $debug_parts_bitstring = unpack("B32", pack("N", $debug_parts));
424 }
425 else
426 {
427 print STDERR "Value '$debug_parts' invalid for option d (number expected)\n";
428 $err_counter++;
429 }
430 }
432 # Exit if an error occour
433 if( $err_counter > 0 )
434 {
435 &usage( "", 1 );
436 exit( -1 );
437 }
438 }
441 #=== FUNCTION ================================================================
442 # NAME: check_pid
443 # PARAMETERS: nothing
444 # RETURNS: nothing
445 # DESCRIPTION: handels pid processing
446 #===============================================================================
447 sub check_pid {
448 $pid = -1;
449 # Check, if we are already running
450 if( open(LOCK_FILE, "<$pid_file") ) {
451 $pid = <LOCK_FILE>;
452 if( defined $pid ) {
453 chomp( $pid );
454 if( -f "/proc/$pid/stat" ) {
455 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
456 if( $stat ) {
457 print STDERR "\nERROR: Already running!\n";
458 close( LOCK_FILE );
459 exit -1;
460 }
461 }
462 }
463 close( LOCK_FILE );
464 unlink( $pid_file );
465 }
467 # create a syslog msg if it is not to possible to open PID file
468 if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
469 my($msg) = "Couldn't obtain lockfile '$pid_file' ";
470 if (open(LOCK_FILE, '<', $pid_file)
471 && ($pid = <LOCK_FILE>))
472 {
473 chomp($pid);
474 $msg .= "(PID $pid)\n";
475 } else {
476 $msg .= "(unable to read PID)\n";
477 }
478 if( ! ($foreground) ) {
479 openlog( $0, "cons,pid", "daemon" );
480 syslog( "warning", $msg );
481 closelog();
482 }
483 else {
484 print( STDERR " $msg " );
485 }
486 exit( -1 );
487 }
488 }
490 #=== FUNCTION ================================================================
491 # NAME: import_modules
492 # PARAMETERS: module_path - string - abs. path to the directory the modules
493 # are stored
494 # RETURNS: nothing
495 # DESCRIPTION: each file in module_path which ends with '.pm' and activation
496 # state is on is imported by "require 'file';"
497 #===============================================================================
498 sub import_modules {
499 if (not -e $modules_path) {
500 daemon_log("0 ERROR: cannot find directory or directory is not readable: $modules_path", 1);
501 }
503 opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
505 while (defined (my $file = readdir (DIR))) {
506 if (not $file =~ /(\S*?).pm$/) {
507 next;
508 }
509 my $mod_name = $1;
511 # ArpHandler switch
512 if( $file =~ /ArpHandler.pm/ ) {
513 if( $arp_enabled eq "false" ) { next; }
514 }
516 eval { require $file; };
517 if ($@) {
518 daemon_log("0 ERROR: gosa-si-server could not load module $file", 1);
519 daemon_log("$@", 1);
520 exit;
521 } else {
522 my $info = eval($mod_name.'::get_module_info()');
523 # Only load module if get_module_info() returns a non-null object
524 if( $info ) {
525 my ($input_address, $input_key, $event_hash) = @{$info};
526 $known_modules->{$mod_name} = $info;
527 daemon_log("0 INFO: module $mod_name loaded", 5);
528 }
529 }
530 }
531 close (DIR);
532 }
534 #=== FUNCTION ================================================================
535 # NAME: password_check
536 # PARAMETERS: nothing
537 # RETURNS: nothing
538 # DESCRIPTION: escalates an critical error if two modules exist which are avaialable by
539 # the same password
540 #===============================================================================
541 sub password_check {
542 my $passwd_hash = {};
543 while (my ($mod_name, $mod_info) = each %$known_modules) {
544 my $mod_passwd = @$mod_info[1];
545 if (not defined $mod_passwd) { next; }
546 if (not exists $passwd_hash->{$mod_passwd}) {
547 $passwd_hash->{$mod_passwd} = $mod_name;
549 # escalates critical error
550 } else {
551 &daemon_log("0 ERROR: two loaded modules do have the same password. Please modify the 'key'-parameter in config file");
552 &daemon_log("0 ERROR: module='$mod_name' and module='".$passwd_hash->{$mod_passwd}."'");
553 exit( -1 );
554 }
555 }
557 }
560 #=== FUNCTION ================================================================
561 # NAME: sig_int_handler
562 # PARAMETERS: signal - string - signal arose from system
563 # RETURNS: nothing
564 # DESCRIPTION: handels tasks to be done befor signal becomes active
565 #===============================================================================
566 sub sig_int_handler {
567 my ($signal) = @_;
569 # if (defined($ldap_handle)) {
570 # $ldap_handle->disconnect;
571 # }
572 # TODO alle verbliebenden ldap verbindungen aus allen heaps beenden
575 daemon_log("shutting down gosa-si-server", 1);
576 system("kill `ps -C gosa-si-server -o pid=`");
577 }
578 $SIG{INT} = \&sig_int_handler;
581 sub check_key_and_xml_validity {
582 my ($crypted_msg, $module_key, $session_id) = @_;
583 my $msg;
584 my $msg_hash;
585 my $error_string;
586 eval{
587 $msg = &decrypt_msg($crypted_msg, $module_key);
589 if ($msg =~ /<xml>/i){
590 $msg =~ s/\s+/ /g; # just for better daemon_log
591 daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 18);
592 $msg_hash = $xml->XMLin($msg, ForceArray=>1);
594 ##############
595 # check header
596 if( not exists $msg_hash->{'header'} ) { die "no header specified"; }
597 my $header_l = $msg_hash->{'header'};
598 if( (1 > @{$header_l}) || ( ( 'HASH' eq ref @{$header_l}[0]) && (1 > keys %{@{$header_l}[0]}) ) ) { die 'empty header tag'; }
599 if( 1 < @{$header_l} ) { die 'more than one header specified'; }
600 my $header = @{$header_l}[0];
601 if( 0 == length $header) { die 'empty string in header tag'; }
603 ##############
604 # check source
605 if( not exists $msg_hash->{'source'} ) { die "no source specified"; }
606 my $source_l = $msg_hash->{'source'};
607 if( (1 > @{$source_l}) || ( ( 'HASH' eq ref @{$source_l}[0]) && (1 > keys %{@{$source_l}[0]}) ) ) { die 'empty source tag'; }
608 if( 1 < @{$source_l} ) { die 'more than one source specified'; }
609 my $source = @{$source_l}[0];
610 if( 0 == length $source) { die 'source error'; }
612 ##############
613 # check target
614 if( not exists $msg_hash->{'target'} ) { die "no target specified"; }
615 my $target_l = $msg_hash->{'target'};
616 if( (1 > @{$target_l}) || ( ('HASH' eq ref @{$target_l}[0]) && (1 > keys %{@{$target_l}[0]}) ) ) { die 'empty target tag'; }
617 }
618 };
619 if($@) {
620 daemon_log("$session_id ERROR: do not understand the message: $@", 1);
621 $msg = undef;
622 $msg_hash = undef;
623 }
625 return ($msg, $msg_hash);
626 }
629 sub check_outgoing_xml_validity {
630 my ($msg, $session_id) = @_;
632 my $msg_hash;
633 eval{
634 $msg_hash = $xml->XMLin($msg, ForceArray=>1);
636 ##############
637 # check header
638 my $header_l = $msg_hash->{'header'};
639 if( 1 != @{$header_l} ) {
640 die 'no or more than one headers specified';
641 }
642 my $header = @{$header_l}[0];
643 if( 0 == length $header) {
644 die 'header has length 0';
645 }
647 ##############
648 # check source
649 my $source_l = $msg_hash->{'source'};
650 if( 1 != @{$source_l} ) {
651 die 'no or more than 1 sources specified';
652 }
653 my $source = @{$source_l}[0];
654 if( 0 == length $source) {
655 die 'source has length 0';
656 }
658 # Check if source contains hostname instead of ip address
659 if($source =~ /^[a-z][\w-\.]+:\d+$/i) {
660 my ($hostname,$port) = split(/:/, $source);
661 my $ip_address = inet_ntoa(scalar gethostbyname($hostname));
662 if(defined($ip_address) && $ip_address =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/ && $port =~ /^\d+$/) {
663 # Write ip address to $source variable
664 $source = "$ip_address:$port";
665 $msg_hash->{source}[0] = $source ;
666 $msg =~ s/<source>.*<\/source>/<source>$source<\/source>/;
667 }
668 }
669 unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
670 $source =~ /^GOSA$/i) {
671 die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
672 }
674 ##############
675 # check target
676 my $target_l = $msg_hash->{'target'};
677 if( 0 == @{$target_l} ) {
678 die "no targets specified";
679 }
680 foreach my $target (@$target_l) {
681 if( 0 == length $target) {
682 die "target has length 0";
683 }
684 unless( $target =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
685 $target =~ /^GOSA$/i ||
686 $target =~ /^\*$/ ||
687 $target =~ /KNOWN_SERVER/i ||
688 $target =~ /JOBDB/i ||
689 $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 ){
690 die "target '$target' is not a complete ip-address with port or a valid target name or a mac-address";
691 }
692 }
693 };
694 if($@) {
695 daemon_log("$session_id ERROR: outgoing msg is not gosa-si envelope conform: $@", 1);
696 daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 1);
697 $msg_hash = undef;
698 }
700 return ($msg, $msg_hash);
701 }
704 sub input_from_known_server {
705 my ($input, $remote_ip, $session_id) = @_ ;
706 my ($msg, $msg_hash, $module);
708 my $sql_statement= "SELECT * FROM known_server";
709 my $query_res = $known_server_db->select_dbentry( $sql_statement );
711 while( my ($hit_num, $hit) = each %{ $query_res } ) {
712 my $host_name = $hit->{hostname};
713 if( not $host_name =~ "^$remote_ip") {
714 next;
715 }
716 my $host_key = $hit->{hostkey};
717 daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 14);
718 daemon_log("$session_id DEBUG: input_from_known_server: host_key: $host_key", 14);
720 # check if module can open msg envelope with module key
721 my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
722 if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
723 daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 14);
724 daemon_log("$@", 14);
725 next;
726 }
727 else {
728 $msg = $tmp_msg;
729 $msg_hash = $tmp_msg_hash;
730 $module = "ServerPackages";
731 daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 14);
732 last;
733 }
734 }
736 if( (!$msg) || (!$msg_hash) || (!$module) ) {
737 daemon_log("$session_id DEBUG: Incoming message is not from a known server", 14);
738 }
740 return ($msg, $msg_hash, $module);
741 }
744 sub input_from_known_client {
745 my ($input, $remote_ip, $session_id) = @_ ;
746 my ($msg, $msg_hash, $module);
748 my $sql_statement= "SELECT * FROM known_clients";
749 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
750 while( my ($hit_num, $hit) = each %{ $query_res } ) {
751 my $host_name = $hit->{hostname};
752 if( not $host_name =~ /^$remote_ip/) {
753 next;
754 }
755 my $host_key = $hit->{hostkey};
756 &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 14);
757 &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 14);
759 # check if module can open msg envelope with module key
760 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
762 if( (!$msg) || (!$msg_hash) ) {
763 &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 14);
764 next;
765 }
766 else {
767 $module = "ClientPackages";
768 daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 14);
769 last;
770 }
771 }
773 if( (!$msg) || (!$msg_hash) || (!$module) ) {
774 &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 14);
775 }
777 return ($msg, $msg_hash, $module);
778 }
781 sub input_from_unknown_host {
782 no strict "refs";
783 my ($input, $session_id) = @_ ;
784 my ($msg, $msg_hash, $module);
785 my $error_string;
787 my %act_modules = %$known_modules;
789 while( my ($mod, $info) = each(%act_modules)) {
791 # check a key exists for this module
792 my $module_key = ${$mod."_key"};
793 if( not defined $module_key ) {
794 if( $mod eq 'ArpHandler' ) {
795 next;
796 }
797 daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
798 next;
799 }
800 daemon_log("$session_id DEBUG: $mod: $module_key", 14);
802 # check if module can open msg envelope with module key
803 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
804 if( (not defined $msg) || (not defined $msg_hash) ) {
805 next;
806 } else {
807 $module = $mod;
808 daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 18);
809 last;
810 }
811 }
813 if( (!$msg) || (!$msg_hash) || (!$module)) {
814 daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 14);
815 }
817 return ($msg, $msg_hash, $module);
818 }
821 sub create_ciphering {
822 my ($passwd) = @_;
823 if((!defined($passwd)) || length($passwd)==0) {
824 $passwd = "";
825 }
826 $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
827 my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
828 my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
829 $my_cipher->set_iv($iv);
830 return $my_cipher;
831 }
834 sub encrypt_msg {
835 my ($msg, $key) = @_;
836 my $my_cipher = &create_ciphering($key);
837 my $len;
838 {
839 use bytes;
840 $len= 16-length($msg)%16;
841 }
842 $msg = "\0"x($len).$msg;
843 $msg = $my_cipher->encrypt($msg);
844 chomp($msg = &encode_base64($msg));
845 # there are no newlines allowed inside msg
846 $msg=~ s/\n//g;
847 return $msg;
848 }
851 sub decrypt_msg {
853 my ($msg, $key) = @_ ;
854 $msg = &decode_base64($msg);
855 my $my_cipher = &create_ciphering($key);
856 $msg = $my_cipher->decrypt($msg);
857 $msg =~ s/\0*//g;
858 return $msg;
859 }
862 sub get_encrypt_key {
863 my ($target) = @_ ;
864 my $encrypt_key;
865 my $error = 0;
867 # target can be in known_server
868 if( not defined $encrypt_key ) {
869 my $sql_statement= "SELECT * FROM known_server WHERE hostname='$target'";
870 my $query_res = $known_server_db->select_dbentry( $sql_statement );
871 while( my ($hit_num, $hit) = each %{ $query_res } ) {
872 my $host_name = $hit->{hostname};
873 if( $host_name ne $target ) {
874 next;
875 }
876 $encrypt_key = $hit->{hostkey};
877 last;
878 }
879 }
881 # target can be in known_client
882 if( not defined $encrypt_key ) {
883 my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$target'";
884 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
885 while( my ($hit_num, $hit) = each %{ $query_res } ) {
886 my $host_name = $hit->{hostname};
887 if( $host_name ne $target ) {
888 next;
889 }
890 $encrypt_key = $hit->{hostkey};
891 last;
892 }
893 }
895 return $encrypt_key;
896 }
899 #=== FUNCTION ================================================================
900 # NAME: open_socket
901 # PARAMETERS: PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
902 # [PeerPort] string necessary if port not appended by PeerAddr
903 # RETURNS: socket IO::Socket::INET
904 # DESCRIPTION: open a socket to PeerAddr
905 #===============================================================================
906 sub open_socket {
907 my ($PeerAddr, $PeerPort) = @_ ;
908 if(defined($PeerPort)){
909 $PeerAddr = $PeerAddr.":".$PeerPort;
910 }
911 my $socket;
912 $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
913 Porto => "tcp",
914 Type => SOCK_STREAM,
915 Timeout => 5,
916 );
917 if(not defined $socket) {
918 return;
919 }
920 # &daemon_log("DEBUG: open_socket: $PeerAddr", 7);
921 return $socket;
922 }
925 sub send_msg_to_target {
926 my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
927 my $error = 0;
928 my $header;
929 my $timestamp = &get_time();
930 my $new_status;
931 my $act_status;
932 my ($sql_statement, $res);
934 if( $msg_header ) {
935 $header = "'$msg_header'-";
936 } else {
937 $header = "";
938 }
940 # Memorize own source address
941 my $own_source_address = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
942 $own_source_address .= ":".$server_port;
944 # Patch 0.0.0.0 source to real address
945 $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$own_source_address<\/source>/s;
946 # Patch GOSA source to real address and add forward_to_gosa tag
947 $msg =~ s/<source>GOSA<\/source>/<source>$own_source_address<\/source> <forward_to_gosa>$own_source_address,$session_id<\/forward_to_gosa>/ ;
949 # encrypt xml msg
950 my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
952 # opensocket
953 my $socket = &open_socket($address);
954 if( !$socket ) {
955 daemon_log("$session_id ERROR: Cannot open socket to host '$address'. Message processing aborted!", 1);
956 $error++;
957 }
959 if( $error == 0 ) {
960 # send xml msg
961 print $socket $crypted_msg.";$own_source_address\n";
962 daemon_log("$session_id INFO: send ".$header."msg to $address", 5);
963 daemon_log("$session_id DEBUG: message:\n$msg", 12);
965 }
967 # close socket in any case
968 if( $socket ) {
969 close $socket;
970 }
972 if( $error > 0 ) { $new_status = "down"; }
973 else { $new_status = $msg_header; }
976 # known_clients
977 $sql_statement = "SELECT * FROM $known_clients_tn WHERE hostname='$address'";
978 $res = $known_clients_db->select_dbentry($sql_statement);
979 if( keys(%$res) == 1) {
980 $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
981 if ($act_status eq "down" && $new_status eq "down") {
982 $sql_statement = "DELETE FROM known_clients WHERE hostname='$address'";
983 $res = $known_clients_db->del_dbentry($sql_statement);
984 daemon_log("$session_id WARNING: failed 2x to send msg to host '$address', delete host from known_clients", 3);
985 } else {
986 $sql_statement = "UPDATE known_clients SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
987 $res = $known_clients_db->update_dbentry($sql_statement);
988 if($new_status eq "down"){
989 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
990 } else {
991 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
992 }
993 }
994 }
996 # known_server
997 $sql_statement = "SELECT * FROM $known_server_tn WHERE hostname='$address'";
998 $res = $known_server_db->select_dbentry($sql_statement);
999 if( keys(%$res) == 1) {
1000 $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
1001 if ($act_status eq "down" && $new_status eq "down") {
1002 $sql_statement = "DELETE FROM known_server WHERE hostname='$address'";
1003 $res = $known_server_db->del_dbentry($sql_statement);
1004 daemon_log("$session_id WARNING: failed 2x to send a message to host '$address', delete host from known_server", 3);
1005 }
1006 else {
1007 $sql_statement = "UPDATE known_server SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
1008 $res = $known_server_db->update_dbentry($sql_statement);
1009 if($new_status eq "down"){
1010 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
1011 } else {
1012 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
1013 }
1014 }
1015 }
1016 return $error;
1017 }
1020 sub update_jobdb_status_for_send_msgs {
1021 my ($session_id, $answer, $error) = @_;
1022 if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
1023 &daemon_log("$session_id DEBUG: try to update job status", 138);
1024 my $jobdb_id = $1;
1026 $answer =~ /<header>(.*)<\/header>/;
1027 my $job_header = $1;
1029 $answer =~ /<target>(.*)<\/target>/;
1030 my $job_target = $1;
1032 # Sending msg failed
1033 if( $error ) {
1035 # Set jobs to done, jobs do not need to deliver their message in any case
1036 if (($job_header eq "trigger_action_localboot")
1037 ||($job_header eq "trigger_action_lock")
1038 ||($job_header eq "trigger_action_halt")
1039 ) {
1040 my $sql_statement = "UPDATE $job_queue_tn SET status='done' WHERE id=$jobdb_id";
1041 my $res = $job_db->update_dbentry($sql_statement);
1043 # Reactivate jobs, jobs need to deliver their message
1044 } elsif (($job_header eq "trigger_action_activate")
1045 ||($job_header eq "trigger_action_update")
1046 ||($job_header eq "trigger_action_reinstall")
1047 ||($job_header eq "trigger_activate_new")
1048 ) {
1049 &reactivate_job_with_delay($session_id, $job_target, $job_header, 30 );
1051 # For all other messages
1052 } else {
1053 my $sql_statement = "UPDATE $job_queue_tn ".
1054 "SET status='error', result='can not deliver msg, please consult log file' ".
1055 "WHERE id=$jobdb_id";
1056 my $res = $job_db->update_dbentry($sql_statement);
1057 }
1059 # Sending msg was successful
1060 } else {
1061 # Set jobs localboot, lock, activate, halt, reboot and wake to done
1062 # jobs reinstall, update, inst_update do themself setting to done
1063 if (($job_header eq "trigger_action_localboot")
1064 ||($job_header eq "trigger_action_lock")
1065 ||($job_header eq "trigger_action_activate")
1066 ||($job_header eq "trigger_action_halt")
1067 ||($job_header eq "trigger_action_reboot")
1068 ||($job_header eq "trigger_action_wake")
1069 ||($job_header eq "trigger_wake")
1070 ) {
1072 my $sql_statement = "UPDATE $job_queue_tn ".
1073 "SET status='done' ".
1074 "WHERE id=$jobdb_id AND status='processed'";
1075 my $res = $job_db->update_dbentry($sql_statement);
1076 } else {
1077 &daemon_log("$session_id DEBUG: sending message succeed but cannot update job status.", 138);
1078 }
1079 }
1080 } else {
1081 &daemon_log("$session_id DEBUG: cannot update job status, msg has no jobdb_id-tag.", 138);
1082 }
1083 }
1085 sub reactivate_job_with_delay {
1086 my ($session_id, $target, $header, $delay) = @_ ;
1087 # 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
1089 if (not defined $delay) { $delay = 30 } ;
1090 my $delay_timestamp = &calc_timestamp(&get_time(), "plus", $delay);
1092 my $sql = "UPDATE $job_queue_tn Set timestamp='$delay_timestamp', status='waiting' WHERE (macaddress LIKE 'target' AND headertag='$header')";
1093 my $res = $job_db->update_dbentry($sql);
1094 daemon_log("$session_id INFO: '$header'-job will be reactivated at '$delay_timestamp' ".
1095 "cause client '$target' is currently not available", 5);
1096 return;
1097 }
1100 sub sig_handler {
1101 my ($kernel, $signal) = @_[KERNEL, ARG0] ;
1102 daemon_log("0 INFO got signal '$signal'", 1);
1103 $kernel->sig_handled();
1104 return;
1105 }
1108 sub msg_to_decrypt {
1109 my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
1110 my $session_id = $session->ID;
1111 my ($msg, $msg_hash, $module);
1112 my $error = 0;
1114 # fetch new msg out of @msgs_to_decrypt
1115 my $tmp_next_msg = shift @msgs_to_decrypt;
1116 my ($next_msg, $msg_source) = split(/;/, $tmp_next_msg);
1118 # msg is from a new client or gosa
1119 ($msg, $msg_hash, $module) = &input_from_unknown_host($next_msg, $session_id);
1121 # msg is from a gosa-si-server
1122 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1123 if (not defined $msg_source)
1124 {
1125 # Only needed, to be compatible with older gosa-si-server versions
1126 ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $heap->{'remote_ip'}, $session_id);
1127 }
1128 else
1129 {
1130 ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $msg_source, $session_id);
1131 }
1132 }
1133 # msg is from a gosa-si-client
1134 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1135 ($msg, $msg_hash, $module) = &input_from_known_client($next_msg, $msg_source, $session_id);
1136 }
1137 # an error occurred
1138 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1139 # If an incoming msg could not be decrypted (maybe a wrong key), decide if msg comes from a client
1140 # or a server. In case of a client, send a ping. If the client could not understand a msg from its
1141 # server the client cause a re-registering process. In case of a server, decrease update_time in kown_server_db
1142 # and trigger a re-registering process for servers
1143 if (defined $msg_source && $msg_source =~ /:$server_port$/)
1144 {
1145 daemon_log("$session_id WARNING: Cannot understand incoming msg from server '$msg_source'. Cause re-registration process for servers.", 3);
1146 my $update_statement = "UPDATE $known_server_tn SET update_time='19700101000000' WHERE hostname='$msg_source'";
1147 daemon_log("$session_id DEBUG: $update_statement", 7);
1148 my $upadte_res = $known_server_db->exec_statement($update_statement);
1149 $kernel->yield("register_at_foreign_servers");
1150 }
1151 elsif (defined $msg_source)
1152 {
1153 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);
1154 #my $remote_ip = $heap->{'remote_ip'};
1155 #my $remote_port = $heap->{'remote_port'};
1156 my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source><target>$msg_source</target></xml>";
1157 my ($test_error, $test_error_string) = &send_msg_to_target($ping_msg, "$msg_source", "dummy-key", "gosa_ping", $session_id);
1158 daemon_log("$session_id WARNING: sending msg to cause re-registering: $ping_msg", 3);
1159 }
1160 else
1161 {
1162 my $foreign_host = defined $msg_source ? $msg_source : $heap->{'remote_ip'};
1163 daemon_log("$session_id ERROR: incoming message from host '$foreign_host' cannot be understood. Processing aborted: $tmp_next_msg", 1);
1164 }
1166 $error++
1167 }
1170 my $header;
1171 my $target;
1172 my $source;
1173 my $done = 0;
1174 my $sql;
1175 my $res;
1177 # check whether this message should be processed here
1178 if ($error == 0) {
1179 $header = @{$msg_hash->{'header'}}[0];
1180 $target = @{$msg_hash->{'target'}}[0];
1181 $source = @{$msg_hash->{'source'}}[0];
1182 my $not_found_in_known_clients_db = 0;
1183 my $not_found_in_known_server_db = 0;
1184 my $not_found_in_foreign_clients_db = 0;
1185 my $local_address;
1186 my $local_mac;
1187 my ($target_ip, $target_port) = split(':', $target);
1189 # Determine the local ip address if target is an ip address
1190 if ($target =~ /^\d+\.\d+\.\d+\.\d+:\d+$/) {
1191 $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1192 } else {
1193 $local_address = $server_address;
1194 }
1196 # Determine the local mac address if target is a mac address
1197 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) {
1198 my $loc_ip = &get_local_ip_for_remote_ip($heap->{'remote_ip'});
1199 my $network_interface= &get_interface_for_ip($loc_ip);
1200 $local_mac = &get_mac_for_interface($network_interface);
1201 } else {
1202 $local_mac = $server_mac_address;
1203 }
1205 # target and source is equal to GOSA -> process here
1206 if (not $done) {
1207 if ($target eq "GOSA" && $source eq "GOSA") {
1208 $done = 1;
1209 &daemon_log("$session_id DEBUG: target and source is 'GOSA' -> process '$header' here", 11);
1210 }
1211 }
1213 # target is own address without forward_to_gosa-tag -> process here
1214 if (not $done) {
1215 #if ((($target eq $local_address) || ($target eq $local_mac) ) && (not exists $msg_hash->{'forward_to_gosa'})) {
1216 if (($target eq $local_address) && (not exists $msg_hash->{'forward_to_gosa'})) {
1217 $done = 1;
1218 if ($source eq "GOSA") {
1219 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1220 }
1221 &daemon_log("$session_id DEBUG: target is own address without forward_to_gosa-tag -> process '$header' here", 11);
1222 }
1223 }
1225 # target is own address with forward_to_gosa-tag not pointing to myself -> process here
1226 if (not $done) {
1227 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1228 my $gosa_at;
1229 my $gosa_session_id;
1230 if (($target eq $local_address) && (defined $forward_to_gosa)){
1231 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1232 if ($gosa_at ne $local_address) {
1233 $done = 1;
1234 &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag not pointing to myself -> process '$header' here", 11);
1235 }
1236 }
1237 }
1239 # Target is a client address and there is a processing function within a plugin -> process loaclly
1240 if (not $done)
1241 {
1242 # Check if target is a client address
1243 $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1244 $res = $known_clients_db->select_dbentry($sql);
1245 if ((keys(%$res) > 0) )
1246 {
1247 my $hostname = $res->{1}->{'hostname'};
1248 my $reduced_header = $header;
1249 $reduced_header =~ s/gosa_//;
1250 # Check if there is a processing function within a plugin
1251 if (exists $known_functions->{$reduced_header})
1252 {
1253 $msg =~ s/<target>\S*<\/target>/<target>$hostname<\/target>/;
1254 $done = 1;
1255 &daemon_log("$session_id DEBUG: Target is client address with processing function within a plugin -> process '$header' here", 11);
1256 }
1257 }
1258 }
1260 # If header has a 'job_' prefix, do always process message locally
1261 # which means put it into job queue
1262 if ((not $done) && ($header =~ /job_/))
1263 {
1264 $done = 1;
1265 &daemon_log("$session_id DEBUG: Header has a 'job_' prefix. Put it into job queue. -> process '$header' here", 11);
1266 }
1268 # if message should be processed here -> add message to incoming_db
1269 if ($done) {
1270 # if a 'job_' or a 'gosa_' message comes from a foreign server, fake module from
1271 # ServerPackages to GosaPackages so gosa-si-server knows how to process this kind of messages
1272 if ($header =~ /^gosa_/ || $header =~ /^job_/) {
1273 $module = "GosaPackages";
1274 }
1276 my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1277 primkey=>[],
1278 headertag=>$header,
1279 targettag=>$target,
1280 xmlmessage=>&encode_base64($msg),
1281 timestamp=>&get_time,
1282 module=>$module,
1283 sessionid=>$session_id,
1284 } );
1286 }
1288 # target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa
1289 if (not $done) {
1290 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1291 my $gosa_at;
1292 my $gosa_session_id;
1293 if (($target eq $local_address) && (defined $forward_to_gosa)){
1294 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1295 if ($gosa_at eq $local_address) {
1296 my $session_reference = $kernel->ID_id_to_session($gosa_session_id);
1297 if( defined $session_reference ) {
1298 $heap = $session_reference->get_heap();
1299 }
1300 if(exists $heap->{'client'}) {
1301 $msg = &encrypt_msg($msg, $GosaPackages_key);
1302 $heap->{'client'}->put($msg);
1303 &daemon_log("$session_id DEBUG: incoming '$header' message forwarded to GOsa", 11);
1304 }
1305 $done = 1;
1306 &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag pointing at myself -> forward '$header' to gosa", 11);
1307 }
1308 }
1310 }
1312 # target is a client address in known_clients -> forward to client
1313 if (not $done) {
1314 $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1315 $res = $known_clients_db->select_dbentry($sql);
1316 if (keys(%$res) > 0)
1317 {
1318 $done = 1;
1319 &daemon_log("$session_id DEBUG: target is a client address in known_clients -> forward '$header' to client", 11);
1320 my $hostkey = $res->{1}->{'hostkey'};
1321 my $hostname = $res->{1}->{'hostname'};
1322 $msg =~ s/<target>\S*<\/target>/<target>$hostname<\/target>/;
1323 $msg =~ s/<header>gosa_/<header>/;
1324 my $error= &send_msg_to_target($msg, $hostname, $hostkey, $header, $session_id);
1325 if ($error) {
1326 &daemon_log("$session_id ERROR: Some problems occurred while trying to send msg to client '$hostkey': $msg", 1);
1327 }
1328 }
1329 else
1330 {
1331 $not_found_in_known_clients_db = 1;
1332 }
1333 }
1335 # target is a client address in foreign_clients -> forward to registration server
1336 if (not $done) {
1337 $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1338 $res = $foreign_clients_db->select_dbentry($sql);
1339 if (keys(%$res) > 0) {
1340 my $hostname = $res->{1}->{'hostname'};
1341 my ($host_ip, $host_port) = split(/:/, $hostname);
1342 my $local_address = &get_local_ip_for_remote_ip($host_ip).":$server_port";
1343 my $regserver = $res->{1}->{'regserver'};
1344 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$regserver'";
1345 my $res = $known_server_db->select_dbentry($sql);
1346 if (keys(%$res) > 0) {
1347 my $regserver_key = $res->{1}->{'hostkey'};
1348 $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1349 $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1350 if ($source eq "GOSA") {
1351 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1352 }
1353 my $error= &send_msg_to_target($msg, $regserver, $regserver_key, $header, $session_id);
1354 if ($error) {
1355 &daemon_log("$session_id ERROR: some problems occurred while trying to send msg to registration server: $msg", 1);
1356 }
1357 }
1358 $done = 1;
1359 &daemon_log("$session_id DEBUG: target is a client address in foreign_clients -> forward '$header' to registration server", 11);
1360 } else {
1361 $not_found_in_foreign_clients_db = 1;
1362 }
1363 }
1365 # target is a server address -> forward to server
1366 if (not $done) {
1367 $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1368 $res = $known_server_db->select_dbentry($sql);
1369 if (keys(%$res) > 0) {
1370 my $hostkey = $res->{1}->{'hostkey'};
1372 if ($source eq "GOSA") {
1373 $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1374 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1376 }
1378 &send_msg_to_target($msg, $target, $hostkey, $header, $session_id);
1379 $done = 1;
1380 &daemon_log("$session_id DEBUG: target is a server address -> forward '$header' to server", 11);
1381 } else {
1382 $not_found_in_known_server_db = 1;
1383 }
1384 }
1387 # target is not in foreign_clients_db, known_server_db or known_clients_db, maybe it is a complete new one -> process here
1388 if ( $not_found_in_foreign_clients_db
1389 && $not_found_in_known_server_db
1390 && $not_found_in_known_clients_db) {
1391 &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);
1392 if ($header =~ /^gosa_/ || $header =~ /^job_/) {
1393 $module = "GosaPackages";
1394 }
1395 my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1396 primkey=>[],
1397 headertag=>$header,
1398 targettag=>$target,
1399 xmlmessage=>&encode_base64($msg),
1400 timestamp=>&get_time,
1401 module=>$module,
1402 sessionid=>$session_id,
1403 } );
1404 $done = 1;
1405 }
1408 if (not $done) {
1409 daemon_log("$session_id ERROR: do not know what to do with this message: $msg", 1);
1410 if ($source eq "GOSA") {
1411 my %data = ('error_msg' => &encode_base64($msg), 'error_string' => "Do not know what to do with this message!");
1412 my $error_msg = &build_msg("error", $local_address, "GOSA", \%data );
1414 my $session_reference = $kernel->ID_id_to_session($session_id);
1415 if( defined $session_reference ) {
1416 $heap = $session_reference->get_heap();
1417 }
1418 if(exists $heap->{'client'}) {
1419 $error_msg = &encrypt_msg($error_msg, $GosaPackages_key);
1420 $heap->{'client'}->put($error_msg);
1421 }
1422 }
1423 }
1425 }
1427 return;
1428 }
1431 sub next_task {
1432 my ($session, $heap, $task) = @_[SESSION, HEAP, ARG0, ARG1];
1433 my $running_task = POE::Wheel::Run->new(
1434 Program => sub { process_task($session, $heap, $task) },
1435 StdioFilter => POE::Filter::Reference->new(),
1436 StdoutEvent => "task_result",
1437 StderrEvent => "task_debug",
1438 CloseEvent => "task_done",
1439 );
1440 $heap->{task}->{ $running_task->ID } = $running_task;
1441 }
1443 sub handle_task_result {
1444 my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
1445 my $client_answer = $result->{'answer'};
1446 if( $client_answer =~ s/session_id=(\d+)$// ) {
1447 my $session_id = $1;
1448 if( defined $session_id ) {
1449 my $session_reference = $kernel->ID_id_to_session($session_id);
1450 if( defined $session_reference ) {
1451 $heap = $session_reference->get_heap();
1452 }
1453 }
1455 if(exists $heap->{'client'}) {
1456 $heap->{'client'}->put($client_answer);
1457 }
1458 }
1459 $kernel->sig(CHLD => "child_reap");
1460 }
1462 sub handle_task_debug {
1463 my $result = $_[ARG0];
1464 print STDERR "$result\n";
1465 }
1467 sub handle_task_done {
1468 my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1469 delete $heap->{task}->{$task_id};
1470 if (exists $heap->{ldap_handle}->{$task_id}) {
1471 &release_ldap_handle($heap->{ldap_handle}->{$task_id});
1472 }
1473 }
1475 sub process_task {
1476 no strict "refs";
1477 #CHECK: Not @_[...]?
1478 my ($session, $heap, $task) = @_;
1479 my $error = 0;
1480 my $answer_l;
1481 my ($answer_header, @answer_target_l, $answer_source);
1482 my $client_answer = "";
1484 # prepare all variables needed to process message
1485 #my $msg = $task->{'xmlmessage'};
1486 my $msg = &decode_base64($task->{'xmlmessage'});
1487 my $incoming_id = $task->{'id'};
1488 my $module = $task->{'module'};
1489 my $header = $task->{'headertag'};
1490 my $session_id = $task->{'sessionid'};
1491 my $msg_hash;
1492 eval {
1493 $msg_hash = $xml->XMLin($msg, ForceArray=>1);
1494 };
1495 daemon_log("ERROR: XML failure '$@'") if ($@);
1496 my $source = @{$msg_hash->{'source'}}[0];
1498 # set timestamp of incoming client uptodate, so client will not
1499 # be deleted from known_clients because of expiration
1500 my $cur_time = &get_time();
1501 my $sql = "UPDATE $known_clients_tn SET timestamp='$cur_time' WHERE hostname='$source'";
1502 my $res = $known_clients_db->exec_statement($sql);
1504 ######################
1505 # process incoming msg
1506 if( $error == 0) {
1507 daemon_log("$session_id INFO: Incoming msg (session_id=$session_id) with header '".@{$msg_hash->{'header'}}[0]."'", 5);
1508 daemon_log("$session_id DEBUG: Processing module ".$module, 26);
1509 $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1511 if ( 0 < @{$answer_l} ) {
1512 my $answer_str = join("\n", @{$answer_l});
1513 my @headers;
1514 while ($answer_str =~ /<header>(\w+)<\/header>/g) {
1515 push(@headers, $1);
1516 }
1517 daemon_log("$session_id INFO: got answer message(s) with header: '".join("', '", @headers)."'", 5);
1518 daemon_log("$session_id DEBUG: $module: got answer from module: \n".$answer_str,26);
1519 } else {
1520 daemon_log("$session_id DEBUG: $module: got no answer from module!" ,26);
1521 }
1523 }
1524 if( !$answer_l ) { $error++ };
1526 ########
1527 # answer
1528 if( $error == 0 ) {
1530 foreach my $answer ( @{$answer_l} ) {
1531 # check outgoing msg to xml validity
1532 my ($answer, $answer_hash) = &check_outgoing_xml_validity($answer, $session_id);
1533 if( not defined $answer_hash ) { next; }
1535 $answer_header = @{$answer_hash->{'header'}}[0];
1536 @answer_target_l = @{$answer_hash->{'target'}};
1537 $answer_source = @{$answer_hash->{'source'}}[0];
1539 # deliver msg to all targets
1540 foreach my $answer_target ( @answer_target_l ) {
1542 # targets of msg are all gosa-si-clients in known_clients_db
1543 if( $answer_target eq "*" ) {
1544 # answer is for all clients
1545 my $sql_statement= "SELECT * FROM known_clients";
1546 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1547 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1548 my $host_name = $hit->{hostname};
1549 my $host_key = $hit->{hostkey};
1550 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1551 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1552 }
1553 }
1555 # targets of msg are all gosa-si-server in known_server_db
1556 elsif( $answer_target eq "KNOWN_SERVER" ) {
1557 # answer is for all server in known_server
1558 my $sql_statement= "SELECT * FROM $known_server_tn";
1559 my $query_res = $known_server_db->select_dbentry( $sql_statement );
1560 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1561 my $host_name = $hit->{hostname};
1562 my $host_key = $hit->{hostkey};
1563 $answer =~ s/<target>\S+<\/target>/<target>$host_name<\/target>/g;
1564 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1565 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1566 }
1567 }
1569 # target of msg is GOsa
1570 elsif( $answer_target eq "GOSA" ) {
1571 my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1572 my $add_on = "";
1573 if( defined $session_id ) {
1574 $add_on = ".session_id=$session_id";
1575 }
1576 # answer is for GOSA and has to returned to connected client
1577 my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1578 $client_answer = $gosa_answer.$add_on;
1579 }
1581 # target of msg is job queue at this host
1582 elsif( $answer_target eq "JOBDB") {
1583 $answer =~ /<header>(\S+)<\/header>/;
1584 my $header;
1585 if( defined $1 ) { $header = $1; }
1586 my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1587 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1588 }
1590 # Target of msg is a mac address
1591 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 ) {
1592 daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients and foreign_clients", 5);
1594 # Looking for macaddress in known_clients
1595 my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1596 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1597 my $found_ip_flag = 0;
1598 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1599 my $host_name = $hit->{hostname};
1600 my $host_key = $hit->{hostkey};
1601 $answer =~ s/$answer_target/$host_name/g;
1602 daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1603 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1604 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1605 $found_ip_flag++ ;
1606 }
1608 # Looking for macaddress in foreign_clients
1609 if ($found_ip_flag == 0) {
1610 my $sql = "SELECT * FROM $foreign_clients_tn WHERE macaddress LIKE '$answer_target'";
1611 my $res = $foreign_clients_db->select_dbentry($sql);
1612 while( my ($hit_num, $hit) = each %{ $res } ) {
1613 my $host_name = $hit->{hostname};
1614 my $reg_server = $hit->{regserver};
1615 daemon_log("$session_id INFO: found host '$host_name' with mac '$answer_target', registered at '$reg_server'", 5);
1617 # Fetch key for reg_server
1618 my $reg_server_key;
1619 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$reg_server'";
1620 my $res = $known_server_db->select_dbentry($sql);
1621 if (exists $res->{1}) {
1622 $reg_server_key = $res->{1}->{'hostkey'};
1623 } else {
1624 daemon_log("$session_id ERROR: cannot find hostkey for '$host_name' in '$known_server_tn'", 1);
1625 daemon_log("$session_id ERROR: unable to forward answer to correct registration server, processing is aborted!", 1);
1626 $reg_server_key = undef;
1627 }
1629 # Send answer to server where client is registered
1630 if (defined $reg_server_key) {
1631 $answer =~ s/$answer_target/$host_name/g;
1632 my $error = &send_msg_to_target($answer, $reg_server, $reg_server_key, $answer_header, $session_id);
1633 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1634 $found_ip_flag++ ;
1635 }
1636 }
1637 }
1639 # No mac to ip matching found
1640 if( $found_ip_flag == 0) {
1641 daemon_log("$session_id WARNING: no host found in known_clients or foreign_clients with mac address '$answer_target'", 3);
1642 &reactivate_job_with_delay($session_id, $answer_target, $answer_header, 30);
1643 }
1645 # Answer is for one specific host
1646 } else {
1647 # get encrypt_key
1648 my $encrypt_key = &get_encrypt_key($answer_target);
1649 if( not defined $encrypt_key ) {
1650 # unknown target
1651 daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1652 next;
1653 }
1654 my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1655 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1656 }
1657 }
1658 }
1659 }
1661 my $filter = POE::Filter::Reference->new();
1662 my %result = (
1663 status => "seems ok to me",
1664 answer => $client_answer,
1665 );
1667 my $output = $filter->put( [ \%result ] );
1668 print @$output;
1671 }
1673 sub session_start {
1674 my ($kernel) = $_[KERNEL];
1675 $global_kernel = $kernel;
1676 $kernel->yield('register_at_foreign_servers');
1677 $kernel->yield('create_fai_server_db', $fai_server_tn );
1678 $kernel->yield('create_fai_release_db', $fai_release_tn );
1679 $kernel->yield('watch_for_next_tasks');
1680 $kernel->sig(USR1 => "sig_handler");
1681 $kernel->sig(USR2 => "recreate_packages_db");
1682 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1683 $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay);
1684 $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay);
1685 $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1686 $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1687 $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1688 $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1690 # Start opsi check
1691 if ($opsi_enabled eq "true") {
1692 $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay);
1693 }
1695 }
1698 sub watch_for_done_jobs {
1699 #CHECK: $heap for what?
1700 my ($kernel,$heap) = @_[KERNEL, HEAP];
1702 my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((status='done') AND (modified='0'))";
1703 my $res = $job_db->select_dbentry( $sql_statement );
1705 while( my ($id, $hit) = each %{$res} ) {
1706 my $jobdb_id = $hit->{id};
1707 my $sql_statement = "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1708 my $res = $job_db->del_dbentry($sql_statement);
1709 }
1711 $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1712 }
1715 sub watch_for_opsi_jobs {
1716 my ($kernel) = $_[KERNEL];
1718 # 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
1719 # opsi install job is to parse the xml message. There is still the correct header.
1720 my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((xmlmessage LIKE '%opsi_install_client</header>%') AND (status='processing') AND (siserver='localhost'))";
1721 my $res = $job_db->select_dbentry( $sql_statement );
1723 # Ask OPSI for an update of the running jobs
1724 while (my ($id, $hit) = each %$res ) {
1725 # Determine current parameters of the job
1726 my $hostId = $hit->{'plainname'};
1727 my $macaddress = $hit->{'macaddress'};
1728 my $progress = $hit->{'progress'};
1730 my $result= {};
1732 # For hosts, only return the products that are or get installed
1733 my $callobj;
1734 $callobj = {
1735 method => 'getProductStates_hash',
1736 params => [ $hostId ],
1737 id => 1,
1738 };
1740 my $hres = $opsi_client->call($opsi_url, $callobj);
1741 #my ($hres_err, $hres_err_string) = &check_opsi_res($hres);
1742 if (not &check_opsi_res($hres)) {
1743 my $htmp= $hres->result->{$hostId};
1745 # Check state != not_installed or action == setup -> load and add
1746 my $products= 0;
1747 my $installed= 0;
1748 my $installing = 0;
1749 my $error= 0;
1750 my @installed_list;
1751 my @error_list;
1752 my $act_status = "none";
1753 foreach my $product (@{$htmp}){
1755 if ($product->{'installationStatus'} ne "not_installed" or
1756 $product->{'actionRequest'} eq "setup"){
1758 # Increase number of products for this host
1759 $products++;
1761 if ($product->{'installationStatus'} eq "failed"){
1762 $result->{$product->{'productId'}}= "error";
1763 unshift(@error_list, $product->{'productId'});
1764 $error++;
1765 }
1766 if ($product->{'installationStatus'} eq "installed" && $product->{'actionRequest'} eq "none"){
1767 $result->{$product->{'productId'}}= "installed";
1768 unshift(@installed_list, $product->{'productId'});
1769 $installed++;
1770 }
1771 if ($product->{'installationStatus'} eq "installing"){
1772 $result->{$product->{'productId'}}= "installing";
1773 $installing++;
1774 $act_status = "installing - ".$product->{'productId'};
1775 }
1776 }
1777 }
1779 # Estimate "rough" progress, avoid division by zero
1780 if ($products == 0) {
1781 $result->{'progress'}= 0;
1782 } else {
1783 $result->{'progress'}= int($installed * 100 / $products);
1784 }
1786 # Set updates in job queue
1787 if ((not $error) && (not $installing) && ($installed)) {
1788 $act_status = "installed - ".join(", ", @installed_list);
1789 }
1790 if ($error) {
1791 $act_status = "error - ".join(", ", @error_list);
1792 }
1793 if ($progress ne $result->{'progress'} ) {
1794 # Updating progress and result
1795 my $update_statement = "UPDATE $job_queue_tn SET modified='1', progress='".$result->{'progress'}."', result='$act_status' WHERE macaddress LIKE '$macaddress' AND siserver='localhost'";
1796 my $update_res = $job_db->update_dbentry($update_statement);
1797 }
1798 if ($progress eq 100) {
1799 # Updateing status
1800 my $done_statement = "UPDATE $job_queue_tn SET modified='1', ";
1801 if ($error) {
1802 $done_statement .= "status='error'";
1803 } else {
1804 $done_statement .= "status='done'";
1805 }
1806 $done_statement .= " WHERE macaddress LIKE '$macaddress' AND siserver='localhost'";
1807 my $done_res = $job_db->update_dbentry($done_statement);
1808 }
1811 }
1812 }
1814 $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay);
1815 }
1818 # If a job got an update or was modified anyway, send to all other si-server an update message of this jobs.
1819 sub watch_for_modified_jobs {
1820 my ($kernel,$heap) = @_[KERNEL, HEAP];
1822 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE (modified='1')";
1823 my $res = $job_db->select_dbentry( $sql_statement );
1825 # if db contains no jobs which should be update, do nothing
1826 if (keys %$res != 0) {
1828 if ($job_synchronization eq "true") {
1829 # make out of the db result a gosa-si message
1830 my $update_msg = &db_res2si_msg ($res, "foreign_job_updates", "KNOWN_SERVER", "MY_LOCAL_ADDRESS");
1832 # update all other SI-server
1833 &inform_all_other_si_server($update_msg);
1834 }
1836 # set jobs all jobs to modified = 0, wait until the next modification for updates of other si-server
1837 $sql_statement = "UPDATE $job_queue_tn SET modified='0' ";
1838 $res = $job_db->update_dbentry($sql_statement);
1839 }
1841 $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay);
1842 }
1845 sub watch_for_new_jobs {
1846 if($watch_for_new_jobs_in_progress == 0) {
1847 $watch_for_new_jobs_in_progress = 1;
1848 my ($kernel,$heap) = @_[KERNEL, HEAP];
1850 # check gosa job queue for jobs with executable timestamp
1851 my $timestamp = &get_time();
1852 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE siserver='localhost' AND status='waiting' AND (CAST(timestamp AS UNSIGNED)) < $timestamp ORDER BY timestamp";
1853 my $res = $job_db->exec_statement( $sql_statement );
1855 # Merge all new jobs that would do the same actions
1856 my @drops;
1857 my $hits;
1858 foreach my $hit (reverse @{$res} ) {
1859 my $macaddress= lc @{$hit}[8];
1860 my $headertag= @{$hit}[5];
1861 if(
1862 defined($hits->{$macaddress}) &&
1863 defined($hits->{$macaddress}->{$headertag}) &&
1864 defined($hits->{$macaddress}->{$headertag}[0])
1865 ) {
1866 push @drops, "DELETE FROM $job_queue_tn WHERE id = $hits->{$macaddress}->{$headertag}[0]";
1867 }
1868 $hits->{$macaddress}->{$headertag}= $hit;
1869 }
1871 # Delete new jobs with a matching job in state 'processing'
1872 foreach my $macaddress (keys %{$hits}) {
1873 foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1874 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1875 if(defined($jobdb_id)) {
1876 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND headertag='$jobdb_headertag' AND status='processing'";
1877 my $res = $job_db->exec_statement( $sql_statement );
1878 foreach my $hit (@{$res}) {
1879 push @drops, "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1880 }
1881 } else {
1882 daemon_log("J ERROR: Job without id exists for macaddress $macaddress!", 1);
1883 }
1884 }
1885 }
1887 # Commit deletion
1888 $job_db->exec_statementlist(\@drops);
1890 # Look for new jobs that could be executed
1891 foreach my $macaddress (keys %{$hits}) {
1893 # Look if there is an executing job
1894 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='processing'";
1895 my $res = $job_db->exec_statement( $sql_statement );
1897 # Skip new jobs for host if there is a processing job
1898 if(defined($res) and defined @{$res}[0]) {
1899 # Prevent race condition if there is a trigger_activate job waiting and a goto-activation job processing
1900 my $row = @{$res}[0] if (ref $res eq 'ARRAY');
1901 if(@{$row}[5] eq 'trigger_action_reinstall') {
1902 my $sql_statement_2 = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='waiting' AND headertag = 'trigger_activate_new'";
1903 my $res_2 = $job_db->exec_statement( $sql_statement_2 );
1904 if(defined($res_2) and defined @{$res_2}[0]) {
1905 # Set status from goto-activation to 'waiting' and update timestamp
1906 $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'");
1907 }
1908 }
1909 next;
1910 }
1912 foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1913 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1914 if(defined($jobdb_id)) {
1915 my $job_msg = @{$hits->{$macaddress}->{$jobdb_headertag}}[7];
1917 daemon_log("J DEBUG: its time to execute $job_msg", 7);
1918 my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1919 my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1921 # expect macaddress is unique!!!!!!
1922 my $target = $res_hash->{1}->{hostname};
1924 # change header
1925 $job_msg =~ s/<header>job_/<header>gosa_/;
1927 # add sqlite_id
1928 $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1930 $job_msg =~ /<header>(\S+)<\/header>/;
1931 my $header = $1 ;
1932 my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");
1934 # update status in job queue to ...
1935 # ... 'processing', for jobs: 'reinstall', 'update'
1936 if (($header =~ /gosa_trigger_action_reinstall/)
1937 || ($header =~ /gosa_trigger_activate_new/)
1938 || ($header =~ /gosa_trigger_action_update/)) {
1939 my $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
1940 my $dbres = $job_db->update_dbentry($sql_statement);
1941 }
1943 # ... 'done', for all other jobs, they are no longer needed in the jobqueue
1944 else {
1945 my $sql_statement = "UPDATE $job_queue_tn SET status='done' WHERE id=$jobdb_id";
1946 my $dbres = $job_db->update_dbentry($sql_statement);
1947 }
1950 # We don't want parallel processing
1951 last;
1952 }
1953 }
1954 }
1956 $watch_for_new_jobs_in_progress = 0;
1957 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1958 }
1959 }
1962 sub watch_for_new_messages {
1963 my ($kernel,$heap) = @_[KERNEL, HEAP];
1964 my @coll_user_msg; # collection list of outgoing messages
1966 # check messaging_db for new incoming messages with executable timestamp
1967 my $timestamp = &get_time();
1968 my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS UNSIGNED))<$timestamp AND flag='n' AND direction='in' )";
1969 my $res = $messaging_db->exec_statement( $sql_statement );
1970 foreach my $hit (@{$res}) {
1972 # create outgoing messages
1973 my $message_to = @{$hit}[3];
1974 # translate message_to to plain login name
1975 my @message_to_l = split(/,/, $message_to);
1976 my %receiver_h;
1977 foreach my $receiver (@message_to_l) {
1978 if ($receiver =~ /^u_([\s\S]*)$/) {
1979 $receiver_h{$1} = 0;
1980 } elsif ($receiver =~ /^g_([\s\S]*)$/) {
1981 my $group_name = $1;
1982 # fetch all group members from ldap and add them to receiver hash
1983 my $ldap_handle = &get_ldap_handle();
1984 if (defined $ldap_handle) {
1985 my $mesg = $ldap_handle->search(
1986 base => $ldap_base,
1987 scope => 'sub',
1988 attrs => ['memberUid'],
1989 filter => "cn=$group_name",
1990 );
1991 if ($mesg->count) {
1992 my @entries = $mesg->entries;
1993 foreach my $entry (@entries) {
1994 my @receivers= $entry->get_value("memberUid");
1995 foreach my $receiver (@receivers) {
1996 $receiver_h{$receiver} = 0;
1997 }
1998 }
1999 }
2000 # translating errors ?
2001 if ($mesg->code) {
2002 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: $mesg->error", 1);
2003 }
2004 &release_ldap_handle($ldap_handle);
2005 # ldap handle error ?
2006 } else {
2007 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: no ldap handle available", 1);
2008 }
2009 } else {
2010 my $sbjct = &encode_base64(@{$hit}[1]);
2011 my $msg = &encode_base64(@{$hit}[7]);
2012 &daemon_log("M WARNING: unknown receiver '$receiver' for a user-message '$sbjct - $msg'", 3);
2013 }
2014 }
2015 my @receiver_l = keys(%receiver_h);
2017 my $message_id = @{$hit}[0];
2019 #add each outgoing msg to messaging_db
2020 my $receiver;
2021 foreach $receiver (@receiver_l) {
2022 my $sql_statement = "INSERT INTO $messaging_tn (id, subject, message_from, message_to, flag, direction, delivery_time, message, timestamp) ".
2023 "VALUES ('".
2024 $message_id."', '". # id
2025 @{$hit}[1]."', '". # subject
2026 @{$hit}[2]."', '". # message_from
2027 $receiver."', '". # message_to
2028 "none"."', '". # flag
2029 "out"."', '". # direction
2030 @{$hit}[6]."', '". # delivery_time
2031 @{$hit}[7]."', '". # message
2032 $timestamp."'". # timestamp
2033 ")";
2034 &daemon_log("M DEBUG: $sql_statement", 1);
2035 my $res = $messaging_db->exec_statement($sql_statement);
2036 &daemon_log("M INFO: message '".@{$hit}[0]."' is prepared for delivery to receiver '$receiver'", 5);
2037 }
2039 # set incoming message to flag d=deliverd
2040 $sql_statement = "UPDATE $messaging_tn SET flag='p' WHERE id='$message_id'";
2041 &daemon_log("M DEBUG: $sql_statement", 7);
2042 $res = $messaging_db->update_dbentry($sql_statement);
2043 &daemon_log("M INFO: message '$message_id' is set to flag 'p' (processed)", 5);
2044 }
2046 $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
2047 return;
2048 }
2050 sub watch_for_delivery_messages {
2051 my ($kernel, $heap) = @_[KERNEL, HEAP];
2053 # select outgoing messages
2054 my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( flag='p' AND direction='out' )";
2055 my $res = $messaging_db->exec_statement( $sql_statement );
2057 # build out msg for each usr
2058 foreach my $hit (@{$res}) {
2059 my $receiver = @{$hit}[3];
2060 my $msg_id = @{$hit}[0];
2061 my $subject = @{$hit}[1];
2062 my $message = @{$hit}[7];
2064 # resolve usr -> host where usr is logged in
2065 my $sql = "SELECT * FROM $login_users_tn WHERE (user='$receiver')";
2066 my $res = $login_users_db->exec_statement($sql);
2068 # receiver is logged in nowhere
2069 if (not ref(@$res[0]) eq "ARRAY") { next; }
2071 # receiver ist logged in at a client registered at local server
2072 my $send_succeed = 0;
2073 foreach my $hit (@$res) {
2074 my $receiver_host = @$hit[0];
2075 my $delivered2host = 0;
2076 &daemon_log("M DEBUG: user '$receiver' is logged in at host '$receiver_host'", 7);
2078 # Looking for host in know_clients_db
2079 my $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$receiver_host')";
2080 my $res = $known_clients_db->exec_statement($sql);
2082 # Host is known in known_clients_db
2083 if (ref(@$res[0]) eq "ARRAY") {
2084 my $receiver_key = @{@{$res}[0]}[2];
2085 my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
2086 my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data );
2087 my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0);
2088 if ($error == 0 ) {
2089 $send_succeed++ ;
2090 $delivered2host++ ;
2091 &daemon_log("M DEBUG: send message for user '$receiver' to host '$receiver_host'", 7);
2092 } else {
2093 &daemon_log("M DEBUG: cannot send message for user '$receiver' to host '$receiver_host'", 7);
2094 }
2095 }
2097 # Message already send, do not need to do anything more, otherwise ...
2098 if ($delivered2host) { next;}
2100 # ...looking for host in foreign_clients_db
2101 $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$receiver_host')";
2102 $res = $foreign_clients_db->exec_statement($sql);
2104 # Host is known in foreign_clients_db
2105 if (ref(@$res[0]) eq "ARRAY") {
2106 my $registration_server = @{@{$res}[0]}[2];
2108 # Fetch encryption key for registration server
2109 my $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$registration_server')";
2110 my $res = $known_server_db->exec_statement($sql);
2111 if (ref(@$res[0]) eq "ARRAY") {
2112 my $registration_server_key = @{@{$res}[0]}[3];
2113 my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
2114 my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data );
2115 my $error = &send_msg_to_target($out_msg, $registration_server, $registration_server_key, "usr_msg", 0);
2116 if ($error == 0 ) {
2117 $send_succeed++ ;
2118 $delivered2host++ ;
2119 &daemon_log("M DEBUG: send message for user '$receiver' to server '$registration_server'", 7);
2120 } else {
2121 &daemon_log("M ERROR: cannot send message for user '$receiver' to server '$registration_server'", 1);
2122 }
2124 } else {
2125 &daemon_log("M ERROR: host '$receiver_host' is reported to be ".
2126 "registrated at server '$registration_server', ".
2127 "but no data available in known_server_db ", 1);
2128 }
2129 }
2131 if (not $delivered2host) {
2132 &daemon_log("M ERROR: unable to send user message to host '$receiver_host'", 1);
2133 }
2134 }
2136 if ($send_succeed) {
2137 # set outgoing msg at db to deliverd
2138 my $sql = "UPDATE $messaging_tn SET flag='d' WHERE (id='$msg_id' AND direction='out' AND message_to='$receiver')";
2139 my $res = $messaging_db->exec_statement($sql);
2140 &daemon_log("M INFO: send message for user '$receiver' to logged in hosts", 5);
2141 } else {
2142 &daemon_log("M WARNING: failed to deliver message for user '$receiver'", 3);
2143 }
2144 }
2146 $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
2147 return;
2148 }
2151 sub watch_for_done_messages {
2152 my ($kernel,$heap) = @_[KERNEL, HEAP];
2154 my $sql = "SELECT * FROM $messaging_tn WHERE (flag='p' AND direction='in')";
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 my $res = $messaging_db->exec_statement($sql);
2163 # not all usr msgs have been seen till now
2164 if ( ref(@$res[0]) eq "ARRAY") { next; }
2166 $sql = "DELETE FROM $messaging_tn WHERE (id='$msg_id')";
2167 $res = $messaging_db->exec_statement($sql);
2169 }
2171 $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
2172 return;
2173 }
2176 sub watch_for_old_known_clients {
2177 my ($kernel,$heap) = @_[KERNEL, HEAP];
2179 my $sql_statement = "SELECT * FROM $known_clients_tn";
2180 my $res = $known_clients_db->select_dbentry( $sql_statement );
2182 my $cur_time = int(&get_time());
2184 while ( my ($hit_num, $hit) = each %$res) {
2185 my $expired_timestamp = int($hit->{'timestamp'});
2186 $expired_timestamp =~ /(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/;
2187 my $dt = DateTime->new( year => $1,
2188 month => $2,
2189 day => $3,
2190 hour => $4,
2191 minute => $5,
2192 second => $6,
2193 );
2195 $dt->add( seconds => 2 * int($hit->{'keylifetime'}) );
2196 $expired_timestamp = $dt->ymd('').$dt->hms('');
2197 if ($cur_time > $expired_timestamp) {
2198 my $hostname = $hit->{'hostname'};
2199 my $del_sql = "DELETE FROM $known_clients_tn WHERE hostname='$hostname'";
2200 my $del_res = $known_clients_db->exec_statement($del_sql);
2202 &main::daemon_log("0 INFO: timestamp '".$hit->{'timestamp'}."' of client '$hostname' is expired('$expired_timestamp'), client will be deleted from known_clients_db", 5);
2203 }
2205 }
2207 $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
2208 }
2211 sub watch_for_next_tasks {
2212 my ($kernel,$heap) = @_[KERNEL, HEAP];
2214 my $sql = "SELECT * FROM $incoming_tn";
2215 my $res = $incoming_db->select_dbentry($sql);
2217 while ( my ($hit_num, $hit) = each %$res) {
2218 my $headertag = $hit->{'headertag'};
2219 if ($headertag =~ /^answer_(\d+)/) {
2220 # do not start processing, this message is for a still running POE::Wheel
2221 next;
2222 }
2223 my $message_id = $hit->{'id'};
2224 my $session_id = $hit->{'sessionid'};
2225 &daemon_log("$session_id DEBUG: start processing for message with incoming id: '$message_id'", 11);
2227 $kernel->yield('next_task', $hit);
2229 my $sql = "DELETE FROM $incoming_tn WHERE id=$message_id";
2230 my $res = $incoming_db->exec_statement($sql);
2231 }
2233 $kernel->delay_set('watch_for_next_tasks', 1);
2234 }
2237 sub get_ldap_handle {
2238 my ($session_id) = @_;
2239 my $heap;
2241 if (not defined $session_id ) { $session_id = 0 };
2242 if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
2244 my ($package, $file, $row, $subroutine, $hasArgs, $wantArray, $evalText, $isRequire) = caller(1);
2245 my $caller_text = "subroutine $subroutine";
2246 if ($subroutine eq "(eval)") {
2247 $caller_text = "eval block within file '$file' for '$evalText'";
2248 }
2249 daemon_log("$session_id DEBUG: new ldap handle for '$caller_text' required!", 42);
2251 get_handle:
2252 my $ldap_handle = Net::LDAP->new( $ldap_uri );
2253 if (not ref $ldap_handle) {
2254 daemon_log("$session_id ERROR: Connection to LDAP URI '$ldap_uri' failed! Retrying!", 1);
2255 usleep(100000);
2256 goto get_handle;
2257 } else {
2258 daemon_log("$session_id DEBUG: Connection to LDAP URI '$ldap_uri' established.", 42);
2259 }
2261 $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);
2262 return $ldap_handle;
2263 }
2266 sub release_ldap_handle {
2267 my ($ldap_handle, $session_id) = @_ ;
2268 if (not defined $session_id ) { $session_id = 0 };
2270 if(ref $ldap_handle) {
2271 $ldap_handle->disconnect();
2272 }
2273 &main::daemon_log("$session_id DEBUG: Released a ldap handle!", 42);
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'", 42);
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, $session_id);
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, $session_id);
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,138);
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",138);
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",138);
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", 138);
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", 138);
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 my $block_res = $known_server_db->exec_statement($block_statement);
3254 # Fetch all si-server from db where update_time is younger than act_time
3255 my $fetch_statement = "SELECT * FROM $known_server_tn WHERE update_time='19700101000000'";
3256 my $fetch_res = $known_server_db->exec_statement($fetch_statement);
3258 # Detect already connected clients. Will be added to registration msg later.
3259 my $client_sql = "SELECT * FROM $known_clients_tn";
3260 my $client_res = $known_clients_db->exec_statement($client_sql);
3262 # Send registration messag to all fetched si-server
3263 foreach my $hit (@$fetch_res) {
3264 my $hostname = @$hit[0];
3265 my $hostkey = &create_passwd;
3267 # Add already connected clients to registration message
3268 my $myhash = &create_xml_hash('new_server', $server_address, $hostname);
3269 &add_content2xml_hash($myhash, 'key', $hostkey);
3270 map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res);
3272 # Add locally loaded gosa-si modules to registration message
3273 my $loaded_modules = {};
3274 while (my ($package, $pck_info) = each %$known_modules) {
3275 next if ((!defined(@$pck_info[2])) || (!(ref (@$pck_info[2]) eq 'HASH')));
3276 foreach my $act_module (keys(%{@$pck_info[2]})) {
3277 $loaded_modules->{$act_module} = "";
3278 }
3279 }
3280 map(&add_content2xml_hash($myhash, "loaded_modules", $_), keys(%$loaded_modules));
3282 # Add macaddress to registration message
3283 my ($host_ip, $host_port) = split(/:/, $hostname);
3284 my $local_ip = &get_local_ip_for_remote_ip($host_ip);
3285 my $network_interface= &get_interface_for_ip($local_ip);
3286 my $host_mac = &get_mac_for_interface($network_interface);
3287 &add_content2xml_hash($myhash, 'macaddress', $host_mac);
3289 # Build registration message and send it
3290 my $foreign_server_msg = &create_xml_string($myhash);
3291 my $error = &send_msg_to_target($foreign_server_msg, $hostname, $ServerPackages_key, "new_server", 0);
3292 }
3295 # After n sec perform a check of all server registration processes
3296 $kernel->delay_set("control_server_registration", 2);
3298 return;
3299 }
3302 sub control_server_registration {
3303 my ($kernel) = $_[KERNEL];
3305 # Check if all registration processes succeed or not
3306 my $select_statement = "SELECT * FROM $known_server_tn WHERE status='new_server'";
3307 my $select_res = $known_server_db->exec_statement($select_statement);
3309 # If at least one registration process failed, maybe in case of a race condition
3310 # with a foreign registration process
3311 if (@$select_res > 0)
3312 {
3313 # Release block statement 'new_server' to make the server accessible
3314 # for foreign registration processes
3315 my $update_statement = "UPDATE $known_server_tn SET status='waiting' WHERE status='new_server'";
3316 my $update_res = $known_server_db->exec_statement($update_statement);
3318 # Set a random delay to avoid the registration race condition
3319 my $new_foreign_servers_register_delay = int(rand(4))+1;
3320 $kernel->delay_set("register_at_foreign_servers", $new_foreign_servers_register_delay);
3321 }
3322 # If all registration processes succeed
3323 else
3324 {
3325 $kernel->delay_set("register_at_foreign_servers", $foreign_servers_register_delay);
3326 }
3328 return;
3329 }
3332 #==== MAIN = main ==============================================================
3333 # parse commandline options
3334 Getopt::Long::Configure( "bundling" );
3335 GetOptions("h|help" => \&usage,
3336 "c|config=s" => \$cfg_file,
3337 "f|foreground" => \$foreground,
3338 "v|verbose+" => \$verbose,
3339 "no-arp+" => \$no_arp,
3340 "d=s" => \$debug_parts,
3341 ) or (&usage("", 1)&&(exit(-1)));
3343 # read and set config parameters
3344 &check_cmdline_param ;
3345 &read_configfile($cfg_file, %cfg_defaults);
3346 &check_pid;
3348 $SIG{CHLD} = 'IGNORE';
3350 # forward error messages to logfile
3351 if( ! $foreground ) {
3352 open( STDIN, '+>/dev/null' );
3353 open( STDOUT, '+>&STDIN' );
3354 open( STDERR, '+>&STDIN' );
3355 }
3357 # Just fork, if we are not in foreground mode
3358 if( ! $foreground ) {
3359 chdir '/' or die "Can't chdir to /: $!";
3360 $pid = fork;
3361 setsid or die "Can't start a new session: $!";
3362 umask 0;
3363 } else {
3364 $pid = $$;
3365 }
3367 # Do something useful - put our PID into the pid_file
3368 if( 0 != $pid ) {
3369 open( LOCK_FILE, ">$pid_file" );
3370 print LOCK_FILE "$pid\n";
3371 close( LOCK_FILE );
3372 if( !$foreground ) {
3373 exit( 0 )
3374 };
3375 }
3377 # parse head url and revision from svn
3378 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
3379 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
3380 $server_headURL = defined $1 ? $1 : 'unknown' ;
3381 $server_revision = defined $2 ? $2 : 'unknown' ;
3382 if ($server_headURL =~ /\/tag\// ||
3383 $server_headURL =~ /\/branches\// ) {
3384 $server_status = "stable";
3385 } else {
3386 $server_status = "developmental" ;
3387 }
3388 # Prepare log file and set permissions
3389 $root_uid = getpwnam('root');
3390 $adm_gid = getgrnam('adm');
3391 open(FH, ">>$log_file");
3392 close FH;
3393 chmod(0440, $log_file);
3394 chown($root_uid, $adm_gid, $log_file);
3395 chown($root_uid, $adm_gid, "/var/lib/gosa-si");
3397 daemon_log(" ", 1);
3398 daemon_log("$0 started!", 1);
3399 daemon_log("status: $server_status", 1);
3400 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1);
3402 # Buildup data bases
3403 {
3404 no strict "refs";
3406 if ($db_module eq "DBmysql") {
3407 # connect to incoming_db
3408 $incoming_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3410 # connect to gosa-si job queue
3411 $job_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3413 # connect to known_clients_db
3414 $known_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3416 # connect to foreign_clients_db
3417 $foreign_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3419 # connect to known_server_db
3420 $known_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3422 # connect to login_usr_db
3423 $login_users_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3425 # connect to fai_server_db
3426 $fai_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3428 # connect to fai_release_db
3429 $fai_release_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3431 # connect to packages_list_db
3432 $packages_list_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3434 # connect to messaging_db
3435 $messaging_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3437 } elsif ($db_module eq "DBsqlite") {
3438 # connect to incoming_db
3439 unlink($incoming_file_name);
3440 $incoming_db = GOSA::DBsqlite->new($incoming_file_name);
3441 chmod(0640, $incoming_file_name);
3442 chown($root_uid, $adm_gid, $incoming_file_name);
3444 # connect to gosa-si job queue
3445 $job_db = GOSA::DBsqlite->new($job_queue_file_name);
3446 chmod(0640, $job_queue_file_name);
3447 chown($root_uid, $adm_gid, $job_queue_file_name);
3449 # connect to known_clients_db
3450 #unlink($known_clients_file_name);
3451 $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
3452 chmod(0640, $known_clients_file_name);
3453 chown($root_uid, $adm_gid, $known_clients_file_name);
3455 # connect to foreign_clients_db
3456 #unlink($foreign_clients_file_name);
3457 $foreign_clients_db = GOSA::DBsqlite->new($foreign_clients_file_name);
3458 chmod(0640, $foreign_clients_file_name);
3459 chown($root_uid, $adm_gid, $foreign_clients_file_name);
3461 # connect to known_server_db
3462 #unlink($known_server_file_name);
3463 $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
3464 chmod(0640, $known_server_file_name);
3465 chown($root_uid, $adm_gid, $known_server_file_name);
3467 # connect to login_usr_db
3468 #unlink($login_users_file_name);
3469 $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
3470 chmod(0640, $login_users_file_name);
3471 chown($root_uid, $adm_gid, $login_users_file_name);
3473 # connect to fai_server_db
3474 unlink($fai_server_file_name);
3475 $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
3476 chmod(0640, $fai_server_file_name);
3477 chown($root_uid, $adm_gid, $fai_server_file_name);
3479 # connect to fai_release_db
3480 unlink($fai_release_file_name);
3481 $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
3482 chmod(0640, $fai_release_file_name);
3483 chown($root_uid, $adm_gid, $fai_release_file_name);
3485 # connect to packages_list_db
3486 unlink($packages_list_under_construction);
3487 $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
3488 chmod(0640, $packages_list_file_name);
3489 chown($root_uid, $adm_gid, $packages_list_file_name);
3491 # connect to messaging_db
3492 #unlink($messaging_file_name);
3493 $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
3494 chmod(0640, $messaging_file_name);
3495 chown($root_uid, $adm_gid, $messaging_file_name);
3496 }
3497 }
3500 # Creating tables
3501 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
3502 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
3503 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
3504 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
3505 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
3506 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
3507 $foreign_clients_db->create_table($foreign_clients_tn, \@foreign_clients_col_names);
3508 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
3509 $incoming_db->create_table($incoming_tn, \@incoming_col_names);
3510 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
3512 # create xml object used for en/decrypting
3513 $xml = new XML::Simple();
3516 # foreign servers
3517 my @foreign_server_list;
3519 # add foreign server from cfg file
3520 if ($foreign_server_string ne "") {
3521 my @cfg_foreign_server_list = split(",", $foreign_server_string);
3522 foreach my $foreign_server (@cfg_foreign_server_list) {
3523 push(@foreign_server_list, $foreign_server);
3524 }
3526 daemon_log("0 INFO: found foreign server in config file: ".join(", ", @foreign_server_list), 5);
3527 }
3529 # Perform a DNS lookup for server registration if flag is true
3530 if ($dns_lookup eq "true") {
3531 # Add foreign server from dns
3532 my @tmp_servers;
3533 if (not $server_domain) {
3534 # Try our DNS Searchlist
3535 for my $domain(get_dns_domains()) {
3536 chomp($domain);
3537 my ($tmp_domains, $error_string) = &get_server_addresses($domain);
3538 if(@$tmp_domains) {
3539 for my $tmp_server(@$tmp_domains) {
3540 push @tmp_servers, $tmp_server;
3541 }
3542 }
3543 }
3544 if(@tmp_servers && length(@tmp_servers)==0) {
3545 daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3546 }
3547 } else {
3548 @tmp_servers = &get_server_addresses($server_domain);
3549 if( 0 == @tmp_servers ) {
3550 daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3551 }
3552 }
3554 daemon_log("0 INFO: found foreign server via DNS ".join(", ", @tmp_servers), 5);
3556 foreach my $server (@tmp_servers) {
3557 unshift(@foreign_server_list, $server);
3558 }
3559 } else {
3560 daemon_log("0 INFO: DNS lookup for server registration is disabled", 5);
3561 }
3564 # eliminate duplicate entries
3565 @foreign_server_list = &del_doubles(@foreign_server_list);
3566 my $all_foreign_server = join(", ", @foreign_server_list);
3567 daemon_log("0 INFO: found foreign server in config file and DNS: '$all_foreign_server'", 5);
3569 # add all found foreign servers to known_server
3570 my $cur_timestamp = &get_time();
3571 foreach my $foreign_server (@foreign_server_list) {
3573 # do not add myself to known_server_db
3574 if (&is_local($foreign_server)) { next; }
3575 ######################################
3577 my $res = $known_server_db->add_dbentry( {table=>$known_server_tn,
3578 primkey=>['hostname'],
3579 hostname=>$foreign_server,
3580 macaddress=>"",
3581 status=>'not_yet_registered',
3582 hostkey=>"none",
3583 loaded_modules => "none",
3584 timestamp=>$cur_timestamp,
3585 update_time=>'19700101000000',
3586 } );
3587 }
3590 # Import all modules
3591 &import_modules;
3593 # Check wether all modules are gosa-si valid passwd check
3594 &password_check;
3596 # Create functions hash
3597 #print STDERR Dumper $known_modules;
3598 while (my ($module, @mod_info) = each %$known_modules)
3599 {
3600 #print STDERR Dumper $module;
3601 while (my ($plugin, $functions) = each %{$mod_info[0][2]})
3602 {
3603 #print STDERR Dumper $functions;
3604 while (my ($function, $nothing) = each %$functions )
3605 {
3606 $known_functions->{$function} = $nothing;
3607 }
3608 }
3609 }
3611 # Prepare for using Opsi
3612 if ($opsi_enabled eq "true") {
3613 use JSON::RPC::Client;
3614 use XML::Quote qw(:all);
3615 $opsi_url= "https://".$opsi_admin.":".$opsi_password."@".$opsi_server.":4447/rpc";
3616 $opsi_client = new JSON::RPC::Client;
3617 }
3620 POE::Component::Server::TCP->new(
3621 Alias => "TCP_SERVER",
3622 Port => $server_port,
3623 ClientInput => sub {
3624 my ($kernel, $input, $heap, $session) = @_[KERNEL, ARG0, HEAP, SESSION];
3625 my $session_id = $session->ID;
3626 if ($input =~ /;([\d\.]+:[\d]+)$/)
3627 {
3628 &daemon_log("$session_id DEBUG: incoming message from '$1'", 11);
3629 }
3630 else
3631 {
3632 my $remote_ip = $heap->{'remote_ip'};
3633 &daemon_log("$session_id DEBUG: incoming message from '$remote_ip'", 11);
3634 }
3635 push(@msgs_to_decrypt, $input);
3636 $kernel->yield("msg_to_decrypt");
3637 },
3638 InlineStates => {
3639 msg_to_decrypt => \&msg_to_decrypt,
3640 next_task => \&next_task,
3641 task_result => \&handle_task_result,
3642 task_done => \&handle_task_done,
3643 task_debug => \&handle_task_debug,
3644 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!" },
3645 }
3646 );
3648 daemon_log("0 INFO: start socket for incoming xml messages at port '$server_port' ", 1);
3650 # create session for repeatedly checking the job queue for jobs
3651 POE::Session->create(
3652 inline_states => {
3653 _start => \&session_start,
3654 register_at_foreign_servers => \®ister_at_foreign_servers,
3655 control_server_registration => \&control_server_registration,
3656 sig_handler => \&sig_handler,
3657 next_task => \&next_task,
3658 task_result => \&handle_task_result,
3659 task_done => \&handle_task_done,
3660 task_debug => \&handle_task_debug,
3661 watch_for_next_tasks => \&watch_for_next_tasks,
3662 watch_for_new_messages => \&watch_for_new_messages,
3663 watch_for_delivery_messages => \&watch_for_delivery_messages,
3664 watch_for_done_messages => \&watch_for_done_messages,
3665 watch_for_new_jobs => \&watch_for_new_jobs,
3666 watch_for_modified_jobs => \&watch_for_modified_jobs,
3667 watch_for_done_jobs => \&watch_for_done_jobs,
3668 watch_for_opsi_jobs => \&watch_for_opsi_jobs,
3669 watch_for_old_known_clients => \&watch_for_old_known_clients,
3670 create_packages_list_db => \&run_create_packages_list_db,
3671 create_fai_server_db => \&run_create_fai_server_db,
3672 create_fai_release_db => \&run_create_fai_release_db,
3673 recreate_packages_db => \&run_recreate_packages_db,
3674 session_run_result => \&session_run_result,
3675 session_run_debug => \&session_run_debug,
3676 session_run_done => \&session_run_done,
3677 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!" },
3678 }
3679 );
3682 POE::Kernel->run();
3683 exit;