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