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