3bce9a9b76daa4511f3a4e271d9653bed94dc5ee
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][a-z0-9\.]+:\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 }
666 }
667 unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
668 $source =~ /^GOSA$/i) {
669 die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
670 }
672 ##############
673 # check target
674 my $target_l = $msg_hash->{'target'};
675 if( 0 == @{$target_l} ) {
676 die "no targets specified";
677 }
678 foreach my $target (@$target_l) {
679 if( 0 == length $target) {
680 die "target has length 0";
681 }
682 unless( $target =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
683 $target =~ /^GOSA$/i ||
684 $target =~ /^\*$/ ||
685 $target =~ /KNOWN_SERVER/i ||
686 $target =~ /JOBDB/i ||
687 $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 ){
688 die "target '$target' is not a complete ip-address with port or a valid target name or a mac-address";
689 }
690 }
691 };
692 if($@) {
693 daemon_log("$session_id ERROR: outgoing msg is not gosa-si envelope conform: $@", 1);
694 daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 1);
695 $msg_hash = undef;
696 }
698 return ($msg_hash);
699 }
702 sub input_from_known_server {
703 my ($input, $remote_ip, $session_id) = @_ ;
704 my ($msg, $msg_hash, $module);
706 my $sql_statement= "SELECT * FROM known_server";
707 my $query_res = $known_server_db->select_dbentry( $sql_statement );
709 while( my ($hit_num, $hit) = each %{ $query_res } ) {
710 my $host_name = $hit->{hostname};
711 if( not $host_name =~ "^$remote_ip") {
712 next;
713 }
714 my $host_key = $hit->{hostkey};
715 daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 14);
716 daemon_log("$session_id DEBUG: input_from_known_server: host_key: $host_key", 14);
718 # check if module can open msg envelope with module key
719 my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
720 if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
721 daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 14);
722 daemon_log("$@", 14);
723 next;
724 }
725 else {
726 $msg = $tmp_msg;
727 $msg_hash = $tmp_msg_hash;
728 $module = "ServerPackages";
729 daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 14);
730 last;
731 }
732 }
734 if( (!$msg) || (!$msg_hash) || (!$module) ) {
735 daemon_log("$session_id DEBUG: Incoming message is not from a known server", 14);
736 }
738 return ($msg, $msg_hash, $module);
739 }
742 sub input_from_known_client {
743 my ($input, $remote_ip, $session_id) = @_ ;
744 my ($msg, $msg_hash, $module);
746 my $sql_statement= "SELECT * FROM known_clients";
747 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
748 while( my ($hit_num, $hit) = each %{ $query_res } ) {
749 my $host_name = $hit->{hostname};
750 if( not $host_name =~ /^$remote_ip/) {
751 next;
752 }
753 my $host_key = $hit->{hostkey};
754 &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 14);
755 &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 14);
757 # check if module can open msg envelope with module key
758 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
760 if( (!$msg) || (!$msg_hash) ) {
761 &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 14);
762 next;
763 }
764 else {
765 $module = "ClientPackages";
766 daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 14);
767 last;
768 }
769 }
771 if( (!$msg) || (!$msg_hash) || (!$module) ) {
772 &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 14);
773 }
775 return ($msg, $msg_hash, $module);
776 }
779 sub input_from_unknown_host {
780 no strict "refs";
781 my ($input, $session_id) = @_ ;
782 my ($msg, $msg_hash, $module);
783 my $error_string;
785 my %act_modules = %$known_modules;
787 while( my ($mod, $info) = each(%act_modules)) {
789 # check a key exists for this module
790 my $module_key = ${$mod."_key"};
791 if( not defined $module_key ) {
792 if( $mod eq 'ArpHandler' ) {
793 next;
794 }
795 daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
796 next;
797 }
798 daemon_log("$session_id DEBUG: $mod: $module_key", 14);
800 # check if module can open msg envelope with module key
801 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
802 if( (not defined $msg) || (not defined $msg_hash) ) {
803 next;
804 } else {
805 $module = $mod;
806 daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 18);
807 last;
808 }
809 }
811 if( (!$msg) || (!$msg_hash) || (!$module)) {
812 daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 14);
813 }
815 return ($msg, $msg_hash, $module);
816 }
819 sub create_ciphering {
820 my ($passwd) = @_;
821 if((!defined($passwd)) || length($passwd)==0) {
822 $passwd = "";
823 }
824 $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
825 my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
826 my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
827 $my_cipher->set_iv($iv);
828 return $my_cipher;
829 }
832 sub encrypt_msg {
833 my ($msg, $key) = @_;
834 my $my_cipher = &create_ciphering($key);
835 my $len;
836 {
837 use bytes;
838 $len= 16-length($msg)%16;
839 }
840 $msg = "\0"x($len).$msg;
841 $msg = $my_cipher->encrypt($msg);
842 chomp($msg = &encode_base64($msg));
843 # there are no newlines allowed inside msg
844 $msg=~ s/\n//g;
845 return $msg;
846 }
849 sub decrypt_msg {
851 my ($msg, $key) = @_ ;
852 $msg = &decode_base64($msg);
853 my $my_cipher = &create_ciphering($key);
854 $msg = $my_cipher->decrypt($msg);
855 $msg =~ s/\0*//g;
856 return $msg;
857 }
860 sub get_encrypt_key {
861 my ($target) = @_ ;
862 my $encrypt_key;
863 my $error = 0;
865 # target can be in known_server
866 if( not defined $encrypt_key ) {
867 my $sql_statement= "SELECT * FROM known_server WHERE hostname='$target'";
868 my $query_res = $known_server_db->select_dbentry( $sql_statement );
869 while( my ($hit_num, $hit) = each %{ $query_res } ) {
870 my $host_name = $hit->{hostname};
871 if( $host_name ne $target ) {
872 next;
873 }
874 $encrypt_key = $hit->{hostkey};
875 last;
876 }
877 }
879 # target can be in known_client
880 if( not defined $encrypt_key ) {
881 my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$target'";
882 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
883 while( my ($hit_num, $hit) = each %{ $query_res } ) {
884 my $host_name = $hit->{hostname};
885 if( $host_name ne $target ) {
886 next;
887 }
888 $encrypt_key = $hit->{hostkey};
889 last;
890 }
891 }
893 return $encrypt_key;
894 }
897 #=== FUNCTION ================================================================
898 # NAME: open_socket
899 # PARAMETERS: PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
900 # [PeerPort] string necessary if port not appended by PeerAddr
901 # RETURNS: socket IO::Socket::INET
902 # DESCRIPTION: open a socket to PeerAddr
903 #===============================================================================
904 sub open_socket {
905 my ($PeerAddr, $PeerPort) = @_ ;
906 if(defined($PeerPort)){
907 $PeerAddr = $PeerAddr.":".$PeerPort;
908 }
909 my $socket;
910 $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
911 Porto => "tcp",
912 Type => SOCK_STREAM,
913 Timeout => 5,
914 );
915 if(not defined $socket) {
916 return;
917 }
918 # &daemon_log("DEBUG: open_socket: $PeerAddr", 7);
919 return $socket;
920 }
923 sub send_msg_to_target {
924 my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
925 my $error = 0;
926 my $header;
927 my $timestamp = &get_time();
928 my $new_status;
929 my $act_status;
930 my ($sql_statement, $res);
932 if( $msg_header ) {
933 $header = "'$msg_header'-";
934 } else {
935 $header = "";
936 }
938 # Memorize own source address
939 my $own_source_address = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
940 $own_source_address .= ":".$server_port;
942 # Patch 0.0.0.0 source to real address
943 $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$own_source_address<\/source>/s;
944 # Patch GOSA source to real address and add forward_to_gosa tag
945 $msg =~ s/<source>GOSA<\/source>/<source>$own_source_address<\/source> <forward_to_gosa>$own_source_address,$session_id<\/forward_to_gosa>/ ;
947 # encrypt xml msg
948 my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
950 # opensocket
951 my $socket = &open_socket($address);
952 if( !$socket ) {
953 daemon_log("$session_id ERROR: Cannot open socket to host '$address'. Message processing aborted!", 1);
954 $error++;
955 }
957 if( $error == 0 ) {
958 # send xml msg
959 print $socket $crypted_msg.";$own_source_address\n";
960 daemon_log("$session_id INFO: send ".$header."msg to $address", 5);
961 daemon_log("$session_id DEBUG: message:\n$msg", 12);
963 }
965 # close socket in any case
966 if( $socket ) {
967 close $socket;
968 }
970 if( $error > 0 ) { $new_status = "down"; }
971 else { $new_status = $msg_header; }
974 # known_clients
975 $sql_statement = "SELECT * FROM $known_clients_tn WHERE hostname='$address'";
976 $res = $known_clients_db->select_dbentry($sql_statement);
977 if( keys(%$res) == 1) {
978 $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
979 if ($act_status eq "down" && $new_status eq "down") {
980 $sql_statement = "DELETE FROM known_clients WHERE hostname='$address'";
981 $res = $known_clients_db->del_dbentry($sql_statement);
982 daemon_log("$session_id WARNING: failed 2x to send msg to host '$address', delete host from known_clients", 3);
983 } else {
984 $sql_statement = "UPDATE known_clients SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
985 $res = $known_clients_db->update_dbentry($sql_statement);
986 if($new_status eq "down"){
987 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
988 } else {
989 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
990 }
991 }
992 }
994 # known_server
995 $sql_statement = "SELECT * FROM $known_server_tn WHERE hostname='$address'";
996 $res = $known_server_db->select_dbentry($sql_statement);
997 if( keys(%$res) == 1) {
998 $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
999 if ($act_status eq "down" && $new_status eq "down") {
1000 $sql_statement = "DELETE FROM known_server WHERE hostname='$address'";
1001 $res = $known_server_db->del_dbentry($sql_statement);
1002 daemon_log("$session_id WARNING: failed 2x to send a message to host '$address', delete host from known_server", 3);
1003 }
1004 else {
1005 $sql_statement = "UPDATE known_server SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
1006 $res = $known_server_db->update_dbentry($sql_statement);
1007 if($new_status eq "down"){
1008 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
1009 } else {
1010 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
1011 }
1012 }
1013 }
1014 return $error;
1015 }
1018 sub update_jobdb_status_for_send_msgs {
1019 my ($session_id, $answer, $error) = @_;
1020 if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
1021 &daemon_log("$session_id DEBUG: try to update job status", 138);
1022 my $jobdb_id = $1;
1024 $answer =~ /<header>(.*)<\/header>/;
1025 my $job_header = $1;
1027 $answer =~ /<target>(.*)<\/target>/;
1028 my $job_target = $1;
1030 # Sending msg failed
1031 if( $error ) {
1033 # Set jobs to done, jobs do not need to deliver their message in any case
1034 if (($job_header eq "trigger_action_localboot")
1035 ||($job_header eq "trigger_action_lock")
1036 ||($job_header eq "trigger_action_halt")
1037 ) {
1038 my $sql_statement = "UPDATE $job_queue_tn SET status='done' WHERE id=$jobdb_id";
1039 my $res = $job_db->update_dbentry($sql_statement);
1041 # Reactivate jobs, jobs need to deliver their message
1042 } elsif (($job_header eq "trigger_action_activate")
1043 ||($job_header eq "trigger_action_update")
1044 ||($job_header eq "trigger_action_reinstall")
1045 ||($job_header eq "trigger_activate_new")
1046 ) {
1047 &reactivate_job_with_delay($session_id, $job_target, $job_header, 30 );
1049 # For all other messages
1050 } else {
1051 my $sql_statement = "UPDATE $job_queue_tn ".
1052 "SET status='error', result='can not deliver msg, please consult log file' ".
1053 "WHERE id=$jobdb_id";
1054 my $res = $job_db->update_dbentry($sql_statement);
1055 }
1057 # Sending msg was successful
1058 } else {
1059 # Set jobs localboot, lock, activate, halt, reboot and wake to done
1060 # jobs reinstall, update, inst_update do themself setting to done
1061 if (($job_header eq "trigger_action_localboot")
1062 ||($job_header eq "trigger_action_lock")
1063 ||($job_header eq "trigger_action_activate")
1064 ||($job_header eq "trigger_action_halt")
1065 ||($job_header eq "trigger_action_reboot")
1066 ||($job_header eq "trigger_action_wake")
1067 ||($job_header eq "trigger_wake")
1068 ) {
1070 my $sql_statement = "UPDATE $job_queue_tn ".
1071 "SET status='done' ".
1072 "WHERE id=$jobdb_id AND status='processed'";
1073 my $res = $job_db->update_dbentry($sql_statement);
1074 } else {
1075 &daemon_log("$session_id DEBUG: sending message succeed but cannot update job status.", 138);
1076 }
1077 }
1078 } else {
1079 &daemon_log("$session_id DEBUG: cannot update job status, msg has no jobdb_id-tag.", 138);
1080 }
1081 }
1083 sub reactivate_job_with_delay {
1084 my ($session_id, $target, $header, $delay) = @_ ;
1085 # Sometimes the client is still booting or does not wake up, in this case reactivate the job (if it exists) with a delay of n sec
1087 if (not defined $delay) { $delay = 30 } ;
1088 my $delay_timestamp = &calc_timestamp(&get_time(), "plus", $delay);
1090 my $sql = "UPDATE $job_queue_tn Set timestamp='$delay_timestamp', status='waiting' WHERE (macaddress LIKE 'target' AND headertag='$header')";
1091 my $res = $job_db->update_dbentry($sql);
1092 daemon_log("$session_id INFO: '$header'-job will be reactivated at '$delay_timestamp' ".
1093 "cause client '$target' is currently not available", 5);
1094 return;
1095 }
1098 sub sig_handler {
1099 my ($kernel, $signal) = @_[KERNEL, ARG0] ;
1100 daemon_log("0 INFO got signal '$signal'", 1);
1101 $kernel->sig_handled();
1102 return;
1103 }
1106 sub msg_to_decrypt {
1107 my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
1108 my $session_id = $session->ID;
1109 my ($msg, $msg_hash, $module);
1110 my $error = 0;
1112 # fetch new msg out of @msgs_to_decrypt
1113 my $tmp_next_msg = shift @msgs_to_decrypt;
1114 my ($next_msg, $msg_source) = split(/;/, $tmp_next_msg);
1116 # msg is from a new client or gosa
1117 ($msg, $msg_hash, $module) = &input_from_unknown_host($next_msg, $session_id);
1119 # msg is from a gosa-si-server
1120 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1121 if (not defined $msg_source)
1122 {
1123 # Only needed, to be compatible with older gosa-si-server versions
1124 ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $heap->{'remote_ip'}, $session_id);
1125 }
1126 else
1127 {
1128 ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $msg_source, $session_id);
1129 }
1130 }
1131 # msg is from a gosa-si-client
1132 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1133 ($msg, $msg_hash, $module) = &input_from_known_client($next_msg, $msg_source, $session_id);
1134 }
1135 # an error occurred
1136 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1137 # If an incoming msg could not be decrypted (maybe a wrong key), decide if msg comes from a client
1138 # or a server. In case of a client, send a ping. If the client could not understand a msg from its
1139 # server the client cause a re-registering process. In case of a server, decrease update_time in kown_server_db
1140 # and trigger a re-registering process for servers
1141 if (defined $msg_source && $msg_source =~ /:$server_port$/)
1142 {
1143 daemon_log("$session_id WARNING: Cannot understand incoming msg from server '$msg_source'. Cause re-registration process for servers.", 3);
1144 my $update_statement = "UPDATE $known_server_tn SET update_time='19700101000000' WHERE hostname='$msg_source'";
1145 daemon_log("$session_id DEBUG: $update_statement", 7);
1146 my $upadte_res = $known_server_db->exec_statement($update_statement);
1147 $kernel->yield("register_at_foreign_servers");
1148 }
1149 elsif (defined $msg_source)
1150 {
1151 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);
1152 #my $remote_ip = $heap->{'remote_ip'};
1153 #my $remote_port = $heap->{'remote_port'};
1154 my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source><target>$msg_source</target></xml>";
1155 my ($test_error, $test_error_string) = &send_msg_to_target($ping_msg, "$msg_source", "dummy-key", "gosa_ping", $session_id);
1156 daemon_log("$session_id WARNING: sending msg to cause re-registering: $ping_msg", 3);
1157 }
1158 else
1159 {
1160 my $foreign_host = defined $msg_source ? $msg_source : $heap->{'remote_ip'};
1161 daemon_log("$session_id ERROR: incoming message from host '$foreign_host' cannot be understood. Processing aborted: $tmp_next_msg", 1);
1162 }
1164 $error++
1165 }
1168 my $header;
1169 my $target;
1170 my $source;
1171 my $done = 0;
1172 my $sql;
1173 my $res;
1175 # check whether this message should be processed here
1176 if ($error == 0) {
1177 $header = @{$msg_hash->{'header'}}[0];
1178 $target = @{$msg_hash->{'target'}}[0];
1179 $source = @{$msg_hash->{'source'}}[0];
1180 my $not_found_in_known_clients_db = 0;
1181 my $not_found_in_known_server_db = 0;
1182 my $not_found_in_foreign_clients_db = 0;
1183 my $local_address;
1184 my $local_mac;
1185 my ($target_ip, $target_port) = split(':', $target);
1187 # Determine the local ip address if target is an ip address
1188 if ($target =~ /^\d+\.\d+\.\d+\.\d+:\d+$/) {
1189 $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1190 } else {
1191 $local_address = $server_address;
1192 }
1194 # Determine the local mac address if target is a mac address
1195 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) {
1196 my $loc_ip = &get_local_ip_for_remote_ip($heap->{'remote_ip'});
1197 my $network_interface= &get_interface_for_ip($loc_ip);
1198 $local_mac = &get_mac_for_interface($network_interface);
1199 } else {
1200 $local_mac = $server_mac_address;
1201 }
1203 # target and source is equal to GOSA -> process here
1204 if (not $done) {
1205 if ($target eq "GOSA" && $source eq "GOSA") {
1206 $done = 1;
1207 &daemon_log("$session_id DEBUG: target and source is 'GOSA' -> process '$header' here", 11);
1208 }
1209 }
1211 # target is own address without forward_to_gosa-tag -> process here
1212 if (not $done) {
1213 #if ((($target eq $local_address) || ($target eq $local_mac) ) && (not exists $msg_hash->{'forward_to_gosa'})) {
1214 if (($target eq $local_address) && (not exists $msg_hash->{'forward_to_gosa'})) {
1215 $done = 1;
1216 if ($source eq "GOSA") {
1217 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1218 }
1219 &daemon_log("$session_id DEBUG: target is own address without forward_to_gosa-tag -> process '$header' here", 11);
1220 }
1221 }
1223 # target is own address with forward_to_gosa-tag not pointing to myself -> process here
1224 if (not $done) {
1225 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1226 my $gosa_at;
1227 my $gosa_session_id;
1228 if (($target eq $local_address) && (defined $forward_to_gosa)){
1229 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1230 if ($gosa_at ne $local_address) {
1231 $done = 1;
1232 &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag not pointing to myself -> process '$header' here", 11);
1233 }
1234 }
1235 }
1237 # Target is a client address and there is a processing function within a plugin -> process loaclly
1238 if (not $done)
1239 {
1240 # Check if target is a client address
1241 $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1242 $res = $known_clients_db->select_dbentry($sql);
1243 if ((keys(%$res) > 0) )
1244 {
1245 my $hostname = $res->{1}->{'hostname'};
1246 my $reduced_header = $header;
1247 $reduced_header =~ s/gosa_//;
1248 # Check if there is a processing function within a plugin
1249 if (exists $known_functions->{$reduced_header})
1250 {
1251 $msg =~ s/<target>\S*<\/target>/<target>$hostname<\/target>/;
1252 $done = 1;
1253 &daemon_log("$session_id DEBUG: Target is client address with processing function within a plugin -> process '$header' here", 11);
1254 }
1255 }
1256 }
1258 # If header has a 'job_' prefix, do always process message locally
1259 # which means put it into job queue
1260 if ((not $done) && ($header =~ /job_/))
1261 {
1262 $done = 1;
1263 &daemon_log("$session_id DEBUG: Header has a 'job_' prefix. Put it into job queue. -> process '$header' here", 11);
1264 }
1266 # if message should be processed here -> add message to incoming_db
1267 if ($done) {
1268 # if a 'job_' or a 'gosa_' message comes from a foreign server, fake module from
1269 # ServerPackages to GosaPackages so gosa-si-server knows how to process this kind of messages
1270 if ($header =~ /^gosa_/ || $header =~ /^job_/) {
1271 $module = "GosaPackages";
1272 }
1274 my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1275 primkey=>[],
1276 headertag=>$header,
1277 targettag=>$target,
1278 xmlmessage=>&encode_base64($msg),
1279 timestamp=>&get_time,
1280 module=>$module,
1281 sessionid=>$session_id,
1282 } );
1284 }
1286 # target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa
1287 if (not $done) {
1288 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1289 my $gosa_at;
1290 my $gosa_session_id;
1291 if (($target eq $local_address) && (defined $forward_to_gosa)){
1292 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1293 if ($gosa_at eq $local_address) {
1294 my $session_reference = $kernel->ID_id_to_session($gosa_session_id);
1295 if( defined $session_reference ) {
1296 $heap = $session_reference->get_heap();
1297 }
1298 if(exists $heap->{'client'}) {
1299 $msg = &encrypt_msg($msg, $GosaPackages_key);
1300 $heap->{'client'}->put($msg);
1301 &daemon_log("$session_id DEBUG: incoming '$header' message forwarded to GOsa", 11);
1302 }
1303 $done = 1;
1304 &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag pointing at myself -> forward '$header' to gosa", 11);
1305 }
1306 }
1308 }
1310 # target is a client address in known_clients -> forward to client
1311 if (not $done) {
1312 $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1313 $res = $known_clients_db->select_dbentry($sql);
1314 if (keys(%$res) > 0)
1315 {
1316 $done = 1;
1317 &daemon_log("$session_id DEBUG: target is a client address in known_clients -> forward '$header' to client", 11);
1318 my $hostkey = $res->{1}->{'hostkey'};
1319 my $hostname = $res->{1}->{'hostname'};
1320 $msg =~ s/<target>\S*<\/target>/<target>$hostname<\/target>/;
1321 $msg =~ s/<header>gosa_/<header>/;
1322 my $error= &send_msg_to_target($msg, $hostname, $hostkey, $header, $session_id);
1323 if ($error) {
1324 &daemon_log("$session_id ERROR: Some problems occurred while trying to send msg to client '$hostkey': $msg", 1);
1325 }
1326 }
1327 else
1328 {
1329 $not_found_in_known_clients_db = 1;
1330 }
1331 }
1333 # target is a client address in foreign_clients -> forward to registration server
1334 if (not $done) {
1335 $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1336 $res = $foreign_clients_db->select_dbentry($sql);
1337 if (keys(%$res) > 0) {
1338 my $hostname = $res->{1}->{'hostname'};
1339 my ($host_ip, $host_port) = split(/:/, $hostname);
1340 my $local_address = &get_local_ip_for_remote_ip($host_ip).":$server_port";
1341 my $regserver = $res->{1}->{'regserver'};
1342 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$regserver'";
1343 my $res = $known_server_db->select_dbentry($sql);
1344 if (keys(%$res) > 0) {
1345 my $regserver_key = $res->{1}->{'hostkey'};
1346 $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1347 $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1348 if ($source eq "GOSA") {
1349 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1350 }
1351 my $error= &send_msg_to_target($msg, $regserver, $regserver_key, $header, $session_id);
1352 if ($error) {
1353 &daemon_log("$session_id ERROR: some problems occurred while trying to send msg to registration server: $msg", 1);
1354 }
1355 }
1356 $done = 1;
1357 &daemon_log("$session_id DEBUG: target is a client address in foreign_clients -> forward '$header' to registration server", 11);
1358 } else {
1359 $not_found_in_foreign_clients_db = 1;
1360 }
1361 }
1363 # target is a server address -> forward to server
1364 if (not $done) {
1365 $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1366 $res = $known_server_db->select_dbentry($sql);
1367 if (keys(%$res) > 0) {
1368 my $hostkey = $res->{1}->{'hostkey'};
1370 if ($source eq "GOSA") {
1371 $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1372 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1374 }
1376 &send_msg_to_target($msg, $target, $hostkey, $header, $session_id);
1377 $done = 1;
1378 &daemon_log("$session_id DEBUG: target is a server address -> forward '$header' to server", 11);
1379 } else {
1380 $not_found_in_known_server_db = 1;
1381 }
1382 }
1385 # target is not in foreign_clients_db, known_server_db or known_clients_db, maybe it is a complete new one -> process here
1386 if ( $not_found_in_foreign_clients_db
1387 && $not_found_in_known_server_db
1388 && $not_found_in_known_clients_db) {
1389 &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);
1390 if ($header =~ /^gosa_/ || $header =~ /^job_/) {
1391 $module = "GosaPackages";
1392 }
1393 my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1394 primkey=>[],
1395 headertag=>$header,
1396 targettag=>$target,
1397 xmlmessage=>&encode_base64($msg),
1398 timestamp=>&get_time,
1399 module=>$module,
1400 sessionid=>$session_id,
1401 } );
1402 $done = 1;
1403 }
1406 if (not $done) {
1407 daemon_log("$session_id ERROR: do not know what to do with this message: $msg", 1);
1408 if ($source eq "GOSA") {
1409 my %data = ('error_msg' => &encode_base64($msg), 'error_string' => "Do not know what to do with this message!");
1410 my $error_msg = &build_msg("error", $local_address, "GOSA", \%data );
1412 my $session_reference = $kernel->ID_id_to_session($session_id);
1413 if( defined $session_reference ) {
1414 $heap = $session_reference->get_heap();
1415 }
1416 if(exists $heap->{'client'}) {
1417 $error_msg = &encrypt_msg($error_msg, $GosaPackages_key);
1418 $heap->{'client'}->put($error_msg);
1419 }
1420 }
1421 }
1423 }
1425 return;
1426 }
1429 sub next_task {
1430 my ($session, $heap, $task) = @_[SESSION, HEAP, ARG0, ARG1];
1431 my $running_task = POE::Wheel::Run->new(
1432 Program => sub { process_task($session, $heap, $task) },
1433 StdioFilter => POE::Filter::Reference->new(),
1434 StdoutEvent => "task_result",
1435 StderrEvent => "task_debug",
1436 CloseEvent => "task_done",
1437 );
1438 $heap->{task}->{ $running_task->ID } = $running_task;
1439 }
1441 sub handle_task_result {
1442 my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
1443 my $client_answer = $result->{'answer'};
1444 if( $client_answer =~ s/session_id=(\d+)$// ) {
1445 my $session_id = $1;
1446 if( defined $session_id ) {
1447 my $session_reference = $kernel->ID_id_to_session($session_id);
1448 if( defined $session_reference ) {
1449 $heap = $session_reference->get_heap();
1450 }
1451 }
1453 if(exists $heap->{'client'}) {
1454 $heap->{'client'}->put($client_answer);
1455 }
1456 }
1457 $kernel->sig(CHLD => "child_reap");
1458 }
1460 sub handle_task_debug {
1461 my $result = $_[ARG0];
1462 print STDERR "$result\n";
1463 }
1465 sub handle_task_done {
1466 my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1467 delete $heap->{task}->{$task_id};
1468 if (exists $heap->{ldap_handle}->{$task_id}) {
1469 &release_ldap_handle($heap->{ldap_handle}->{$task_id});
1470 }
1471 }
1473 sub process_task {
1474 no strict "refs";
1475 #CHECK: Not @_[...]?
1476 my ($session, $heap, $task) = @_;
1477 my $error = 0;
1478 my $answer_l;
1479 my ($answer_header, @answer_target_l, $answer_source);
1480 my $client_answer = "";
1482 # prepare all variables needed to process message
1483 #my $msg = $task->{'xmlmessage'};
1484 my $msg = &decode_base64($task->{'xmlmessage'});
1485 my $incoming_id = $task->{'id'};
1486 my $module = $task->{'module'};
1487 my $header = $task->{'headertag'};
1488 my $session_id = $task->{'sessionid'};
1489 my $msg_hash;
1490 eval {
1491 $msg_hash = $xml->XMLin($msg, ForceArray=>1);
1492 };
1493 daemon_log("ERROR: XML failure '$@'") if ($@);
1494 my $source = @{$msg_hash->{'source'}}[0];
1496 # set timestamp of incoming client uptodate, so client will not
1497 # be deleted from known_clients because of expiration
1498 my $cur_time = &get_time();
1499 my $sql = "UPDATE $known_clients_tn SET timestamp='$cur_time' WHERE hostname='$source'";
1500 my $res = $known_clients_db->exec_statement($sql);
1502 ######################
1503 # process incoming msg
1504 if( $error == 0) {
1505 daemon_log("$session_id INFO: Incoming msg (session_id=$session_id) with header '".@{$msg_hash->{'header'}}[0]."'", 5);
1506 daemon_log("$session_id DEBUG: Processing module ".$module, 26);
1507 $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1509 if ( 0 < @{$answer_l} ) {
1510 my $answer_str = join("\n", @{$answer_l});
1511 my @headers;
1512 while ($answer_str =~ /<header>(\w+)<\/header>/g) {
1513 push(@headers, $1);
1514 }
1515 daemon_log("$session_id INFO: got answer message(s) with header: '".join("', '", @headers)."'", 5);
1516 daemon_log("$session_id DEBUG: $module: got answer from module: \n".$answer_str,26);
1517 } else {
1518 daemon_log("$session_id DEBUG: $module: got no answer from module!" ,26);
1519 }
1521 }
1522 if( !$answer_l ) { $error++ };
1524 ########
1525 # answer
1526 if( $error == 0 ) {
1528 foreach my $answer ( @{$answer_l} ) {
1529 # check outgoing msg to xml validity
1530 my $answer_hash = &check_outgoing_xml_validity($answer, $session_id);
1531 if( not defined $answer_hash ) { next; }
1533 $answer_header = @{$answer_hash->{'header'}}[0];
1534 @answer_target_l = @{$answer_hash->{'target'}};
1535 $answer_source = @{$answer_hash->{'source'}}[0];
1537 # deliver msg to all targets
1538 foreach my $answer_target ( @answer_target_l ) {
1540 # targets of msg are all gosa-si-clients in known_clients_db
1541 if( $answer_target eq "*" ) {
1542 # answer is for all clients
1543 my $sql_statement= "SELECT * FROM known_clients";
1544 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1545 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1546 my $host_name = $hit->{hostname};
1547 my $host_key = $hit->{hostkey};
1548 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1549 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1550 }
1551 }
1553 # targets of msg are all gosa-si-server in known_server_db
1554 elsif( $answer_target eq "KNOWN_SERVER" ) {
1555 # answer is for all server in known_server
1556 my $sql_statement= "SELECT * FROM $known_server_tn";
1557 my $query_res = $known_server_db->select_dbentry( $sql_statement );
1558 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1559 my $host_name = $hit->{hostname};
1560 my $host_key = $hit->{hostkey};
1561 $answer =~ s/<target>\S+<\/target>/<target>$host_name<\/target>/g;
1562 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1563 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1564 }
1565 }
1567 # target of msg is GOsa
1568 elsif( $answer_target eq "GOSA" ) {
1569 my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1570 my $add_on = "";
1571 if( defined $session_id ) {
1572 $add_on = ".session_id=$session_id";
1573 }
1574 # answer is for GOSA and has to returned to connected client
1575 my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1576 $client_answer = $gosa_answer.$add_on;
1577 }
1579 # target of msg is job queue at this host
1580 elsif( $answer_target eq "JOBDB") {
1581 $answer =~ /<header>(\S+)<\/header>/;
1582 my $header;
1583 if( defined $1 ) { $header = $1; }
1584 my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1585 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1586 }
1588 # Target of msg is a mac address
1589 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 ) {
1590 daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients and foreign_clients", 5);
1592 # Looking for macaddress in known_clients
1593 my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1594 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1595 my $found_ip_flag = 0;
1596 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1597 my $host_name = $hit->{hostname};
1598 my $host_key = $hit->{hostkey};
1599 $answer =~ s/$answer_target/$host_name/g;
1600 daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1601 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1602 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1603 $found_ip_flag++ ;
1604 }
1606 # Looking for macaddress in foreign_clients
1607 if ($found_ip_flag == 0) {
1608 my $sql = "SELECT * FROM $foreign_clients_tn WHERE macaddress LIKE '$answer_target'";
1609 my $res = $foreign_clients_db->select_dbentry($sql);
1610 while( my ($hit_num, $hit) = each %{ $res } ) {
1611 my $host_name = $hit->{hostname};
1612 my $reg_server = $hit->{regserver};
1613 daemon_log("$session_id INFO: found host '$host_name' with mac '$answer_target', registered at '$reg_server'", 5);
1615 # Fetch key for reg_server
1616 my $reg_server_key;
1617 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$reg_server'";
1618 my $res = $known_server_db->select_dbentry($sql);
1619 if (exists $res->{1}) {
1620 $reg_server_key = $res->{1}->{'hostkey'};
1621 } else {
1622 daemon_log("$session_id ERROR: cannot find hostkey for '$host_name' in '$known_server_tn'", 1);
1623 daemon_log("$session_id ERROR: unable to forward answer to correct registration server, processing is aborted!", 1);
1624 $reg_server_key = undef;
1625 }
1627 # Send answer to server where client is registered
1628 if (defined $reg_server_key) {
1629 $answer =~ s/$answer_target/$host_name/g;
1630 my $error = &send_msg_to_target($answer, $reg_server, $reg_server_key, $answer_header, $session_id);
1631 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1632 $found_ip_flag++ ;
1633 }
1634 }
1635 }
1637 # No mac to ip matching found
1638 if( $found_ip_flag == 0) {
1639 daemon_log("$session_id WARNING: no host found in known_clients or foreign_clients with mac address '$answer_target'", 3);
1640 &reactivate_job_with_delay($session_id, $answer_target, $answer_header, 30);
1641 }
1643 # Answer is for one specific host
1644 } else {
1645 # get encrypt_key
1646 my $encrypt_key = &get_encrypt_key($answer_target);
1647 if( not defined $encrypt_key ) {
1648 # unknown target
1649 daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1650 next;
1651 }
1652 my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1653 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1654 }
1655 }
1656 }
1657 }
1659 my $filter = POE::Filter::Reference->new();
1660 my %result = (
1661 status => "seems ok to me",
1662 answer => $client_answer,
1663 );
1665 my $output = $filter->put( [ \%result ] );
1666 print @$output;
1669 }
1671 sub session_start {
1672 my ($kernel) = $_[KERNEL];
1673 $global_kernel = $kernel;
1674 $kernel->yield('register_at_foreign_servers');
1675 $kernel->yield('create_fai_server_db', $fai_server_tn );
1676 $kernel->yield('create_fai_release_db', $fai_release_tn );
1677 $kernel->yield('watch_for_next_tasks');
1678 $kernel->sig(USR1 => "sig_handler");
1679 $kernel->sig(USR2 => "recreate_packages_db");
1680 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1681 $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay);
1682 $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay);
1683 $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1684 $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1685 $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1686 $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1688 # Start opsi check
1689 if ($opsi_enabled eq "true") {
1690 $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay);
1691 }
1693 }
1696 sub watch_for_done_jobs {
1697 #CHECK: $heap for what?
1698 my ($kernel,$heap) = @_[KERNEL, HEAP];
1700 my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((status='done') AND (modified='0'))";
1701 my $res = $job_db->select_dbentry( $sql_statement );
1703 while( my ($id, $hit) = each %{$res} ) {
1704 my $jobdb_id = $hit->{id};
1705 my $sql_statement = "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1706 my $res = $job_db->del_dbentry($sql_statement);
1707 }
1709 $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1710 }
1713 sub watch_for_opsi_jobs {
1714 my ($kernel) = $_[KERNEL];
1716 # 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
1717 # opsi install job is to parse the xml message. There is still the correct header.
1718 my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((xmlmessage LIKE '%opsi_install_client</header>%') AND (status='processing') AND (siserver='localhost'))";
1719 my $res = $job_db->select_dbentry( $sql_statement );
1721 # Ask OPSI for an update of the running jobs
1722 while (my ($id, $hit) = each %$res ) {
1723 # Determine current parameters of the job
1724 my $hostId = $hit->{'plainname'};
1725 my $macaddress = $hit->{'macaddress'};
1726 my $progress = $hit->{'progress'};
1728 my $result= {};
1730 # For hosts, only return the products that are or get installed
1731 my $callobj;
1732 $callobj = {
1733 method => 'getProductStates_hash',
1734 params => [ $hostId ],
1735 id => 1,
1736 };
1738 my $hres = $opsi_client->call($opsi_url, $callobj);
1739 #my ($hres_err, $hres_err_string) = &check_opsi_res($hres);
1740 if (not &check_opsi_res($hres)) {
1741 my $htmp= $hres->result->{$hostId};
1743 # Check state != not_installed or action == setup -> load and add
1744 my $products= 0;
1745 my $installed= 0;
1746 my $installing = 0;
1747 my $error= 0;
1748 my @installed_list;
1749 my @error_list;
1750 my $act_status = "none";
1751 foreach my $product (@{$htmp}){
1753 if ($product->{'installationStatus'} ne "not_installed" or
1754 $product->{'actionRequest'} eq "setup"){
1756 # Increase number of products for this host
1757 $products++;
1759 if ($product->{'installationStatus'} eq "failed"){
1760 $result->{$product->{'productId'}}= "error";
1761 unshift(@error_list, $product->{'productId'});
1762 $error++;
1763 }
1764 if ($product->{'installationStatus'} eq "installed" && $product->{'actionRequest'} eq "none"){
1765 $result->{$product->{'productId'}}= "installed";
1766 unshift(@installed_list, $product->{'productId'});
1767 $installed++;
1768 }
1769 if ($product->{'installationStatus'} eq "installing"){
1770 $result->{$product->{'productId'}}= "installing";
1771 $installing++;
1772 $act_status = "installing - ".$product->{'productId'};
1773 }
1774 }
1775 }
1777 # Estimate "rough" progress, avoid division by zero
1778 if ($products == 0) {
1779 $result->{'progress'}= 0;
1780 } else {
1781 $result->{'progress'}= int($installed * 100 / $products);
1782 }
1784 # Set updates in job queue
1785 if ((not $error) && (not $installing) && ($installed)) {
1786 $act_status = "installed - ".join(", ", @installed_list);
1787 }
1788 if ($error) {
1789 $act_status = "error - ".join(", ", @error_list);
1790 }
1791 if ($progress ne $result->{'progress'} ) {
1792 # Updating progress and result
1793 my $update_statement = "UPDATE $job_queue_tn SET modified='1', progress='".$result->{'progress'}."', result='$act_status' WHERE macaddress LIKE '$macaddress' AND siserver='localhost'";
1794 my $update_res = $job_db->update_dbentry($update_statement);
1795 }
1796 if ($progress eq 100) {
1797 # Updateing status
1798 my $done_statement = "UPDATE $job_queue_tn SET modified='1', ";
1799 if ($error) {
1800 $done_statement .= "status='error'";
1801 } else {
1802 $done_statement .= "status='done'";
1803 }
1804 $done_statement .= " WHERE macaddress LIKE '$macaddress' AND siserver='localhost'";
1805 my $done_res = $job_db->update_dbentry($done_statement);
1806 }
1809 }
1810 }
1812 $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay);
1813 }
1816 # If a job got an update or was modified anyway, send to all other si-server an update message of this jobs.
1817 sub watch_for_modified_jobs {
1818 my ($kernel,$heap) = @_[KERNEL, HEAP];
1820 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE (modified='1')";
1821 my $res = $job_db->select_dbentry( $sql_statement );
1823 # if db contains no jobs which should be update, do nothing
1824 if (keys %$res != 0) {
1826 if ($job_synchronization eq "true") {
1827 # make out of the db result a gosa-si message
1828 my $update_msg = &db_res2si_msg ($res, "foreign_job_updates", "KNOWN_SERVER", "MY_LOCAL_ADDRESS");
1830 # update all other SI-server
1831 &inform_all_other_si_server($update_msg);
1832 }
1834 # set jobs all jobs to modified = 0, wait until the next modification for updates of other si-server
1835 $sql_statement = "UPDATE $job_queue_tn SET modified='0' ";
1836 $res = $job_db->update_dbentry($sql_statement);
1837 }
1839 $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay);
1840 }
1843 sub watch_for_new_jobs {
1844 if($watch_for_new_jobs_in_progress == 0) {
1845 $watch_for_new_jobs_in_progress = 1;
1846 my ($kernel,$heap) = @_[KERNEL, HEAP];
1848 # check gosa job queue for jobs with executable timestamp
1849 my $timestamp = &get_time();
1850 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE siserver='localhost' AND status='waiting' AND (CAST(timestamp AS UNSIGNED)) < $timestamp ORDER BY timestamp";
1851 my $res = $job_db->exec_statement( $sql_statement );
1853 # Merge all new jobs that would do the same actions
1854 my @drops;
1855 my $hits;
1856 foreach my $hit (reverse @{$res} ) {
1857 my $macaddress= lc @{$hit}[8];
1858 my $headertag= @{$hit}[5];
1859 if(
1860 defined($hits->{$macaddress}) &&
1861 defined($hits->{$macaddress}->{$headertag}) &&
1862 defined($hits->{$macaddress}->{$headertag}[0])
1863 ) {
1864 push @drops, "DELETE FROM $job_queue_tn WHERE id = $hits->{$macaddress}->{$headertag}[0]";
1865 }
1866 $hits->{$macaddress}->{$headertag}= $hit;
1867 }
1869 # Delete new jobs with a matching job in state 'processing'
1870 foreach my $macaddress (keys %{$hits}) {
1871 foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1872 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1873 if(defined($jobdb_id)) {
1874 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND headertag='$jobdb_headertag' AND status='processing'";
1875 my $res = $job_db->exec_statement( $sql_statement );
1876 foreach my $hit (@{$res}) {
1877 push @drops, "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1878 }
1879 } else {
1880 daemon_log("J ERROR: Job without id exists for macaddress $macaddress!", 1);
1881 }
1882 }
1883 }
1885 # Commit deletion
1886 $job_db->exec_statementlist(\@drops);
1888 # Look for new jobs that could be executed
1889 foreach my $macaddress (keys %{$hits}) {
1891 # Look if there is an executing job
1892 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='processing'";
1893 my $res = $job_db->exec_statement( $sql_statement );
1895 # Skip new jobs for host if there is a processing job
1896 if(defined($res) and defined @{$res}[0]) {
1897 # Prevent race condition if there is a trigger_activate job waiting and a goto-activation job processing
1898 my $row = @{$res}[0] if (ref $res eq 'ARRAY');
1899 if(@{$row}[5] eq 'trigger_action_reinstall') {
1900 my $sql_statement_2 = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='waiting' AND headertag = 'trigger_activate_new'";
1901 my $res_2 = $job_db->exec_statement( $sql_statement_2 );
1902 if(defined($res_2) and defined @{$res_2}[0]) {
1903 # Set status from goto-activation to 'waiting' and update timestamp
1904 $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'");
1905 }
1906 }
1907 next;
1908 }
1910 foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1911 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1912 if(defined($jobdb_id)) {
1913 my $job_msg = @{$hits->{$macaddress}->{$jobdb_headertag}}[7];
1915 daemon_log("J DEBUG: its time to execute $job_msg", 7);
1916 my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1917 my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1919 # expect macaddress is unique!!!!!!
1920 my $target = $res_hash->{1}->{hostname};
1922 # change header
1923 $job_msg =~ s/<header>job_/<header>gosa_/;
1925 # add sqlite_id
1926 $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1928 $job_msg =~ /<header>(\S+)<\/header>/;
1929 my $header = $1 ;
1930 my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");
1932 # update status in job queue to ...
1933 # ... 'processing', for jobs: 'reinstall', 'update'
1934 if (($header =~ /gosa_trigger_action_reinstall/)
1935 || ($header =~ /gosa_trigger_activate_new/)
1936 || ($header =~ /gosa_trigger_action_update/)) {
1937 my $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
1938 my $dbres = $job_db->update_dbentry($sql_statement);
1939 }
1941 # ... 'done', for all other jobs, they are no longer needed in the jobqueue
1942 else {
1943 my $sql_statement = "UPDATE $job_queue_tn SET status='done' WHERE id=$jobdb_id";
1944 my $dbres = $job_db->update_dbentry($sql_statement);
1945 }
1948 # We don't want parallel processing
1949 last;
1950 }
1951 }
1952 }
1954 $watch_for_new_jobs_in_progress = 0;
1955 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1956 }
1957 }
1960 sub watch_for_new_messages {
1961 my ($kernel,$heap) = @_[KERNEL, HEAP];
1962 my @coll_user_msg; # collection list of outgoing messages
1964 # check messaging_db for new incoming messages with executable timestamp
1965 my $timestamp = &get_time();
1966 my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS UNSIGNED))<$timestamp AND flag='n' AND direction='in' )";
1967 my $res = $messaging_db->exec_statement( $sql_statement );
1968 foreach my $hit (@{$res}) {
1970 # create outgoing messages
1971 my $message_to = @{$hit}[3];
1972 # translate message_to to plain login name
1973 my @message_to_l = split(/,/, $message_to);
1974 my %receiver_h;
1975 foreach my $receiver (@message_to_l) {
1976 if ($receiver =~ /^u_([\s\S]*)$/) {
1977 $receiver_h{$1} = 0;
1978 } elsif ($receiver =~ /^g_([\s\S]*)$/) {
1979 my $group_name = $1;
1980 # fetch all group members from ldap and add them to receiver hash
1981 my $ldap_handle = &get_ldap_handle();
1982 if (defined $ldap_handle) {
1983 my $mesg = $ldap_handle->search(
1984 base => $ldap_base,
1985 scope => 'sub',
1986 attrs => ['memberUid'],
1987 filter => "cn=$group_name",
1988 );
1989 if ($mesg->count) {
1990 my @entries = $mesg->entries;
1991 foreach my $entry (@entries) {
1992 my @receivers= $entry->get_value("memberUid");
1993 foreach my $receiver (@receivers) {
1994 $receiver_h{$receiver} = 0;
1995 }
1996 }
1997 }
1998 # translating errors ?
1999 if ($mesg->code) {
2000 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: $mesg->error", 1);
2001 }
2002 &release_ldap_handle($ldap_handle);
2003 # ldap handle error ?
2004 } else {
2005 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: no ldap handle available", 1);
2006 }
2007 } else {
2008 my $sbjct = &encode_base64(@{$hit}[1]);
2009 my $msg = &encode_base64(@{$hit}[7]);
2010 &daemon_log("M WARNING: unknown receiver '$receiver' for a user-message '$sbjct - $msg'", 3);
2011 }
2012 }
2013 my @receiver_l = keys(%receiver_h);
2015 my $message_id = @{$hit}[0];
2017 #add each outgoing msg to messaging_db
2018 my $receiver;
2019 foreach $receiver (@receiver_l) {
2020 my $sql_statement = "INSERT INTO $messaging_tn (id, subject, message_from, message_to, flag, direction, delivery_time, message, timestamp) ".
2021 "VALUES ('".
2022 $message_id."', '". # id
2023 @{$hit}[1]."', '". # subject
2024 @{$hit}[2]."', '". # message_from
2025 $receiver."', '". # message_to
2026 "none"."', '". # flag
2027 "out"."', '". # direction
2028 @{$hit}[6]."', '". # delivery_time
2029 @{$hit}[7]."', '". # message
2030 $timestamp."'". # timestamp
2031 ")";
2032 &daemon_log("M DEBUG: $sql_statement", 1);
2033 my $res = $messaging_db->exec_statement($sql_statement);
2034 &daemon_log("M INFO: message '".@{$hit}[0]."' is prepared for delivery to receiver '$receiver'", 5);
2035 }
2037 # set incoming message to flag d=deliverd
2038 $sql_statement = "UPDATE $messaging_tn SET flag='p' WHERE id='$message_id'";
2039 &daemon_log("M DEBUG: $sql_statement", 7);
2040 $res = $messaging_db->update_dbentry($sql_statement);
2041 &daemon_log("M INFO: message '$message_id' is set to flag 'p' (processed)", 5);
2042 }
2044 $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
2045 return;
2046 }
2048 sub watch_for_delivery_messages {
2049 my ($kernel, $heap) = @_[KERNEL, HEAP];
2051 # select outgoing messages
2052 my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( flag='p' AND direction='out' )";
2053 my $res = $messaging_db->exec_statement( $sql_statement );
2055 # build out msg for each usr
2056 foreach my $hit (@{$res}) {
2057 my $receiver = @{$hit}[3];
2058 my $msg_id = @{$hit}[0];
2059 my $subject = @{$hit}[1];
2060 my $message = @{$hit}[7];
2062 # resolve usr -> host where usr is logged in
2063 my $sql = "SELECT * FROM $login_users_tn WHERE (user='$receiver')";
2064 my $res = $login_users_db->exec_statement($sql);
2066 # receiver is logged in nowhere
2067 if (not ref(@$res[0]) eq "ARRAY") { next; }
2069 # receiver ist logged in at a client registered at local server
2070 my $send_succeed = 0;
2071 foreach my $hit (@$res) {
2072 my $receiver_host = @$hit[0];
2073 my $delivered2host = 0;
2074 &daemon_log("M DEBUG: user '$receiver' is logged in at host '$receiver_host'", 7);
2076 # Looking for host in know_clients_db
2077 my $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$receiver_host')";
2078 my $res = $known_clients_db->exec_statement($sql);
2080 # Host is known in known_clients_db
2081 if (ref(@$res[0]) eq "ARRAY") {
2082 my $receiver_key = @{@{$res}[0]}[2];
2083 my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
2084 my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data );
2085 my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0);
2086 if ($error == 0 ) {
2087 $send_succeed++ ;
2088 $delivered2host++ ;
2089 &daemon_log("M DEBUG: send message for user '$receiver' to host '$receiver_host'", 7);
2090 } else {
2091 &daemon_log("M DEBUG: cannot send message for user '$receiver' to host '$receiver_host'", 7);
2092 }
2093 }
2095 # Message already send, do not need to do anything more, otherwise ...
2096 if ($delivered2host) { next;}
2098 # ...looking for host in foreign_clients_db
2099 $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$receiver_host')";
2100 $res = $foreign_clients_db->exec_statement($sql);
2102 # Host is known in foreign_clients_db
2103 if (ref(@$res[0]) eq "ARRAY") {
2104 my $registration_server = @{@{$res}[0]}[2];
2106 # Fetch encryption key for registration server
2107 my $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$registration_server')";
2108 my $res = $known_server_db->exec_statement($sql);
2109 if (ref(@$res[0]) eq "ARRAY") {
2110 my $registration_server_key = @{@{$res}[0]}[3];
2111 my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
2112 my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data );
2113 my $error = &send_msg_to_target($out_msg, $registration_server, $registration_server_key, "usr_msg", 0);
2114 if ($error == 0 ) {
2115 $send_succeed++ ;
2116 $delivered2host++ ;
2117 &daemon_log("M DEBUG: send message for user '$receiver' to server '$registration_server'", 7);
2118 } else {
2119 &daemon_log("M ERROR: cannot send message for user '$receiver' to server '$registration_server'", 1);
2120 }
2122 } else {
2123 &daemon_log("M ERROR: host '$receiver_host' is reported to be ".
2124 "registrated at server '$registration_server', ".
2125 "but no data available in known_server_db ", 1);
2126 }
2127 }
2129 if (not $delivered2host) {
2130 &daemon_log("M ERROR: unable to send user message to host '$receiver_host'", 1);
2131 }
2132 }
2134 if ($send_succeed) {
2135 # set outgoing msg at db to deliverd
2136 my $sql = "UPDATE $messaging_tn SET flag='d' WHERE (id='$msg_id' AND direction='out' AND message_to='$receiver')";
2137 my $res = $messaging_db->exec_statement($sql);
2138 &daemon_log("M INFO: send message for user '$receiver' to logged in hosts", 5);
2139 } else {
2140 &daemon_log("M WARNING: failed to deliver message for user '$receiver'", 3);
2141 }
2142 }
2144 $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
2145 return;
2146 }
2149 sub watch_for_done_messages {
2150 my ($kernel,$heap) = @_[KERNEL, HEAP];
2152 my $sql = "SELECT * FROM $messaging_tn WHERE (flag='p' AND direction='in')";
2153 my $res = $messaging_db->exec_statement($sql);
2155 foreach my $hit (@{$res}) {
2156 my $msg_id = @{$hit}[0];
2158 my $sql = "SELECT * FROM $messaging_tn WHERE (id='$msg_id' AND direction='out' AND (NOT flag='s'))";
2159 my $res = $messaging_db->exec_statement($sql);
2161 # not all usr msgs have been seen till now
2162 if ( ref(@$res[0]) eq "ARRAY") { next; }
2164 $sql = "DELETE FROM $messaging_tn WHERE (id='$msg_id')";
2165 $res = $messaging_db->exec_statement($sql);
2167 }
2169 $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
2170 return;
2171 }
2174 sub watch_for_old_known_clients {
2175 my ($kernel,$heap) = @_[KERNEL, HEAP];
2177 my $sql_statement = "SELECT * FROM $known_clients_tn";
2178 my $res = $known_clients_db->select_dbentry( $sql_statement );
2180 my $cur_time = int(&get_time());
2182 while ( my ($hit_num, $hit) = each %$res) {
2183 my $expired_timestamp = int($hit->{'timestamp'});
2184 $expired_timestamp =~ /(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/;
2185 my $dt = DateTime->new( year => $1,
2186 month => $2,
2187 day => $3,
2188 hour => $4,
2189 minute => $5,
2190 second => $6,
2191 );
2193 $dt->add( seconds => 2 * int($hit->{'keylifetime'}) );
2194 $expired_timestamp = $dt->ymd('').$dt->hms('');
2195 if ($cur_time > $expired_timestamp) {
2196 my $hostname = $hit->{'hostname'};
2197 my $del_sql = "DELETE FROM $known_clients_tn WHERE hostname='$hostname'";
2198 my $del_res = $known_clients_db->exec_statement($del_sql);
2200 &main::daemon_log("0 INFO: timestamp '".$hit->{'timestamp'}."' of client '$hostname' is expired('$expired_timestamp'), client will be deleted from known_clients_db", 5);
2201 }
2203 }
2205 $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
2206 }
2209 sub watch_for_next_tasks {
2210 my ($kernel,$heap) = @_[KERNEL, HEAP];
2212 my $sql = "SELECT * FROM $incoming_tn";
2213 my $res = $incoming_db->select_dbentry($sql);
2215 while ( my ($hit_num, $hit) = each %$res) {
2216 my $headertag = $hit->{'headertag'};
2217 if ($headertag =~ /^answer_(\d+)/) {
2218 # do not start processing, this message is for a still running POE::Wheel
2219 next;
2220 }
2221 my $message_id = $hit->{'id'};
2222 my $session_id = $hit->{'sessionid'};
2223 &daemon_log("$session_id DEBUG: start processing for message with incoming id: '$message_id'", 11);
2225 $kernel->yield('next_task', $hit);
2227 my $sql = "DELETE FROM $incoming_tn WHERE id=$message_id";
2228 my $res = $incoming_db->exec_statement($sql);
2229 }
2231 $kernel->delay_set('watch_for_next_tasks', 1);
2232 }
2235 sub get_ldap_handle {
2236 my ($session_id) = @_;
2237 my $heap;
2239 if (not defined $session_id ) { $session_id = 0 };
2240 if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
2242 my ($package, $file, $row, $subroutine, $hasArgs, $wantArray, $evalText, $isRequire) = caller(1);
2243 my $caller_text = "subroutine $subroutine";
2244 if ($subroutine eq "(eval)") {
2245 $caller_text = "eval block within file '$file' for '$evalText'";
2246 }
2247 daemon_log("$session_id DEBUG: new ldap handle for '$caller_text' required!", 42);
2249 get_handle:
2250 my $ldap_handle = Net::LDAP->new( $ldap_uri );
2251 if (not ref $ldap_handle) {
2252 daemon_log("$session_id ERROR: Connection to LDAP URI '$ldap_uri' failed! Retrying!", 1);
2253 usleep(100000);
2254 goto get_handle;
2255 } else {
2256 daemon_log("$session_id DEBUG: Connection to LDAP URI '$ldap_uri' established.", 42);
2257 }
2259 $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);
2260 return $ldap_handle;
2261 }
2264 sub release_ldap_handle {
2265 my ($ldap_handle, $session_id) = @_ ;
2266 if (not defined $session_id ) { $session_id = 0 };
2268 if(ref $ldap_handle) {
2269 $ldap_handle->disconnect();
2270 }
2271 &main::daemon_log("$session_id DEBUG: Released a ldap handle!", 42);
2272 return;
2273 }
2276 sub change_fai_state {
2277 my ($st, $targets, $session_id) = @_;
2278 $session_id = 0 if not defined $session_id;
2279 # Set FAI state to localboot
2280 my %mapActions= (
2281 reboot => '',
2282 update => 'softupdate',
2283 localboot => 'localboot',
2284 reinstall => 'install',
2285 rescan => '',
2286 wake => '',
2287 memcheck => 'memcheck',
2288 sysinfo => 'sysinfo',
2289 install => 'install',
2290 );
2292 # Return if this is unknown
2293 if (!exists $mapActions{ $st }){
2294 daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1);
2295 return;
2296 }
2298 my $state= $mapActions{ $st };
2300 # Build search filter for hosts
2301 my $search= "(&(objectClass=GOhard)";
2302 foreach (@{$targets}){
2303 $search.= "(macAddress=$_)";
2304 }
2305 $search.= ")";
2307 # If there's any host inside of the search string, procress them
2308 if (!($search =~ /macAddress/)){
2309 daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);
2310 return;
2311 }
2313 my $ldap_handle = &get_ldap_handle($session_id);
2314 # Perform search for Unit Tag
2315 my $mesg = $ldap_handle->search(
2316 base => $ldap_base,
2317 scope => 'sub',
2318 attrs => ['dn', 'FAIstate', 'objectClass'],
2319 filter => "$search"
2320 );
2322 if ($mesg->count) {
2323 my @entries = $mesg->entries;
2324 if (0 == @entries) {
2325 daemon_log("$session_id ERROR: ldap search failed: ldap_base=$ldap_base, filter=$search", 1);
2326 }
2328 foreach my $entry (@entries) {
2329 # Only modify entry if it is not set to '$state'
2330 if ($entry->get_value("FAIstate") ne "$state"){
2331 daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
2332 my $result;
2333 my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
2334 if (exists $tmp{'FAIobject'}){
2335 if ($state eq ''){
2336 $result= $ldap_handle->modify($entry->dn, changes => [ delete => [ FAIstate => [] ] ]);
2337 } else {
2338 $result= $ldap_handle->modify($entry->dn, changes => [ replace => [ FAIstate => $state ] ]);
2339 }
2340 } elsif ($state ne ''){
2341 $result= $ldap_handle->modify($entry->dn, changes => [ add => [ objectClass => 'FAIobject' ], add => [ FAIstate => $state ] ]);
2342 }
2344 # Errors?
2345 if ($result->code){
2346 daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2347 }
2348 } else {
2349 daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 42);
2350 }
2351 }
2352 } else {
2353 daemon_log("$session_id ERROR: LDAP search failed: ldap_base=$ldap_base, filter=$search", 1);
2354 }
2355 &release_ldap_handle($ldap_handle, $session_id);
2357 return;
2358 }
2361 sub change_goto_state {
2362 my ($st, $targets, $session_id) = @_;
2363 $session_id = 0 if not defined $session_id;
2365 # Switch on or off?
2366 my $state= $st eq 'active' ? 'active': 'locked';
2368 my $ldap_handle = &get_ldap_handle($session_id);
2369 if( defined($ldap_handle) ) {
2371 # Build search filter for hosts
2372 my $search= "(&(objectClass=GOhard)";
2373 foreach (@{$targets}){
2374 $search.= "(macAddress=$_)";
2375 }
2376 $search.= ")";
2378 # If there's any host inside of the search string, procress them
2379 if (!($search =~ /macAddress/)){
2380 &release_ldap_handle($ldap_handle);
2381 return;
2382 }
2384 # Perform search for Unit Tag
2385 my $mesg = $ldap_handle->search(
2386 base => $ldap_base,
2387 scope => 'sub',
2388 attrs => ['dn', 'gotoMode'],
2389 filter => "$search"
2390 );
2392 if ($mesg->count) {
2393 my @entries = $mesg->entries;
2394 foreach my $entry (@entries) {
2396 # Only modify entry if it is not set to '$state'
2397 if ($entry->get_value("gotoMode") ne $state){
2399 daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
2400 my $result;
2401 $result= $ldap_handle->modify($entry->dn, changes => [replace => [ gotoMode => $state ] ]);
2403 # Errors?
2404 if ($result->code){
2405 &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2406 }
2408 }
2409 }
2410 } else {
2411 daemon_log("$session_id ERROR: LDAP search failed in function change_goto_state: ldap_base=$ldap_base, filter=$search", 1);
2412 }
2414 }
2415 &release_ldap_handle($ldap_handle, $session_id);
2416 return;
2417 }
2420 sub run_recreate_packages_db {
2421 my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2422 my $session_id = $session->ID;
2423 &main::daemon_log("$session_id INFO: Recreating FAI Packages DB ('$fai_release_tn', '$fai_server_tn', '$packages_list_tn')", 5);
2424 $kernel->yield('create_fai_release_db', $fai_release_tn);
2425 $kernel->yield('create_fai_server_db', $fai_server_tn);
2426 return;
2427 }
2430 sub run_create_fai_server_db {
2431 my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2432 my $session_id = $session->ID;
2433 my $task = POE::Wheel::Run->new(
2434 Program => sub { &create_fai_server_db($table_name,$kernel, undef, $session_id) },
2435 StdoutEvent => "session_run_result",
2436 StderrEvent => "session_run_debug",
2437 CloseEvent => "session_run_done",
2438 );
2440 $heap->{task}->{ $task->ID } = $task;
2441 return;
2442 }
2445 sub create_fai_server_db {
2446 my ($table_name, $kernel, $dont_create_packages_list, $session_id) = @_;
2447 my $result;
2449 if (not defined $session_id) { $session_id = 0; }
2450 my $ldap_handle = &get_ldap_handle($session_id);
2451 if(defined($ldap_handle)) {
2452 daemon_log("$session_id INFO: create_fai_server_db: start", 5);
2453 my $mesg= $ldap_handle->search(
2454 base => $ldap_base,
2455 scope => 'sub',
2456 attrs => ['FAIrepository', 'gosaUnitTag'],
2457 filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
2458 );
2459 if($mesg->{'resultCode'} == 0 &&
2460 $mesg->count != 0) {
2461 foreach my $entry (@{$mesg->{entries}}) {
2462 if($entry->exists('FAIrepository')) {
2463 # Add an entry for each Repository configured for server
2464 foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
2465 my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
2466 my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
2467 $result= $fai_server_db->add_dbentry( {
2468 table => $table_name,
2469 primkey => ['server', 'fai_release', 'tag'],
2470 server => $tmp_url,
2471 fai_release => $tmp_release,
2472 sections => $tmp_sections,
2473 tag => (length($tmp_tag)>0)?$tmp_tag:"",
2474 } );
2475 }
2476 }
2477 }
2478 }
2479 daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
2480 &release_ldap_handle($ldap_handle);
2482 # TODO: Find a way to post the 'create_packages_list_db' event
2483 if(not defined($dont_create_packages_list)) {
2484 &create_packages_list_db(undef, $session_id);
2485 }
2486 }
2488 return $result;
2489 }
2492 sub run_create_fai_release_db {
2493 my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2494 my $session_id = $session->ID;
2495 my $task = POE::Wheel::Run->new(
2496 Program => sub { &create_fai_release_db($table_name, $session_id) },
2497 StdoutEvent => "session_run_result",
2498 StderrEvent => "session_run_debug",
2499 CloseEvent => "session_run_done",
2500 );
2502 $heap->{task}->{ $task->ID } = $task;
2503 return;
2504 }
2507 sub create_fai_release_db {
2508 my ($table_name, $session_id) = @_;
2509 my $result;
2511 # used for logging
2512 if (not defined $session_id) { $session_id = 0; }
2514 my $ldap_handle = &get_ldap_handle($session_id);
2515 if(defined($ldap_handle)) {
2516 daemon_log("$session_id INFO: create_fai_release_db: start",5);
2517 my $mesg= $ldap_handle->search(
2518 base => $ldap_base,
2519 scope => 'sub',
2520 attrs => [],
2521 filter => "(&(objectClass=organizationalUnit)(ou=fai))",
2522 );
2523 if(($mesg->code == 0) && ($mesg->count != 0))
2524 {
2525 daemon_log("$session_id DEBUG: create_fai_release_db: count " . $mesg->count,138);
2527 # Walk through all possible FAI container ou's
2528 my @sql_list;
2529 my $timestamp= &get_time();
2530 foreach my $ou (@{$mesg->{entries}}) {
2531 my $tmp_classes= resolve_fai_classes($ou->dn, $ldap_handle, $session_id);
2532 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
2533 my @tmp_array=get_fai_release_entries($tmp_classes);
2534 if(@tmp_array) {
2535 foreach my $entry (@tmp_array) {
2536 if(defined($entry) && ref($entry) eq 'HASH') {
2537 my $sql=
2538 "INSERT INTO $table_name "
2539 ."(timestamp, fai_release, class, type, state) VALUES ("
2540 .$timestamp.","
2541 ."'".$entry->{'release'}."',"
2542 ."'".$entry->{'class'}."',"
2543 ."'".$entry->{'type'}."',"
2544 ."'".$entry->{'state'}."')";
2545 push @sql_list, $sql;
2546 }
2547 }
2548 }
2549 }
2550 }
2552 daemon_log("$session_id DEBUG: create_fai_release_db: Inserting ".scalar @sql_list." entries to DB",138);
2553 &release_ldap_handle($ldap_handle);
2554 if(@sql_list) {
2555 unshift @sql_list, "VACUUM";
2556 unshift @sql_list, "DELETE FROM $table_name";
2557 $fai_release_db->exec_statementlist(\@sql_list);
2558 }
2559 daemon_log("$session_id DEBUG: create_fai_release_db: Done with inserting",138);
2560 } else {
2561 daemon_log("$session_id INFO: create_fai_release_db: error: " . $mesg->code, 5);
2562 }
2563 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
2564 }
2565 return $result;
2566 }
2568 sub get_fai_types {
2569 my $tmp_classes = shift || return undef;
2570 my @result;
2572 foreach my $type(keys %{$tmp_classes}) {
2573 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
2574 my $entry = {
2575 type => $type,
2576 state => $tmp_classes->{$type}[0],
2577 };
2578 push @result, $entry;
2579 }
2580 }
2582 return @result;
2583 }
2585 sub get_fai_state {
2586 my $result = "";
2587 my $tmp_classes = shift || return $result;
2589 foreach my $type(keys %{$tmp_classes}) {
2590 if(defined($tmp_classes->{$type}[0])) {
2591 $result = $tmp_classes->{$type}[0];
2593 # State is equal for all types in class
2594 last;
2595 }
2596 }
2598 return $result;
2599 }
2601 sub resolve_fai_classes {
2602 my ($fai_base, $ldap_handle, $session_id) = @_;
2603 if (not defined $session_id) { $session_id = 0; }
2604 my $result;
2605 my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
2606 my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
2607 my $fai_classes;
2609 daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base", 138);
2610 my $mesg= $ldap_handle->search(
2611 base => $fai_base,
2612 scope => 'sub',
2613 attrs => ['cn','objectClass','FAIstate'],
2614 filter => $fai_filter,
2615 );
2616 daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries", 138);
2618 if($mesg->{'resultCode'} == 0 &&
2619 $mesg->count != 0) {
2620 foreach my $entry (@{$mesg->{entries}}) {
2621 if($entry->exists('cn')) {
2622 my $tmp_dn= $entry->dn();
2623 $tmp_dn= substr( $tmp_dn, 0, length($tmp_dn)
2624 - length($fai_base) - 1 );
2626 # Skip classname and ou dn parts for class
2627 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?)$/;
2629 # Skip classes without releases
2630 if((!defined($tmp_release)) || length($tmp_release)==0) {
2631 next;
2632 }
2634 my $tmp_cn= $entry->get_value('cn');
2635 my $tmp_state= $entry->get_value('FAIstate');
2637 my $tmp_type;
2638 # Get FAI type
2639 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
2640 if(grep $_ eq $oclass, @possible_fai_classes) {
2641 $tmp_type= $oclass;
2642 last;
2643 }
2644 }
2646 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2647 # A Subrelease
2648 my @sub_releases = split(/,/, $tmp_release);
2650 # Walk through subreleases and build hash tree
2651 my $hash;
2652 while(my $tmp_sub_release = pop @sub_releases) {
2653 $hash .= "\{'$tmp_sub_release'\}->";
2654 }
2655 eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
2656 } else {
2657 # A branch, no subrelease
2658 push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
2659 }
2660 } elsif (!$entry->exists('cn')) {
2661 my $tmp_dn= $entry->dn();
2662 $tmp_dn= substr( $tmp_dn, 0, length($tmp_dn)
2663 - length($fai_base) - 1 );
2664 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?)$/;
2666 # Skip classes without releases
2667 if((!defined($tmp_release)) || length($tmp_release)==0) {
2668 next;
2669 }
2671 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2672 # A Subrelease
2673 my @sub_releases= split(/,/, $tmp_release);
2675 # Walk through subreleases and build hash tree
2676 my $hash;
2677 while(my $tmp_sub_release = pop @sub_releases) {
2678 $hash .= "\{'$tmp_sub_release'\}->";
2679 }
2680 # Remove the last two characters
2681 chop($hash);
2682 chop($hash);
2684 eval('$fai_classes->'.$hash.'= {}');
2685 } else {
2686 # A branch, no subrelease
2687 if(!exists($fai_classes->{$tmp_release})) {
2688 $fai_classes->{$tmp_release} = {};
2689 }
2690 }
2691 }
2692 }
2694 # The hash is complete, now we can honor the copy-on-write based missing entries
2695 foreach my $release (keys %$fai_classes) {
2696 $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
2697 }
2698 }
2699 return $result;
2700 }
2702 sub apply_fai_inheritance {
2703 my $fai_classes = shift || return {};
2704 my $tmp_classes;
2706 # Get the classes from the branch
2707 foreach my $class (keys %{$fai_classes}) {
2708 # Skip subreleases
2709 if($class =~ /^ou=.*$/) {
2710 next;
2711 } else {
2712 $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
2713 }
2714 }
2716 # Apply to each subrelease
2717 foreach my $subrelease (keys %{$fai_classes}) {
2718 if($subrelease =~ /ou=/) {
2719 foreach my $tmp_class (keys %{$tmp_classes}) {
2720 if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
2721 $fai_classes->{$subrelease}->{$tmp_class} =
2722 deep_copy($tmp_classes->{$tmp_class});
2723 } else {
2724 foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
2725 if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
2726 $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
2727 deep_copy($tmp_classes->{$tmp_class}->{$type});
2728 }
2729 }
2730 }
2731 }
2732 }
2733 }
2735 # Find subreleases in deeper levels
2736 foreach my $subrelease (keys %{$fai_classes}) {
2737 if($subrelease =~ /ou=/) {
2738 foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
2739 if($subsubrelease =~ /ou=/) {
2740 apply_fai_inheritance($fai_classes->{$subrelease});
2741 }
2742 }
2743 }
2744 }
2746 return $fai_classes;
2747 }
2749 sub get_fai_release_entries {
2750 my $tmp_classes = shift || return;
2751 my $parent = shift || "";
2752 my @result = shift || ();
2754 foreach my $entry (keys %{$tmp_classes}) {
2755 if(defined($entry)) {
2756 if($entry =~ /^ou=.*$/) {
2757 my $release_name = $entry;
2758 $release_name =~ s/ou=//g;
2759 if(length($parent)>0) {
2760 $release_name = $parent."/".$release_name;
2761 }
2762 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2763 foreach my $bufentry(@bufentries) {
2764 push @result, $bufentry;
2765 }
2766 } else {
2767 my @types = get_fai_types($tmp_classes->{$entry});
2768 foreach my $type (@types) {
2769 push @result,
2770 {
2771 'class' => $entry,
2772 'type' => $type->{'type'},
2773 'release' => $parent,
2774 'state' => $type->{'state'},
2775 };
2776 }
2777 }
2778 }
2779 }
2781 return @result;
2782 }
2784 sub deep_copy {
2785 my $this = shift;
2786 if (not ref $this) {
2787 $this;
2788 } elsif (ref $this eq "ARRAY") {
2789 [map deep_copy($_), @$this];
2790 } elsif (ref $this eq "HASH") {
2791 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2792 } else { die "what type is $_?" }
2793 }
2796 sub session_run_result {
2797 my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];
2798 $kernel->sig(CHLD => "child_reap");
2799 }
2801 sub session_run_debug {
2802 my $result = $_[ARG0];
2803 print STDERR "$result\n";
2804 }
2806 sub session_run_done {
2807 my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2808 delete $heap->{task}->{$task_id};
2809 if (exists $heap->{ldap_handle}->{$task_id}) {
2810 &release_ldap_handle($heap->{ldap_handle}->{$task_id});
2811 }
2812 delete $heap->{ldap_handle}->{$task_id};
2813 }
2816 sub create_sources_list {
2817 my $session_id = shift || 0;
2818 my $result="/tmp/gosa_si_tmp_sources_list";
2820 # Remove old file
2821 if(stat($result)) {
2822 unlink($result);
2823 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7);
2824 }
2826 my $fh;
2827 open($fh, ">$result");
2828 if (not defined $fh) {
2829 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7);
2830 return undef;
2831 }
2832 if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2833 my $ldap_handle = &get_ldap_handle($session_id);
2834 my $mesg=$ldap_handle->search(
2835 base => $main::ldap_server_dn,
2836 scope => 'base',
2837 attrs => 'FAIrepository',
2838 filter => 'objectClass=FAIrepositoryServer'
2839 );
2840 if($mesg->count) {
2841 foreach my $entry(@{$mesg->{'entries'}}) {
2842 foreach my $value(@{$entry->get_value('FAIrepository', asref => 1)}) {
2843 my ($server, $tag, $release, $sections)= split /\|/, $value;
2844 my $line = "deb $server $release";
2845 $sections =~ s/,/ /g;
2846 $line.= " $sections";
2847 print $fh $line."\n";
2848 }
2849 }
2850 }
2851 &release_ldap_handle($ldap_handle);
2852 } else {
2853 if (defined $main::ldap_server_dn){
2854 &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1);
2855 } else {
2856 &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2857 }
2858 }
2859 close($fh);
2861 return $result;
2862 }
2865 sub run_create_packages_list_db {
2866 my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2867 my $session_id = $session->ID;
2868 my $task = POE::Wheel::Run->new(
2869 Priority => +20,
2870 Program => sub {&create_packages_list_db(undef, $session_id)},
2871 StdoutEvent => "session_run_result",
2872 StderrEvent => "session_run_debug",
2873 CloseEvent => "session_run_done",
2874 );
2875 $heap->{task}->{ $task->ID } = $task;
2876 }
2879 sub create_packages_list_db {
2880 my ($sources_file, $session_id) = @_;
2882 # it should not be possible to trigger a recreation of packages_list_db
2883 # while packages_list_db is under construction, so set flag packages_list_under_construction
2884 # which is tested befor recreation can be started
2885 if (-r $packages_list_under_construction) {
2886 daemon_log("$session_id WARNING: packages_list_db is right now under construction, please wait until this process is finished", 3);
2887 return;
2888 } else {
2889 daemon_log("$session_id INFO: create_packages_list_db: start", 5);
2890 # set packages_list_under_construction to true
2891 system("touch $packages_list_under_construction");
2892 @packages_list_statements=();
2893 }
2895 if (not defined $session_id) { $session_id = 0; }
2897 if (not defined $sources_file) {
2898 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5);
2899 $sources_file = &create_sources_list($session_id);
2900 }
2902 if (not defined $sources_file) {
2903 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1);
2904 unlink($packages_list_under_construction);
2905 return;
2906 }
2908 my $line;
2910 open(CONFIG, "<$sources_file") or do {
2911 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2912 unlink($packages_list_under_construction);
2913 return;
2914 };
2916 # Read lines
2917 while ($line = <CONFIG>){
2918 # Unify
2919 chop($line);
2920 $line =~ s/^\s+//;
2921 $line =~ s/^\s+/ /;
2923 # Strip comments
2924 $line =~ s/#.*$//g;
2926 # Skip empty lines
2927 if ($line =~ /^\s*$/){
2928 next;
2929 }
2931 # Interpret deb line
2932 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2933 my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2934 my $section;
2935 foreach $section (split(' ', $sections)){
2936 &parse_package_info( $baseurl, $dist, $section, $session_id );
2937 }
2938 }
2939 }
2941 close (CONFIG);
2943 if(keys(%repo_dirs)) {
2944 find(\&cleanup_and_extract, keys( %repo_dirs ));
2945 &main::strip_packages_list_statements();
2946 $packages_list_db->exec_statementlist(\@packages_list_statements);
2947 }
2948 unlink($packages_list_under_construction);
2949 daemon_log("$session_id INFO: create_packages_list_db: finished", 5);
2950 return;
2951 }
2953 # This function should do some intensive task to minimize the db-traffic
2954 sub strip_packages_list_statements {
2955 my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2956 my @new_statement_list=();
2957 my $hash;
2958 my $insert_hash;
2959 my $update_hash;
2960 my $delete_hash;
2961 my $known_packages_hash;
2962 my $local_timestamp=get_time();
2964 foreach my $existing_entry (@existing_entries) {
2965 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
2966 }
2968 foreach my $statement (@packages_list_statements) {
2969 if($statement =~ /^INSERT/i) {
2970 # Assign the values from the insert statement
2971 my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~
2972 /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
2973 if(exists($hash->{$distribution}->{$package}->{$version})) {
2974 # If section or description has changed, update the DB
2975 if(
2976 (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or
2977 (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
2978 ) {
2979 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
2980 } else {
2981 # package is already present in database. cache this knowledge for later use
2982 @{$known_packages_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2983 }
2984 } else {
2985 # Insert a non-existing entry to db
2986 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2987 }
2988 } elsif ($statement =~ /^UPDATE/i) {
2989 my ($template,$package,$version) = ($1,$2,$3) if $statement =~
2990 /^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;
2991 foreach my $distribution (keys %{$hash}) {
2992 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
2993 # update the insertion hash to execute only one query per package (insert instead insert+update)
2994 @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
2995 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
2996 if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
2997 my $section;
2998 my $description;
2999 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
3000 length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
3001 $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
3002 }
3003 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
3004 $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
3005 }
3006 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
3007 }
3008 }
3009 }
3010 }
3011 }
3013 # Check for orphaned entries
3014 foreach my $existing_entry (@existing_entries) {
3015 my $distribution= @{$existing_entry}[0];
3016 my $package= @{$existing_entry}[1];
3017 my $version= @{$existing_entry}[2];
3018 my $section= @{$existing_entry}[3];
3020 if(
3021 exists($insert_hash->{$distribution}->{$package}->{$version}) ||
3022 exists($update_hash->{$distribution}->{$package}->{$version}) ||
3023 exists($known_packages_hash->{$distribution}->{$package}->{$version})
3024 ) {
3025 next;
3026 } else {
3027 # Insert entry to delete hash
3028 @{$delete_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section);
3029 }
3030 }
3032 # unroll the insert hash
3033 foreach my $distribution (keys %{$insert_hash}) {
3034 foreach my $package (keys %{$insert_hash->{$distribution}}) {
3035 foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
3036 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
3037 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
3038 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
3039 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
3040 ."'$local_timestamp')";
3041 }
3042 }
3043 }
3045 # unroll the update hash
3046 foreach my $distribution (keys %{$update_hash}) {
3047 foreach my $package (keys %{$update_hash->{$distribution}}) {
3048 foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
3049 my $set = "";
3050 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
3051 $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
3052 }
3053 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
3054 $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
3055 }
3056 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
3057 $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
3058 }
3059 if(defined($set) and length($set) > 0) {
3060 $set .= "timestamp = '$local_timestamp'";
3061 } else {
3062 next;
3063 }
3064 push @new_statement_list,
3065 "UPDATE $main::packages_list_tn SET $set WHERE"
3066 ." distribution = '$distribution'"
3067 ." AND package = '$package'"
3068 ." AND version = '$version'";
3069 }
3070 }
3071 }
3073 # unroll the delete hash
3074 foreach my $distribution (keys %{$delete_hash}) {
3075 foreach my $package (keys %{$delete_hash->{$distribution}}) {
3076 foreach my $version (keys %{$delete_hash->{$distribution}->{$package}}) {
3077 my $section = @{$delete_hash->{$distribution}->{$package}->{$version}}[3];
3078 push @new_statement_list, "DELETE FROM $main::packages_list_tn WHERE distribution='$distribution' AND package='$package' AND version='$version' AND section='$section'";
3079 }
3080 }
3081 }
3083 unshift(@new_statement_list, "VACUUM");
3085 @packages_list_statements = @new_statement_list;
3086 }
3089 sub parse_package_info {
3090 my ($baseurl, $dist, $section, $session_id)= @_;
3091 my ($package);
3092 if (not defined $session_id) { $session_id = 0; }
3093 my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
3094 $repo_dirs{ "${repo_path}/pool" } = 1;
3096 foreach $package ("Packages.gz"){
3097 daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
3098 get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
3099 parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
3100 }
3102 }
3105 sub get_package {
3106 my ($url, $dest, $session_id)= @_;
3107 if (not defined $session_id) { $session_id = 0; }
3109 my $tpath = dirname($dest);
3110 -d "$tpath" || mkpath "$tpath";
3112 # This is ugly, but I've no time to take a look at "how it works in perl"
3113 if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
3114 system("gunzip -cd '$dest' > '$dest.in'");
3115 daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 7);
3116 unlink($dest);
3117 daemon_log("$session_id DEBUG: delete file '$dest'", 7);
3118 } else {
3119 daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' into '$dest' failed!", 1);
3120 }
3121 return 0;
3122 }
3125 sub parse_package {
3126 my ($path, $dist, $srv_path, $session_id)= @_;
3127 if (not defined $session_id) { $session_id = 0;}
3128 my ($package, $version, $section, $description);
3129 my $PACKAGES;
3130 my $timestamp = &get_time();
3132 if(not stat("$path.in")) {
3133 daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
3134 return;
3135 }
3137 open($PACKAGES, "<$path.in");
3138 if(not defined($PACKAGES)) {
3139 daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1);
3140 return;
3141 }
3143 # Read lines
3144 while (<$PACKAGES>){
3145 my $line = $_;
3146 # Unify
3147 chop($line);
3149 # Use empty lines as a trigger
3150 if ($line =~ /^\s*$/){
3151 my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
3152 push(@packages_list_statements, $sql);
3153 $package = "none";
3154 $version = "none";
3155 $section = "none";
3156 $description = "none";
3157 next;
3158 }
3160 # Trigger for package name
3161 if ($line =~ /^Package:\s/){
3162 ($package)= ($line =~ /^Package: (.*)$/);
3163 next;
3164 }
3166 # Trigger for version
3167 if ($line =~ /^Version:\s/){
3168 ($version)= ($line =~ /^Version: (.*)$/);
3169 next;
3170 }
3172 # Trigger for description
3173 if ($line =~ /^Description:\s/){
3174 ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
3175 next;
3176 }
3178 # Trigger for section
3179 if ($line =~ /^Section:\s/){
3180 ($section)= ($line =~ /^Section: (.*)$/);
3181 next;
3182 }
3184 # Trigger for filename
3185 if ($line =~ /^Filename:\s/){
3186 my ($filename) = ($line =~ /^Filename: (.*)$/);
3187 store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
3188 next;
3189 }
3190 }
3192 close( $PACKAGES );
3193 unlink( "$path.in" );
3194 }
3197 sub store_fileinfo {
3198 my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
3200 my %fileinfo = (
3201 'package' => $package,
3202 'dist' => $dist,
3203 'version' => $vers,
3204 );
3206 $repo_files{ "${srvdir}/$file" } = \%fileinfo;
3207 }
3210 sub cleanup_and_extract {
3211 my $fileinfo = $repo_files{ $File::Find::name };
3213 if( defined $fileinfo ) {
3214 my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
3215 my $sql;
3216 my $package = $fileinfo->{ 'package' };
3217 my $newver = $fileinfo->{ 'version' };
3219 mkpath($dir);
3220 system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
3222 if( -f "$dir/DEBIAN/templates" ) {
3224 daemon_log("0 DEBUG: Found debconf templates in '$package' - $newver", 7);
3226 my $tmpl= ""; {
3227 local $/=undef;
3228 open FILE, "$dir/DEBIAN/templates";
3229 $tmpl = &encode_base64(<FILE>);
3230 close FILE;
3231 }
3232 rmtree("$dir/DEBIAN/templates");
3234 $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
3235 push @packages_list_statements, $sql;
3236 }
3237 }
3239 return;
3240 }
3243 sub register_at_foreign_servers {
3244 my ($kernel) = $_[KERNEL];
3246 # Update status and update-time of all si-server with expired update_time and
3247 # block them for race conditional registration processes of other si-servers.
3248 my $act_time = &get_time();
3249 my $block_statement = "UPDATE $known_server_tn SET status='new_server',update_time='19700101000000' WHERE (CAST(update_time AS UNSIGNED))<$act_time ";
3250 my $block_res = $known_server_db->exec_statement($block_statement);
3252 # Fetch all si-server from db where update_time is younger than act_time
3253 my $fetch_statement = "SELECT * FROM $known_server_tn WHERE update_time='19700101000000'";
3254 my $fetch_res = $known_server_db->exec_statement($fetch_statement);
3256 # Detect already connected clients. Will be added to registration msg later.
3257 my $client_sql = "SELECT * FROM $known_clients_tn";
3258 my $client_res = $known_clients_db->exec_statement($client_sql);
3260 # Send registration messag to all fetched si-server
3261 foreach my $hit (@$fetch_res) {
3262 my $hostname = @$hit[0];
3263 my $hostkey = &create_passwd;
3265 # Add already connected clients to registration message
3266 my $myhash = &create_xml_hash('new_server', $server_address, $hostname);
3267 &add_content2xml_hash($myhash, 'key', $hostkey);
3268 map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res);
3270 # Add locally loaded gosa-si modules to registration message
3271 my $loaded_modules = {};
3272 while (my ($package, $pck_info) = each %$known_modules) {
3273 next if ((!defined(@$pck_info[2])) || (!(ref (@$pck_info[2]) eq 'HASH')));
3274 foreach my $act_module (keys(%{@$pck_info[2]})) {
3275 $loaded_modules->{$act_module} = "";
3276 }
3277 }
3278 map(&add_content2xml_hash($myhash, "loaded_modules", $_), keys(%$loaded_modules));
3280 # Add macaddress to registration message
3281 my ($host_ip, $host_port) = split(/:/, $hostname);
3282 my $local_ip = &get_local_ip_for_remote_ip($host_ip);
3283 my $network_interface= &get_interface_for_ip($local_ip);
3284 my $host_mac = &get_mac_for_interface($network_interface);
3285 &add_content2xml_hash($myhash, 'macaddress', $host_mac);
3287 # Build registration message and send it
3288 my $foreign_server_msg = &create_xml_string($myhash);
3289 my $error = &send_msg_to_target($foreign_server_msg, $hostname, $ServerPackages_key, "new_server", 0);
3290 }
3293 # After n sec perform a check of all server registration processes
3294 $kernel->delay_set("control_server_registration", 2);
3296 return;
3297 }
3300 sub control_server_registration {
3301 my ($kernel) = $_[KERNEL];
3303 # Check if all registration processes succeed or not
3304 my $select_statement = "SELECT * FROM $known_server_tn WHERE status='new_server'";
3305 my $select_res = $known_server_db->exec_statement($select_statement);
3307 # If at least one registration process failed, maybe in case of a race condition
3308 # with a foreign registration process
3309 if (@$select_res > 0)
3310 {
3311 # Release block statement 'new_server' to make the server accessible
3312 # for foreign registration processes
3313 my $update_statement = "UPDATE $known_server_tn SET status='waiting' WHERE status='new_server'";
3314 my $update_res = $known_server_db->exec_statement($update_statement);
3316 # Set a random delay to avoid the registration race condition
3317 my $new_foreign_servers_register_delay = int(rand(4))+1;
3318 $kernel->delay_set("register_at_foreign_servers", $new_foreign_servers_register_delay);
3319 }
3320 # If all registration processes succeed
3321 else
3322 {
3323 $kernel->delay_set("register_at_foreign_servers", $foreign_servers_register_delay);
3324 }
3326 return;
3327 }
3330 #==== MAIN = main ==============================================================
3331 # parse commandline options
3332 Getopt::Long::Configure( "bundling" );
3333 GetOptions("h|help" => \&usage,
3334 "c|config=s" => \$cfg_file,
3335 "f|foreground" => \$foreground,
3336 "v|verbose+" => \$verbose,
3337 "no-arp+" => \$no_arp,
3338 "d=s" => \$debug_parts,
3339 ) or (&usage("", 1)&&(exit(-1)));
3341 # read and set config parameters
3342 &check_cmdline_param ;
3343 &read_configfile($cfg_file, %cfg_defaults);
3344 &check_pid;
3346 $SIG{CHLD} = 'IGNORE';
3348 # forward error messages to logfile
3349 if( ! $foreground ) {
3350 open( STDIN, '+>/dev/null' );
3351 open( STDOUT, '+>&STDIN' );
3352 open( STDERR, '+>&STDIN' );
3353 }
3355 # Just fork, if we are not in foreground mode
3356 if( ! $foreground ) {
3357 chdir '/' or die "Can't chdir to /: $!";
3358 $pid = fork;
3359 setsid or die "Can't start a new session: $!";
3360 umask 0;
3361 } else {
3362 $pid = $$;
3363 }
3365 # Do something useful - put our PID into the pid_file
3366 if( 0 != $pid ) {
3367 open( LOCK_FILE, ">$pid_file" );
3368 print LOCK_FILE "$pid\n";
3369 close( LOCK_FILE );
3370 if( !$foreground ) {
3371 exit( 0 )
3372 };
3373 }
3375 # parse head url and revision from svn
3376 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
3377 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
3378 $server_headURL = defined $1 ? $1 : 'unknown' ;
3379 $server_revision = defined $2 ? $2 : 'unknown' ;
3380 if ($server_headURL =~ /\/tag\// ||
3381 $server_headURL =~ /\/branches\// ) {
3382 $server_status = "stable";
3383 } else {
3384 $server_status = "developmental" ;
3385 }
3386 # Prepare log file and set permissions
3387 $root_uid = getpwnam('root');
3388 $adm_gid = getgrnam('adm');
3389 open(FH, ">>$log_file");
3390 close FH;
3391 chmod(0440, $log_file);
3392 chown($root_uid, $adm_gid, $log_file);
3393 chown($root_uid, $adm_gid, "/var/lib/gosa-si");
3395 daemon_log(" ", 1);
3396 daemon_log("$0 started!", 1);
3397 daemon_log("status: $server_status", 1);
3398 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1);
3400 # Buildup data bases
3401 {
3402 no strict "refs";
3404 if ($db_module eq "DBmysql") {
3405 # connect to incoming_db
3406 $incoming_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3408 # connect to gosa-si job queue
3409 $job_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3411 # connect to known_clients_db
3412 $known_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3414 # connect to foreign_clients_db
3415 $foreign_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3417 # connect to known_server_db
3418 $known_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3420 # connect to login_usr_db
3421 $login_users_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3423 # connect to fai_server_db
3424 $fai_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3426 # connect to fai_release_db
3427 $fai_release_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3429 # connect to packages_list_db
3430 $packages_list_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3432 # connect to messaging_db
3433 $messaging_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3435 } elsif ($db_module eq "DBsqlite") {
3436 # connect to incoming_db
3437 unlink($incoming_file_name);
3438 $incoming_db = GOSA::DBsqlite->new($incoming_file_name);
3439 chmod(0640, $incoming_file_name);
3440 chown($root_uid, $adm_gid, $incoming_file_name);
3442 # connect to gosa-si job queue
3443 $job_db = GOSA::DBsqlite->new($job_queue_file_name);
3444 chmod(0640, $job_queue_file_name);
3445 chown($root_uid, $adm_gid, $job_queue_file_name);
3447 # connect to known_clients_db
3448 #unlink($known_clients_file_name);
3449 $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
3450 chmod(0640, $known_clients_file_name);
3451 chown($root_uid, $adm_gid, $known_clients_file_name);
3453 # connect to foreign_clients_db
3454 #unlink($foreign_clients_file_name);
3455 $foreign_clients_db = GOSA::DBsqlite->new($foreign_clients_file_name);
3456 chmod(0640, $foreign_clients_file_name);
3457 chown($root_uid, $adm_gid, $foreign_clients_file_name);
3459 # connect to known_server_db
3460 #unlink($known_server_file_name);
3461 $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
3462 chmod(0640, $known_server_file_name);
3463 chown($root_uid, $adm_gid, $known_server_file_name);
3465 # connect to login_usr_db
3466 #unlink($login_users_file_name);
3467 $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
3468 chmod(0640, $login_users_file_name);
3469 chown($root_uid, $adm_gid, $login_users_file_name);
3471 # connect to fai_server_db
3472 unlink($fai_server_file_name);
3473 $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
3474 chmod(0640, $fai_server_file_name);
3475 chown($root_uid, $adm_gid, $fai_server_file_name);
3477 # connect to fai_release_db
3478 unlink($fai_release_file_name);
3479 $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
3480 chmod(0640, $fai_release_file_name);
3481 chown($root_uid, $adm_gid, $fai_release_file_name);
3483 # connect to packages_list_db
3484 unlink($packages_list_under_construction);
3485 $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
3486 chmod(0640, $packages_list_file_name);
3487 chown($root_uid, $adm_gid, $packages_list_file_name);
3489 # connect to messaging_db
3490 #unlink($messaging_file_name);
3491 $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
3492 chmod(0640, $messaging_file_name);
3493 chown($root_uid, $adm_gid, $messaging_file_name);
3494 }
3495 }
3498 # Creating tables
3499 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
3500 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
3501 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
3502 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
3503 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
3504 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
3505 $foreign_clients_db->create_table($foreign_clients_tn, \@foreign_clients_col_names);
3506 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
3507 $incoming_db->create_table($incoming_tn, \@incoming_col_names);
3508 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
3510 # create xml object used for en/decrypting
3511 $xml = new XML::Simple();
3514 # foreign servers
3515 my @foreign_server_list;
3517 # add foreign server from cfg file
3518 if ($foreign_server_string ne "") {
3519 my @cfg_foreign_server_list = split(",", $foreign_server_string);
3520 foreach my $foreign_server (@cfg_foreign_server_list) {
3521 push(@foreign_server_list, $foreign_server);
3522 }
3524 daemon_log("0 INFO: found foreign server in config file: ".join(", ", @foreign_server_list), 5);
3525 }
3527 # Perform a DNS lookup for server registration if flag is true
3528 if ($dns_lookup eq "true") {
3529 # Add foreign server from dns
3530 my @tmp_servers;
3531 if (not $server_domain) {
3532 # Try our DNS Searchlist
3533 for my $domain(get_dns_domains()) {
3534 chomp($domain);
3535 my ($tmp_domains, $error_string) = &get_server_addresses($domain);
3536 if(@$tmp_domains) {
3537 for my $tmp_server(@$tmp_domains) {
3538 push @tmp_servers, $tmp_server;
3539 }
3540 }
3541 }
3542 if(@tmp_servers && length(@tmp_servers)==0) {
3543 daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3544 }
3545 } else {
3546 @tmp_servers = &get_server_addresses($server_domain);
3547 if( 0 == @tmp_servers ) {
3548 daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3549 }
3550 }
3552 daemon_log("0 INFO: found foreign server via DNS ".join(", ", @tmp_servers), 5);
3554 foreach my $server (@tmp_servers) {
3555 unshift(@foreign_server_list, $server);
3556 }
3557 } else {
3558 daemon_log("0 INFO: DNS lookup for server registration is disabled", 5);
3559 }
3562 # eliminate duplicate entries
3563 @foreign_server_list = &del_doubles(@foreign_server_list);
3564 my $all_foreign_server = join(", ", @foreign_server_list);
3565 daemon_log("0 INFO: found foreign server in config file and DNS: '$all_foreign_server'", 5);
3567 # add all found foreign servers to known_server
3568 my $cur_timestamp = &get_time();
3569 foreach my $foreign_server (@foreign_server_list) {
3571 # do not add myself to known_server_db
3572 if (&is_local($foreign_server)) { next; }
3573 ######################################
3575 my $res = $known_server_db->add_dbentry( {table=>$known_server_tn,
3576 primkey=>['hostname'],
3577 hostname=>$foreign_server,
3578 macaddress=>"",
3579 status=>'not_yet_registered',
3580 hostkey=>"none",
3581 loaded_modules => "none",
3582 timestamp=>$cur_timestamp,
3583 update_time=>'19700101000000',
3584 } );
3585 }
3588 # Import all modules
3589 &import_modules;
3591 # Check wether all modules are gosa-si valid passwd check
3592 &password_check;
3594 # Create functions hash
3595 #print STDERR Dumper $known_modules;
3596 while (my ($module, @mod_info) = each %$known_modules)
3597 {
3598 #print STDERR Dumper $module;
3599 while (my ($plugin, $functions) = each %{$mod_info[0][2]})
3600 {
3601 #print STDERR Dumper $functions;
3602 while (my ($function, $nothing) = each %$functions )
3603 {
3604 $known_functions->{$function} = $nothing;
3605 }
3606 }
3607 }
3609 # Prepare for using Opsi
3610 if ($opsi_enabled eq "true") {
3611 use JSON::RPC::Client;
3612 use XML::Quote qw(:all);
3613 $opsi_url= "https://".$opsi_admin.":".$opsi_password."@".$opsi_server.":4447/rpc";
3614 $opsi_client = new JSON::RPC::Client;
3615 }
3618 POE::Component::Server::TCP->new(
3619 Alias => "TCP_SERVER",
3620 Port => $server_port,
3621 ClientInput => sub {
3622 my ($kernel, $input, $heap, $session) = @_[KERNEL, ARG0, HEAP, SESSION];
3623 my $session_id = $session->ID;
3624 if ($input =~ /;([\d\.]+:[\d]+)$/)
3625 {
3626 &daemon_log("$session_id DEBUG: incoming message from '$1'", 11);
3627 }
3628 else
3629 {
3630 my $remote_ip = $heap->{'remote_ip'};
3631 &daemon_log("$session_id DEBUG: incoming message from '$remote_ip'", 11);
3632 }
3633 push(@msgs_to_decrypt, $input);
3634 $kernel->yield("msg_to_decrypt");
3635 },
3636 InlineStates => {
3637 msg_to_decrypt => \&msg_to_decrypt,
3638 next_task => \&next_task,
3639 task_result => \&handle_task_result,
3640 task_done => \&handle_task_done,
3641 task_debug => \&handle_task_debug,
3642 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!" },
3643 }
3644 );
3646 daemon_log("0 INFO: start socket for incoming xml messages at port '$server_port' ", 1);
3648 # create session for repeatedly checking the job queue for jobs
3649 POE::Session->create(
3650 inline_states => {
3651 _start => \&session_start,
3652 register_at_foreign_servers => \®ister_at_foreign_servers,
3653 control_server_registration => \&control_server_registration,
3654 sig_handler => \&sig_handler,
3655 next_task => \&next_task,
3656 task_result => \&handle_task_result,
3657 task_done => \&handle_task_done,
3658 task_debug => \&handle_task_debug,
3659 watch_for_next_tasks => \&watch_for_next_tasks,
3660 watch_for_new_messages => \&watch_for_new_messages,
3661 watch_for_delivery_messages => \&watch_for_delivery_messages,
3662 watch_for_done_messages => \&watch_for_done_messages,
3663 watch_for_new_jobs => \&watch_for_new_jobs,
3664 watch_for_modified_jobs => \&watch_for_modified_jobs,
3665 watch_for_done_jobs => \&watch_for_done_jobs,
3666 watch_for_opsi_jobs => \&watch_for_opsi_jobs,
3667 watch_for_old_known_clients => \&watch_for_old_known_clients,
3668 create_packages_list_db => \&run_create_packages_list_db,
3669 create_fai_server_db => \&run_create_fai_server_db,
3670 create_fai_release_db => \&run_create_fai_release_db,
3671 recreate_packages_db => \&run_recreate_packages_db,
3672 session_run_result => \&session_run_result,
3673 session_run_debug => \&session_run_debug,
3674 session_run_done => \&session_run_done,
3675 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!" },
3676 }
3677 );
3680 POE::Kernel->run();
3681 exit;