Code

Merged changeset:13564.
[gosa.git] / gosa-si / gosa-si-server
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);
53 use ResourcePool;
54 use ResourcePool::Factory::Net::LDAP;
56 # revision number of server and program name
57 my $server_headURL;
58 my $server_revision;
59 my $server_status;
60 our $prg= basename($0);
62 my $db_module = "DBsqlite";
63 {
64 no strict "refs";
65 require ("GOSA/".$db_module.".pm");
66 ("GOSA/".$db_module)->import;
67 daemon_log("0 INFO: importing database module '$db_module'", 1);
68 }
70 my $modules_path = "/usr/lib/gosa-si/modules";
71 use lib "/usr/lib/gosa-si/modules";
73 our $global_kernel;
74 my ($foreground, $ping_timeout);
75 my ($server);
76 my ($gosa_server, $job_queue_timeout, $job_queue_loop_delay);
77 my ($messaging_db_loop_delay);
78 my ($procid, $pid);
79 my ($arp_fifo, $ldap_pool, $ldap_factory);
80 my ($xml);
81 my $sources_list;
82 my $max_clients;
83 my %repo_files=();
84 my $repo_path;
85 my %repo_dirs=();
87 # Variables declared in config file are always set to 'our'
88 our (%cfg_defaults, $log_file, $pid_file, 
89     $server_ip, $server_port, $ClientPackages_key, $dns_lookup,
90     $arp_activ, $gosa_unit_tag,
91     $GosaPackages_key, $gosa_timeout,
92     $foreign_server_string, $server_domain, $ServerPackages_key, $foreign_servers_register_delay,
93     $wake_on_lan_passwd, $job_synchronization, $modified_jobs_loop_delay,
94     $arp_enabled, $arp_interface,
95     $opsi_enabled, $opsi_server, $opsi_admin, $opsi_password,
96                 $new_systems_ou,
97 );
99 # additional variable which should be globaly accessable
100 our $server_address;
101 our $server_mac_address;
102 our $gosa_address;
103 our $no_arp;
104 our $verbose;
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 $root_uid;
111 our $adm_gid;
114 # specifies the verbosity of the daemon_log
115 $verbose = 0 ;
117 # if foreground is not null, script will be not forked to background
118 $foreground = 0 ;
120 # specifies the timeout seconds while checking the online status of a registrating client
121 $ping_timeout = 5;
123 $no_arp = 0;
124 my $packages_list_under_construction = "/tmp/packages_list_creation_in_progress";
125 my @packages_list_statements;
126 my $watch_for_new_jobs_in_progress = 0;
128 # holds all incoming decrypted messages
129 our $incoming_db;
130 our $incoming_tn = 'incoming';
131 my $incoming_file_name;
132 my @incoming_col_names = ("id INTEGER PRIMARY KEY",
133         "timestamp VARCHAR(14) DEFAULT 'none'", 
134         "headertag VARCHAR(255) DEFAULT 'none'",
135         "targettag VARCHAR(255) DEFAULT 'none'",
136         "xmlmessage TEXT",
137         "module VARCHAR(255) DEFAULT 'none'",
138         "sessionid VARCHAR(255) DEFAULT '0'",
139 );
141 # holds all gosa jobs
142 our $job_db;
143 our $job_queue_tn = 'jobs';
144 my $job_queue_file_name;
145 my @job_queue_col_names = ("id INTEGER PRIMARY KEY",
146         "timestamp VARCHAR(14) DEFAULT 'none'", 
147         "status VARCHAR(255) DEFAULT 'none'", 
148         "result TEXT",
149         "progress VARCHAR(255) DEFAULT 'none'",
150         "headertag VARCHAR(255) DEFAULT 'none'",
151         "targettag VARCHAR(255) DEFAULT 'none'", 
152         "xmlmessage TEXT", 
153         "macaddress VARCHAR(17) DEFAULT 'none'",
154         "plainname VARCHAR(255) DEFAULT 'none'",
155         "siserver VARCHAR(255) DEFAULT 'none'",
156         "modified INTEGER DEFAULT '0'",
157 );
159 # holds all other gosa-si-server
160 our $known_server_db;
161 our $known_server_tn = "known_server";
162 my $known_server_file_name;
163 my @known_server_col_names = ("hostname VARCHAR(255)", "macaddress VARCHAR(17)", "status VARCHAR(255)", "hostkey VARCHAR(255)", "loaded_modules TEXT", "timestamp VARCHAR(14)");
165 # holds all registrated clients
166 our $known_clients_db;
167 our $known_clients_tn = "known_clients";
168 my $known_clients_file_name;
169 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)");
171 # holds all registered clients at a foreign server
172 our $foreign_clients_db;
173 our $foreign_clients_tn = "foreign_clients"; 
174 my $foreign_clients_file_name;
175 my @foreign_clients_col_names = ("hostname VARCHAR(255)", "macaddress VARCHAR(17)", "regserver VARCHAR(255)", "timestamp VARCHAR(14)");
177 # holds all logged in user at each client 
178 our $login_users_db;
179 our $login_users_tn = "login_users";
180 my $login_users_file_name;
181 my @login_users_col_names = ("client VARCHAR(255)", "user VARCHAR(255)", "timestamp VARCHAR(14)", "regserver VARCHAR(255) DEFAULT 'localhost'");
183 # holds all fai server, the debian release and tag
184 our $fai_server_db;
185 our $fai_server_tn = "fai_server"; 
186 my $fai_server_file_name;
187 our @fai_server_col_names = ("timestamp VARCHAR(14)", "server VARCHAR(255)", "fai_release VARCHAR(255)", "sections VARCHAR(255)", "tag VARCHAR(255)"); 
189 our $fai_release_db;
190 our $fai_release_tn = "fai_release"; 
191 my $fai_release_file_name;
192 our @fai_release_col_names = ("timestamp VARCHAR(14)", "fai_release VARCHAR(255)", "class VARCHAR(255)", "type VARCHAR(255)", "state VARCHAR(255)"); 
194 # holds all packages available from different repositories
195 our $packages_list_db;
196 our $packages_list_tn = "packages_list";
197 my $packages_list_file_name;
198 our @packages_list_col_names = ("distribution VARCHAR(255)", "package VARCHAR(255)", "version VARCHAR(255)", "section VARCHAR(255)", "description TEXT", "template LONGBLOB", "timestamp VARCHAR(14)");
199 my $outdir = "/tmp/packages_list_db";
200 my $arch = "i386"; 
202 # holds all messages which should be delivered to a user
203 our $messaging_db;
204 our $messaging_tn = "messaging"; 
205 our @messaging_col_names = ("id INTEGER", "subject TEXT", "message_from VARCHAR(255)", "message_to VARCHAR(255)", 
206         "flag VARCHAR(255)", "direction VARCHAR(255)", "delivery_time VARCHAR(255)", "message TEXT", "timestamp VARCHAR(14)" );
207 my $messaging_file_name;
209 # path to directory to store client install log files
210 our $client_fai_log_dir = "/var/log/fai"; 
212 # queue which stores taskes until one of the $max_children children are ready to process the task
213 #my @tasks = qw();
214 my @msgs_to_decrypt = qw();
215 my $max_children = 2;
218 # loop delay for job queue to look for opsi jobs
219 my $job_queue_opsi_delay = 10;
220 our $opsi_client;
221 our $opsi_url;
222  
223 # Lifetime of logged in user information. If no update information comes after n seconds, 
224 # the user is expeceted to be no longer logged in or the host is no longer running. Because
225 # of this, the user is deleted from login_users_db
226 our $logged_in_user_date_of_expiry = 600;
229 %cfg_defaults = (
230 "general" => {
231     "log-file" => [\$log_file, "/var/run/".$prg.".log"],
232     "pid-file" => [\$pid_file, "/var/run/".$prg.".pid"],
233     },
234 "server" => {
235     "ip"                    => [\$server_ip, "0.0.0.0"],
236     "port"                  => [\$server_port, "20081"],
237     "known-clients"         => [\$known_clients_file_name, '/var/lib/gosa-si/clients.db' ],
238     "known-servers"         => [\$known_server_file_name, '/var/lib/gosa-si/servers.db'],
239     "incoming"              => [\$incoming_file_name, '/var/lib/gosa-si/incoming.db'],
240     "login-users"           => [\$login_users_file_name, '/var/lib/gosa-si/users.db'],
241     "fai-server"            => [\$fai_server_file_name, '/var/lib/gosa-si/fai_server.db'],
242     "fai-release"           => [\$fai_release_file_name, '/var/lib/gosa-si/fai_release.db'],
243     "packages-list"         => [\$packages_list_file_name, '/var/lib/gosa-si/packages.db'],
244     "messaging"             => [\$messaging_file_name, '/var/lib/gosa-si/messaging.db'],
245     "foreign-clients"       => [\$foreign_clients_file_name, '/var/lib/gosa-si/foreign_clients.db'],
246     "source-list"           => [\$sources_list, '/etc/apt/sources.list'],
247     "repo-path"             => [\$repo_path, '/srv/www/repository'],
248     "ldap-uri"              => [\$ldap_uri, ""],
249     "ldap-base"             => [\$ldap_base, ""],
250     "ldap-admin-dn"         => [\$ldap_admin_dn, ""],
251     "ldap-admin-password"   => [\$ldap_admin_password, ""],
252         "ldap-version"                  => [\$ldap_version, 3],
253     "gosa-unit-tag"         => [\$gosa_unit_tag, ""],
254     "max-clients"           => [\$max_clients, 10],
255     "wol-password"          => [\$wake_on_lan_passwd, ""],
256         "mysql-username"        => [\$mysql_username, "gosa_si"],
257         "mysql-password"        => [\$mysql_password, ""],
258         "mysql-database"        => [\$mysql_database, "gosa_si"],
259         "mysql-host"            => [\$mysql_host, "127.0.0.1"],
260     },
261 "GOsaPackages" => {
262     "job-queue" => [\$job_queue_file_name, '/var/lib/gosa-si/jobs.db'],
263     "job-queue-loop-delay" => [\$job_queue_loop_delay, 3],
264     "messaging-db-loop-delay" => [\$messaging_db_loop_delay, 3],
265     "key" => [\$GosaPackages_key, "none"],
266                 "new-systems-ou" => [\$new_systems_ou, 'ou=workstations,ou=systems'],
267     },
268 "ClientPackages" => {
269     "key" => [\$ClientPackages_key, "none"],
270     "user-date-of-expiry" => [\$logged_in_user_date_of_expiry, 600],
271     },
272 "ServerPackages"=> {
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]
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            -no-arp   : starts $prg without connection to arp module
310  
311 EOF
312     print "\n" ;
316 #===  FUNCTION  ================================================================
317 #         NAME:  logging
318 #   PARAMETERS:  level - string - default 'info'
319 #                msg - string -
320 #                facility - string - default 'LOG_DAEMON'
321 #      RETURNS:  nothing
322 #  DESCRIPTION:  function for logging
323 #===============================================================================
324 sub daemon_log {
325     # log into log_file
326     my( $msg, $level ) = @_;
327     if(not defined $msg) { return }
328     if(not defined $level) { $level = 1 }
329     if(defined $log_file){
330         my $open_log_fh = sysopen(LOG_HANDLE, $log_file, O_WRONLY | O_CREAT | O_APPEND , 0440);
331         if(not $open_log_fh) {
332             print STDERR "cannot open $log_file: $!";
333             return;
334         }
335         # check owner and group of log_file and update settings if necessary
336         my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = stat($log_file);
337         if((not $uid eq $root_uid) || (not $gid eq $adm_gid)) {
338             chown($root_uid, $adm_gid, $log_file);
339                 }
341         chomp($msg);
342         #$msg =~s/\n//g;   # no newlines are allowed in log messages, this is important for later log parsing
343         if($level <= $verbose){
344             my ($seconds, $minutes, $hours, $monthday, $month,
345                     $year, $weekday, $yearday, $sommertime) = localtime(time);
346             $hours = $hours < 10 ? $hours = "0".$hours : $hours;
347             $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
348             $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
349             my @monthnames = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
350             $month = $monthnames[$month];
351             $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
352             $year+=1900;
353             my $name = $prg;
355             my $log_msg = "$month $monthday $hours:$minutes:$seconds $name $msg\n";
356                         flock(LOG_HANDLE, LOCK_EX);
357                         seek(LOG_HANDLE, 0, 2);
358             print LOG_HANDLE $log_msg;
359                         flock(LOG_HANDLE, LOCK_UN);
360             if( $foreground ) { 
361                 print STDERR $log_msg;
362             }
363         }
364         close( LOG_HANDLE );
365     }
369 #===  FUNCTION  ================================================================
370 #         NAME:  check_cmdline_param
371 #   PARAMETERS:  nothing
372 #      RETURNS:  nothing
373 #  DESCRIPTION:  validates commandline parameter
374 #===============================================================================
375 sub check_cmdline_param () {
376     my $err_config;
377     my $err_counter = 0;
378         if(not defined($cfg_file)) {
379                 $cfg_file = "/etc/gosa-si/server.conf";
380                 if(! -r $cfg_file) {
381                         $err_config = "please specify a config file";
382                         $err_counter += 1;
383                 }
384     }
385     if( $err_counter > 0 ) {
386         &usage( "", 1 );
387         if( defined( $err_config)) { print STDERR "$err_config\n"}
388         print STDERR "\n";
389         exit( -1 );
390     }
394 #===  FUNCTION  ================================================================
395 #         NAME:  check_pid
396 #   PARAMETERS:  nothing
397 #      RETURNS:  nothing
398 #  DESCRIPTION:  handels pid processing
399 #===============================================================================
400 sub check_pid {
401     $pid = -1;
402     # Check, if we are already running
403     if( open(LOCK_FILE, "<$pid_file") ) {
404         $pid = <LOCK_FILE>;
405         if( defined $pid ) {
406             chomp( $pid );
407             if( -f "/proc/$pid/stat" ) {
408                 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
409                 if( $stat ) {
410                                         print STDERR "\nERROR: Already running!\n";
411                     close( LOCK_FILE );
412                     exit -1;
413                 }
414             }
415         }
416         close( LOCK_FILE );
417         unlink( $pid_file );
418     }
420     # create a syslog msg if it is not to possible to open PID file
421     if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
422         my($msg) = "Couldn't obtain lockfile '$pid_file' ";
423         if (open(LOCK_FILE, '<', $pid_file)
424                 && ($pid = <LOCK_FILE>))
425         {
426             chomp($pid);
427             $msg .= "(PID $pid)\n";
428         } else {
429             $msg .= "(unable to read PID)\n";
430         }
431         if( ! ($foreground) ) {
432             openlog( $0, "cons,pid", "daemon" );
433             syslog( "warning", $msg );
434             closelog();
435         }
436         else {
437             print( STDERR " $msg " );
438         }
439         exit( -1 );
440     }
443 #===  FUNCTION  ================================================================
444 #         NAME:  import_modules
445 #   PARAMETERS:  module_path - string - abs. path to the directory the modules 
446 #                are stored
447 #      RETURNS:  nothing
448 #  DESCRIPTION:  each file in module_path which ends with '.pm' and activation 
449 #                state is on is imported by "require 'file';"
450 #===============================================================================
451 sub import_modules {
452     daemon_log(" ", 1);
454     if (not -e $modules_path) {
455         daemon_log("0 ERROR: cannot find directory or directory is not readable: $modules_path", 1);   
456     }
458     opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
460         my $ldap_handle = &get_ldap_handle;
461     while (defined (my $file = readdir (DIR))) {
462         if (not $file =~ /(\S*?).pm$/) {
463             next;
464         }
465                 my $mod_name = $1;
467         # ArpHandler switch
468         if( $file =~ /ArpHandler.pm/ ) {
469             if( $arp_enabled eq "false" ) { next; }
470         }
471         
472         eval { require $file; };
473         if ($@) {
474             daemon_log("0 ERROR: gosa-si-server could not load module $file", 1);
475             daemon_log("$@", 1);
476             exit;
477                 } else {
478                         my $info = eval($mod_name.'::get_module_info($ldap_handle)');
479                         # Only load module if get_module_info() returns a non-null object
480                         if( $info ) {
481                                 my ($input_address, $input_key, $event_hash) = @{$info};
482                                 $known_modules->{$mod_name} = $info;
483                                 daemon_log("0 INFO: module $mod_name loaded", 5);
484                         }
485                 }
486     }   
487         &release_ldap_handle($ldap_handle); 
488     close (DIR);
491 #===  FUNCTION  ================================================================
492 #         NAME:  password_check
493 #   PARAMETERS:  nothing
494 #      RETURNS:  nothing
495 #  DESCRIPTION:  escalates an critical error if two modules exist which are avaialable by 
496 #                the same password
497 #===============================================================================
498 sub password_check {
499     my $passwd_hash = {};
500     while (my ($mod_name, $mod_info) = each %$known_modules) {
501         my $mod_passwd = @$mod_info[1];
502         if (not defined $mod_passwd) { next; }
503         if (not exists $passwd_hash->{$mod_passwd}) {
504             $passwd_hash->{$mod_passwd} = $mod_name;
506         # escalates critical error
507         } else {
508             &daemon_log("0 ERROR: two loaded modules do have the same password. Please modify the 'key'-parameter in config file");
509             &daemon_log("0 ERROR: module='$mod_name' and module='".$passwd_hash->{$mod_passwd}."'");
510             exit( -1 );
511         }
512     }
517 #===  FUNCTION  ================================================================
518 #         NAME:  sig_int_handler
519 #   PARAMETERS:  signal - string - signal arose from system
520 #      RETURNS:  nothing
521 #  DESCRIPTION:  handels tasks to be done befor signal becomes active
522 #===============================================================================
523 sub sig_int_handler {
524     my ($signal) = @_;
526 #       if (defined($ldap_handle)) {
527 #               $ldap_handle->disconnect;
528 #       }
529     # TODO alle verbliebenden ldap verbindungen aus allen heaps beenden
530     
532     daemon_log("shutting down gosa-si-server", 1);
533     system("kill `ps -C gosa-si-server -o pid=`");
535 $SIG{INT} = \&sig_int_handler;
538 sub check_key_and_xml_validity {
539     my ($crypted_msg, $module_key, $session_id) = @_;
540     my $msg;
541     my $msg_hash;
542     my $error_string;
543     eval{
544         $msg = &decrypt_msg($crypted_msg, $module_key);
546         if ($msg =~ /<xml>/i){
547             $msg =~ s/\s+/ /g;  # just for better daemon_log
548             daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 9);
549             $msg_hash = $xml->XMLin($msg, ForceArray=>1);
551             ##############
552             # check header
553             if( not exists $msg_hash->{'header'} ) { die "no header specified"; }
554             my $header_l = $msg_hash->{'header'};
555             if( (1 > @{$header_l}) || ( ( 'HASH' eq ref @{$header_l}[0]) && (1 > keys %{@{$header_l}[0]}) ) ) { die 'empty header tag'; }
556             if( 1 < @{$header_l} ) { die 'more than one header specified'; }
557             my $header = @{$header_l}[0];
558             if( 0 == length $header) { die 'empty string in header tag'; }
560             ##############
561             # check source
562             if( not exists $msg_hash->{'source'} ) { die "no source specified"; }
563             my $source_l = $msg_hash->{'source'};
564             if( (1 > @{$source_l}) || ( ( 'HASH' eq ref @{$source_l}[0]) && (1 > keys %{@{$source_l}[0]}) ) ) { die 'empty source tag'; }
565             if( 1 < @{$source_l} ) { die 'more than one source specified'; }
566             my $source = @{$source_l}[0];
567             if( 0 == length $source) { die 'source error'; }
569             ##############
570             # check target
571             if( not exists $msg_hash->{'target'} ) { die "no target specified"; }
572             my $target_l = $msg_hash->{'target'};
573             if( (1 > @{$target_l}) || ( ('HASH' eq ref @{$target_l}[0]) && (1 > keys %{@{$target_l}[0]}) ) ) { die 'empty target tag'; }
574         }
575     };
576     if($@) {
577         daemon_log("$session_id ERROR: do not understand the message: $@", 1);
578         $msg = undef;
579         $msg_hash = undef;
580     }
582     return ($msg, $msg_hash);
586 sub check_outgoing_xml_validity {
587     my ($msg, $session_id) = @_;
589     my $msg_hash;
590     eval{
591         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
593         ##############
594         # check header
595         my $header_l = $msg_hash->{'header'};
596         if( 1 != @{$header_l} ) {
597             die 'no or more than one headers specified';
598         }
599         my $header = @{$header_l}[0];
600         if( 0 == length $header) {
601             die 'header has length 0';
602         }
604         ##############
605         # check source
606         my $source_l = $msg_hash->{'source'};
607         if( 1 != @{$source_l} ) {
608             die 'no or more than 1 sources specified';
609         }
610         my $source = @{$source_l}[0];
611         if( 0 == length $source) {
612             die 'source has length 0';
613         }
615                                 # Check if source contains hostname instead of ip address
616                                 if(not $source =~ /^[a-z0-9\.]+:\d+$/i) {
617                                                 my ($hostname,$port) = split(/:/, $source);
618                                                 my $ip_address = inet_ntoa(scalar gethostbyname($hostname));
619                                                 if(defined($ip_address) && $ip_address =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/ && $port =~ /^\d+$/) {
620                                                         # Write ip address to $source variable
621                                                         $source = "$ip_address:$port";
622                                                 }
623                                 }
624         unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
625                 $source =~ /^GOSA$/i) {
626             die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
627         }
628         
629         ##############
630         # check target  
631         my $target_l = $msg_hash->{'target'};
632         if( 0 == @{$target_l} ) {
633             die "no targets specified";
634         }
635         foreach my $target (@$target_l) {
636             if( 0 == length $target) {
637                 die "target has length 0";
638             }
639             unless( $target =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
640                     $target =~ /^GOSA$/i ||
641                     $target =~ /^\*$/ ||
642                     $target =~ /KNOWN_SERVER/i ||
643                     $target =~ /JOBDB/i ||
644                     $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 ){
645                 die "target '$target' is not a complete ip-address with port or a valid target name or a mac-address";
646             }
647         }
648     };
649     if($@) {
650         daemon_log("$session_id ERROR: outgoing msg is not gosa-si envelope conform: $@", 1);
651         daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 1);
652         $msg_hash = undef;
653     }
655     return ($msg_hash);
659 sub input_from_known_server {
660     my ($input, $remote_ip, $session_id) = @_ ;  
661     my ($msg, $msg_hash, $module);
663     my $sql_statement= "SELECT * FROM known_server";
664     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
666     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
667         my $host_name = $hit->{hostname};
668         if( not $host_name =~ "^$remote_ip") {
669             next;
670         }
671         my $host_key = $hit->{hostkey};
672         daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 7);
673         daemon_log("$session_id DEBUG: input_from_known_server: host_key: $host_key", 7);
675         # check if module can open msg envelope with module key
676         my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
677         if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
678             daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 7);
679             daemon_log("$@", 8);
680             next;
681         }
682         else {
683             $msg = $tmp_msg;
684             $msg_hash = $tmp_msg_hash;
685             $module = "ServerPackages";
686             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 7);
687             last;
688         }
689     }
691     if( (!$msg) || (!$msg_hash) || (!$module) ) {
692         daemon_log("$session_id DEBUG: Incoming message is not from a known server", 7);
693     }
694   
695     return ($msg, $msg_hash, $module);
699 sub input_from_known_client {
700     my ($input, $remote_ip, $session_id) = @_ ;  
701     my ($msg, $msg_hash, $module);
703     my $sql_statement= "SELECT * FROM known_clients";
704     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
705     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
706         my $host_name = $hit->{hostname};
707         if( not $host_name =~ /^$remote_ip:\d*$/) {
708                 next;
709                 }
710         my $host_key = $hit->{hostkey};
711         &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 7);
712         &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 7);
714         # check if module can open msg envelope with module key
715         ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
717         if( (!$msg) || (!$msg_hash) ) {
718             &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 7);
719             &daemon_log("$@", 8);
720             next;
721         }
722         else {
723             $module = "ClientPackages";
724             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 7);
725             last;
726         }
727     }
729     if( (!$msg) || (!$msg_hash) || (!$module) ) {
730         &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 7);
731     }
733     return ($msg, $msg_hash, $module);
737 sub input_from_unknown_host {
738         no strict "refs";
739         my ($input, $session_id) = @_ ;
740         my ($msg, $msg_hash, $module);
741         my $error_string;
743         my %act_modules = %$known_modules;
745         while( my ($mod, $info) = each(%act_modules)) {
747                 # check a key exists for this module
748                 my $module_key = ${$mod."_key"};
749                 if( not defined $module_key ) {
750                         if( $mod eq 'ArpHandler' ) {
751                                 next;
752                         }
753                         daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
754                         next;
755                 }
756                 daemon_log("$session_id DEBUG: $mod: $module_key", 7);
758                 # check if module can open msg envelope with module key
759                 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
760                 if( (not defined $msg) || (not defined $msg_hash) ) {
761                         next;
762                 } else {
763                         $module = $mod;
764             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 7);
765                         last;
766                 }
767         }
769         if( (!$msg) || (!$msg_hash) || (!$module)) {
770                 daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 7);
771         }
773         return ($msg, $msg_hash, $module);
777 sub create_ciphering {
778     my ($passwd) = @_;
779         if((!defined($passwd)) || length($passwd)==0) {
780                 $passwd = "";
781         }
782     $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
783     my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
784     my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
785     $my_cipher->set_iv($iv);
786     return $my_cipher;
790 sub encrypt_msg {
791     my ($msg, $key) = @_;
792     my $my_cipher = &create_ciphering($key);
793     my $len;
794     {
795             use bytes;
796             $len= 16-length($msg)%16;
797     }
798     $msg = "\0"x($len).$msg;
799     $msg = $my_cipher->encrypt($msg);
800     chomp($msg = &encode_base64($msg));
801     # there are no newlines allowed inside msg
802     $msg=~ s/\n//g;
803     return $msg;
807 sub decrypt_msg {
809     my ($msg, $key) = @_ ;
810     $msg = &decode_base64($msg);
811     my $my_cipher = &create_ciphering($key);
812     $msg = $my_cipher->decrypt($msg); 
813     $msg =~ s/\0*//g;
814     return $msg;
818 sub get_encrypt_key {
819     my ($target) = @_ ;
820     my $encrypt_key;
821     my $error = 0;
823     # target can be in known_server
824     if( not defined $encrypt_key ) {
825         my $sql_statement= "SELECT * FROM known_server WHERE hostname='$target'";
826         my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
827         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
828             my $host_name = $hit->{hostname};
829             if( $host_name ne $target ) {
830                 next;
831             }
832             $encrypt_key = $hit->{hostkey};
833             last;
834         }
835     }
837     # target can be in known_client
838     if( not defined $encrypt_key ) {
839         my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$target'";
840         my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
841         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
842             my $host_name = $hit->{hostname};
843             if( $host_name ne $target ) {
844                 next;
845             }
846             $encrypt_key = $hit->{hostkey};
847             last;
848         }
849     }
851     return $encrypt_key;
855 #===  FUNCTION  ================================================================
856 #         NAME:  open_socket
857 #   PARAMETERS:  PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
858 #                [PeerPort] string necessary if port not appended by PeerAddr
859 #      RETURNS:  socket IO::Socket::INET
860 #  DESCRIPTION:  open a socket to PeerAddr
861 #===============================================================================
862 sub open_socket {
863     my ($PeerAddr, $PeerPort) = @_ ;
864     if(defined($PeerPort)){
865         $PeerAddr = $PeerAddr.":".$PeerPort;
866     }
867     my $socket;
868     $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
869             Porto => "tcp",
870             Type => SOCK_STREAM,
871             Timeout => 5,
872             );
873     if(not defined $socket) {
874         return;
875     }
876 #    &daemon_log("DEBUG: open_socket: $PeerAddr", 7);
877     return $socket;
881 sub send_msg_to_target {
882     my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
883     my $error = 0;
884     my $header;
885     my $timestamp = &get_time();
886     my $new_status;
887     my $act_status;
888     my ($sql_statement, $res);
889   
890     if( $msg_header ) {
891         $header = "'$msg_header'-";
892     } else {
893         $header = "";
894     }
896         # Patch the source ip
897         if($msg =~ /<source>0\.0\.0\.0:\d*?<\/source>/) {
898                 my $remote_ip = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
899                 $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$remote_ip:$2<\/source>/s;
900         }
902     # encrypt xml msg
903     my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
905     # opensocket
906     my $socket = &open_socket($address);
907     if( !$socket ) {
908         daemon_log("$session_id WARNING: cannot send ".$header."msg to $address , host not reachable", 3);
909         $error++;
910     }
911     
912     if( $error == 0 ) {
913         # send xml msg
914         print $socket $crypted_msg."\n";
916         daemon_log("$session_id INFO: send ".$header."msg to $address", 5);
917         daemon_log("$session_id DEBUG: message:\n$msg", 9);
918         
919     }
921     # close socket in any case
922     if( $socket ) {
923         close $socket;
924     }
926     if( $error > 0 ) { $new_status = "down"; }
927     else { $new_status = $msg_header; }
930     # known_clients
931     $sql_statement = "SELECT * FROM $known_clients_tn WHERE hostname='$address'";
932     $res = $known_clients_db->select_dbentry($sql_statement);
933     if( keys(%$res) == 1) {
934         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
935         if ($act_status eq "down" && $new_status eq "down") {
936             $sql_statement = "DELETE FROM known_clients WHERE hostname='$address'";
937             $res = $known_clients_db->del_dbentry($sql_statement);
938             daemon_log("$session_id WARNING: failed 2x to send msg to host '$address', delete host from known_clients", 3);
939         } else { 
940             $sql_statement = "UPDATE known_clients SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
941             $res = $known_clients_db->update_dbentry($sql_statement);
942             if($new_status eq "down"){
943                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
944             } else {
945                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
946             }
947         }
948     }
950     # known_server
951     $sql_statement = "SELECT * FROM $known_server_tn WHERE hostname='$address'";
952     $res = $known_server_db->select_dbentry($sql_statement);
953     if( keys(%$res) == 1) {
954         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
955         if ($act_status eq "down" && $new_status eq "down") {
956             $sql_statement = "DELETE FROM known_server WHERE hostname='$address'";
957             $res = $known_server_db->del_dbentry($sql_statement);
958             daemon_log("$session_id WARNING: failed 2x to send a message to host '$address', delete host from known_server", 3);
959         } 
960         else { 
961             $sql_statement = "UPDATE known_server SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
962             $res = $known_server_db->update_dbentry($sql_statement);
963             if($new_status eq "down"){
964                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
965             } else {
966                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
967             }
968         }
969     }
970     return $error; 
974 sub update_jobdb_status_for_send_msgs {
975     my ($session_id, $answer, $error) = @_;
976     &daemon_log("$session_id DEBUG: try to update job status", 7); 
977     if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
978         my $jobdb_id = $1;
979     
980         $answer =~ /<header>(.*)<\/header>/;
981         my $job_header = $1;
983         $answer =~ /<target>(.*)<\/target>/;
984         my $job_target = $1;
985             
986         # Sending msg failed
987         if( $error ) {
989             # Set jobs to done, jobs do not need to deliver their message in any case
990             if (($job_header eq "trigger_action_localboot")
991                     ||($job_header eq "trigger_action_lock")
992                     ||($job_header eq "trigger_action_halt") 
993                     ) {
994                 my $sql_statement = "UPDATE $job_queue_tn SET status='done' WHERE id=$jobdb_id";
995                 &daemon_log("$session_id DEBUG: $sql_statement", 7); 
996                 my $res = $job_db->update_dbentry($sql_statement);
997                 
998             # Reactivate jobs, jobs need to deliver their message
999             } elsif (($job_header eq "trigger_action_activate")
1000                     ||($job_header eq "trigger_action_update")
1001                     ||($job_header eq "trigger_action_reinstall") 
1002                     ||($job_header eq "trigger_activate_new")
1003                     ) {
1004                 &reactivate_job_with_delay($session_id, $job_target, $job_header, 30 );
1006             # For all other messages
1007             } else {
1008                 my $sql_statement = "UPDATE $job_queue_tn ".
1009                     "SET status='error', result='can not deliver msg, please consult log file' ".
1010                     "WHERE id=$jobdb_id";
1011                 &daemon_log("$session_id DEBUG: $sql_statement", 7); 
1012                 my $res = $job_db->update_dbentry($sql_statement);
1013             }
1015         # Sending msg was successful
1016         } else {
1017             # Set jobs localboot, lock, activate, halt, reboot and wake to done
1018             # jobs reinstall, update, inst_update do themself setting to done
1019             if (($job_header eq "trigger_action_localboot")
1020                     ||($job_header eq "trigger_action_lock")
1021                     ||($job_header eq "trigger_action_activate")
1022                     ||($job_header eq "trigger_action_halt") 
1023                     ||($job_header eq "trigger_action_reboot")
1024                     ||($job_header eq "trigger_action_wake")
1025                     ||($job_header eq "trigger_wake")
1026                     ) {
1028                 my $sql_statement = "UPDATE $job_queue_tn ".
1029                     "SET status='done' ".
1030                     "WHERE id=$jobdb_id AND status='processed'";
1031                 &daemon_log("$session_id DEBUG: $sql_statement", 7); 
1032                 my $res = $job_db->update_dbentry($sql_statement);
1033             } else { 
1034                 &daemon_log("$session_id DEBUG: sending message succeed but cannot update job status.", 7); 
1035             } 
1036         } 
1037     } else { 
1038         &daemon_log("$session_id DEBUG: cannot update job status, msg has no jobdb_id-tag: $answer", 7); 
1039     }
1042 sub reactivate_job_with_delay {
1043     my ($session_id, $target, $header, $delay) = @_ ;
1044     # 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
1045     
1046     if (not defined $delay) { $delay = 30 } ;
1047     my $delay_timestamp = &calc_timestamp(&get_time(), "plus", $delay);
1049     my $sql = "UPDATE $job_queue_tn Set timestamp='$delay_timestamp', status='waiting' WHERE (macaddress='$target' AND headertag='$header')"; 
1050     my $res = $job_db->update_dbentry($sql);
1051     daemon_log("$session_id INFO: '$header'-job will be reactivated at '$delay_timestamp' ".
1052             "cause client '$target' is currently not available", 5);
1053     daemon_log("$session_id $sql", 7);                             
1054     return;
1058 sub sig_handler {
1059         my ($kernel, $signal) = @_[KERNEL, ARG0] ;
1060         daemon_log("0 INFO got signal '$signal'", 1); 
1061         $kernel->sig_handled();
1062         return;
1066 sub msg_to_decrypt {
1067         my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
1068         my $session_id = $session->ID;
1069         my ($msg, $msg_hash, $module);
1070         my $error = 0;
1072         # fetch new msg out of @msgs_to_decrypt
1073         my $tmp_next_msg = shift @msgs_to_decrypt;
1074     my ($next_msg, $msg_source) = split(/;/, $tmp_next_msg);
1076         # msg is from a new client or gosa
1077         ($msg, $msg_hash, $module) = &input_from_unknown_host($next_msg, $session_id);
1079         # msg is from a gosa-si-server
1080         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1081                 ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $heap->{'remote_ip'}, $session_id);
1082         }
1083         # msg is from a gosa-si-client
1084         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1085                 ($msg, $msg_hash, $module) = &input_from_known_client($next_msg, $heap->{'remote_ip'}, $session_id);
1086         }
1087         # an error occurred
1088         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1089                 # if an incoming msg could not be decrypted (maybe a wrong key), send client a ping. If the client
1090                 # could not understand a msg from its server the client cause a re-registering process
1091         my $remote_ip = $heap->{'remote_ip'};
1092         my $remote_port = $heap->{'remote_port'};
1093         my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source><target>$msg_source</target></xml>";
1094         my ($test_error, $test_error_string) = &send_msg_to_target($ping_msg, "$msg_source", "dummy-key", "gosa_ping", $session_id);
1096                 daemon_log("$session_id WARNING cannot understand incoming msg, send 'ping'-msg to all host with ip '".$heap->{remote_ip}.
1097                         "' to cause a re-registering of the client if necessary", 3);
1098                 $error++;
1099         }
1102         my $header;
1103         my $target;
1104         my $source;
1105         my $done = 0;
1106         my $sql;
1107         my $res;
1109         # check whether this message should be processed here
1110         if ($error == 0) {
1111                 $header = @{$msg_hash->{'header'}}[0];
1112                 $target = @{$msg_hash->{'target'}}[0];
1113                 $source = @{$msg_hash->{'source'}}[0];
1114                 my $not_found_in_known_clients_db = 0;
1115                 my $not_found_in_known_server_db = 0;
1116                 my $not_found_in_foreign_clients_db = 0;
1117                 my $local_address;
1118                 my $local_mac;
1119                 my ($target_ip, $target_port) = split(':', $target);
1121                 # Determine the local ip address if target is an ip address
1122                 if ($target =~ /^\d+\.\d+\.\d+\.\d+:\d+$/) {
1123                         $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1124                 } else {
1125                         $local_address = $server_address;
1126                 }
1128                 # Determine the local mac address if target is a mac address
1129                 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) {
1130                         my $loc_ip = &get_local_ip_for_remote_ip($heap->{'remote_ip'});
1131                         my $network_interface= &get_interface_for_ip($loc_ip);
1132                         $local_mac = &get_mac_for_interface($network_interface);
1133                 } else {
1134                         $local_mac = $server_mac_address;
1135                 }
1137                 # target and source is equal to GOSA -> process here
1138                 if (not $done) {
1139                         if ($target eq "GOSA" && $source eq "GOSA") {
1140                                 $done = 1;                    
1141                                 &daemon_log("$session_id DEBUG: target and source is 'GOSA' -> process here", 7);
1142                         }
1143                 }
1145                 # target is own address without forward_to_gosa-tag -> process here
1146                 if (not $done) {
1147                         #if ((($target eq $local_address) || ($target eq $local_mac) ) && (not exists $msg_hash->{'forward_to_gosa'})) {
1148                         if (($target eq $local_address) && (not exists $msg_hash->{'forward_to_gosa'})) {
1149                                 $done = 1;
1150                                 if ($source eq "GOSA") {
1151                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1152                                 }
1153                                 &daemon_log("$session_id DEBUG: target is own address without forward_to_gosa-tag -> process here", 7);
1154                         }
1155                 }
1157                 # target is a client address in known_clients -> process here
1158                 if (not $done) {
1159                         $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')"; 
1160                         $res = $known_clients_db->select_dbentry($sql);
1161                         if (keys(%$res) > 0) {
1162                                 $done = 1; 
1163                                 my $hostname = $res->{1}->{'hostname'};
1164                                 $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1165                                 my $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1166                                 if ($source eq "GOSA") {
1167                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1168                                 }
1169                                 &daemon_log("$session_id DEBUG: target is a client address in known_clients -> process here", 7);
1171                         } else {
1172                                 $not_found_in_known_clients_db = 1;
1173                         }
1174                 }
1176                 # target ist own address with forward_to_gosa-tag not pointing to myself -> process here
1177                 if (not $done) {
1178                         my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1179                         my $gosa_at;
1180                         my $gosa_session_id;
1181                         if (($target eq $local_address) && (defined $forward_to_gosa)){
1182                                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1183                                 if ($gosa_at ne $local_address) {
1184                                         $done = 1;
1185                                         &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag not pointing to myself -> process here", 7); 
1186                                 }
1187                         }
1188                 }
1190                 # if message should be processed here -> add message to incoming_db
1191                 if ($done) {
1192                         # if a job or a gosa message comes from a foreign server, fake module to GosaPackages
1193                         # so gosa-si-server knows how to process this kind of messages
1194                         if ($header =~ /^gosa_/ || $header =~ /^job_/) {
1195                                 $module = "GosaPackages";
1196                         }
1198                         my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1199                                         primkey=>[],
1200                                         headertag=>$header,
1201                                         targettag=>$target,
1202                                         xmlmessage=>&encode_base64($msg),
1203                                         timestamp=>&get_time,
1204                                         module=>$module,
1205                                         sessionid=>$session_id,
1206                                 } );
1208                 }
1210                 # target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa
1211                 if (not $done) {
1212                         my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1213                         my $gosa_at;
1214                         my $gosa_session_id;
1215                         if (($target eq $local_address) && (defined $forward_to_gosa)){
1216                                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1217                                 if ($gosa_at eq $local_address) {
1218                                         my $session_reference = $kernel->ID_id_to_session($gosa_session_id);
1219                                         if( defined $session_reference ) {
1220                                                 $heap = $session_reference->get_heap();
1221                                         }
1222                                         if(exists $heap->{'client'}) {
1223                                                 $msg = &encrypt_msg($msg, $GosaPackages_key);
1224                                                 $heap->{'client'}->put($msg);
1225                                                 &daemon_log("$session_id INFO: incoming '$header' message forwarded to GOsa", 5); 
1226                                         }
1227                                         $done = 1;
1228                                         &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa", 7);
1229                                 }
1230                         }
1232                 }
1234                 # target is a client address in foreign_clients -> forward to registration server
1235                 if (not $done) {
1236                         $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1237                         $res = $foreign_clients_db->select_dbentry($sql);
1238                         if (keys(%$res) > 0) {
1239                                 my $hostname = $res->{1}->{'hostname'};
1240                                 my ($host_ip, $host_port) = split(/:/, $hostname);
1241                                 my $local_address =  &get_local_ip_for_remote_ip($host_ip).":$server_port";
1242                                 my $regserver = $res->{1}->{'regserver'};
1243                                 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$regserver'"; 
1244                                 my $res = $known_server_db->select_dbentry($sql);
1245                                 if (keys(%$res) > 0) {
1246                                         my $regserver_key = $res->{1}->{'hostkey'};
1247                                         $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1248                                         $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1249                                         if ($source eq "GOSA") {
1250                                                 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1251                                         }
1252                                         my $error= &send_msg_to_target($msg, $regserver, $regserver_key, $header, $session_id);
1253                                         if ($error) {
1254                                                 &daemon_log("$session_id ERROR: some problems (error=$error) occurred while trying to send msg to registration server: $msg", 1); 
1255                                         }
1256                                 }
1257                                 $done = 1;
1258                                 &daemon_log("$session_id DEBUG: target is a client address in foreign_clients -> forward to registration server", 7);
1259                         } else {
1260                                 $not_found_in_foreign_clients_db = 1;
1261                         }
1262                 }
1264                 # target is a server address -> forward to server
1265                 if (not $done) {
1266                         $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1267                         $res = $known_server_db->select_dbentry($sql);
1268                         if (keys(%$res) > 0) {
1269                                 my $hostkey = $res->{1}->{'hostkey'};
1271                                 if ($source eq "GOSA") {
1272                                         $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1273                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1275                                 }
1277                                 &send_msg_to_target($msg, $target, $hostkey, $header, $session_id);
1278                                 $done = 1;
1279                                 &daemon_log("$session_id DEBUG: target is a server address -> forward to server", 7);
1280                         } else {
1281                                 $not_found_in_known_server_db = 1;
1282                         }
1283                 }
1286                 # target is not in foreign_clients_db, known_server_db or known_clients_db, maybe it is a complete new one -> process here
1287                 if ( $not_found_in_foreign_clients_db 
1288                         && $not_found_in_known_server_db
1289                         && $not_found_in_known_clients_db) {
1290                         &daemon_log("$session_id DEBUG: target is not in foreign_clients_db, known_server_db or known_clients_db, maybe it is a complete new one -> process here", 7);
1291             if ($header =~ /^gosa_/ || $header =~ /^job_/) { 
1292                 $module = "GosaPackages"; 
1293             }
1294                         my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1295                                         primkey=>[],
1296                                         headertag=>$header,
1297                                         targettag=>$target,
1298                                         xmlmessage=>&encode_base64($msg),
1299                                         timestamp=>&get_time,
1300                                         module=>$module,
1301                                         sessionid=>$session_id,
1302                                 } );
1303                         $done = 1;
1304                 }
1307                 if (not $done) {
1308                         daemon_log("$session_id ERROR: do not know what to do with this message: $msg", 1);
1309                         if ($source eq "GOSA") {
1310                                 my %data = ('error_msg' => &encode_base64($msg), 'error_string' => "Do not know what to do with this message!");
1311                                 my $error_msg = &build_msg("error", $local_address, "GOSA", \%data ); 
1313                                 my $session_reference = $kernel->ID_id_to_session($session_id);
1314                                 if( defined $session_reference ) {
1315                                         $heap = $session_reference->get_heap();
1316                                 }
1317                                 if(exists $heap->{'client'}) {
1318                                         $error_msg = &encrypt_msg($error_msg, $GosaPackages_key);
1319                                         $heap->{'client'}->put($error_msg);
1320                                 }
1321                         }
1322                 }
1324         }
1326         return;
1330 sub next_task {
1331     my ($session, $heap, $task, $ldap_handle) = @_[SESSION, HEAP, ARG0, ARG1];
1332     my $running_task = POE::Wheel::Run->new(
1333             Program => sub { process_task($session, $heap, $task, $ldap_handle) },
1334             StdioFilter => POE::Filter::Reference->new(),
1335             StdoutEvent  => "task_result",
1336             StderrEvent  => "task_debug",
1337             CloseEvent   => "task_done",
1338             );
1339     $heap->{task}->{ $running_task->ID } = $running_task;
1340         $heap->{ldap_handle}->{$running_task->ID} = $ldap_handle;
1343 sub handle_task_result {
1344     my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
1345     my $client_answer = $result->{'answer'};
1346     if( $client_answer =~ s/session_id=(\d+)$// ) {
1347         my $session_id = $1;
1348         if( defined $session_id ) {
1349             my $session_reference = $kernel->ID_id_to_session($session_id);
1350             if( defined $session_reference ) {
1351                 $heap = $session_reference->get_heap();
1352             }
1353         }
1355         if(exists $heap->{'client'}) {
1356             $heap->{'client'}->put($client_answer);
1357         }
1358     }
1359     $kernel->sig(CHLD => "child_reap");
1362 sub handle_task_debug {
1363     my $result = $_[ARG0];
1364     print STDERR "$result\n";
1367 sub handle_task_done {
1368     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1369     delete $heap->{task}->{$task_id};
1370         if (exists $heap->{ldap_handle}->{$task_id}) {
1371                 &release_ldap_handle($heap->{ldap_handle}->{$task_id});
1372         }
1375 sub process_task {
1376     no strict "refs";
1377     #CHECK: Not @_[...]?
1378     my ($session, $heap, $task, $ldap_handle) = @_;
1379     my $error = 0;
1380     my $answer_l;
1381     my ($answer_header, @answer_target_l, $answer_source);
1382     my $client_answer = "";
1384     # prepare all variables needed to process message
1385     #my $msg = $task->{'xmlmessage'};
1386     my $msg = &decode_base64($task->{'xmlmessage'});
1387     my $incoming_id = $task->{'id'};
1388     my $module = $task->{'module'};
1389     my $header =  $task->{'headertag'};
1390     my $session_id = $task->{'sessionid'};
1391                 my $msg_hash;
1392                 eval {
1393         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
1394                 }; 
1395                 daemon_log("ERROR: XML failure '$@'") if ($@);
1396     my $source = @{$msg_hash->{'source'}}[0];
1397     
1398     # set timestamp of incoming client uptodate, so client will not 
1399     # be deleted from known_clients because of expiration
1400     my $cur_time = &get_time();
1401     my $sql = "UPDATE $known_clients_tn SET timestamp='$cur_time' WHERE hostname='$source'"; 
1402     my $res = $known_clients_db->exec_statement($sql);
1404     ######################
1405     # process incoming msg
1406     if( $error == 0) {
1407         daemon_log("$session_id INFO: Incoming msg (session_id=$session_id) with header '".@{$msg_hash->{'header'}}[0]."'", 5); 
1408         daemon_log("$session_id DEBUG: Processing module ".$module, 7);
1409         $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id, $ldap_handle);
1411         if ( 0 < @{$answer_l} ) {
1412             my $answer_str = join("\n", @{$answer_l});
1413             while ($answer_str =~ /<header>(\w+)<\/header>/g) {
1414                 daemon_log("$session_id INFO: got answer message with header '$1'", 5);
1415             }
1416             daemon_log("$session_id DEBUG: $module: got answer from module: \n".$answer_str,9);
1417         } else {
1418             daemon_log("$session_id DEBUG: $module: got no answer from module!" ,7);
1419         }
1421     }
1422     if( !$answer_l ) { $error++ };
1424     ########
1425     # answer
1426     if( $error == 0 ) {
1428         foreach my $answer ( @{$answer_l} ) {
1429             # check outgoing msg to xml validity
1430             my $answer_hash = &check_outgoing_xml_validity($answer, $session_id);
1431             if( not defined $answer_hash ) { next; }
1432             
1433             $answer_header = @{$answer_hash->{'header'}}[0];
1434             @answer_target_l = @{$answer_hash->{'target'}};
1435             $answer_source = @{$answer_hash->{'source'}}[0];
1437             # deliver msg to all targets 
1438             foreach my $answer_target ( @answer_target_l ) {
1440                 # targets of msg are all gosa-si-clients in known_clients_db
1441                 if( $answer_target eq "*" ) {
1442                     # answer is for all clients
1443                     my $sql_statement= "SELECT * FROM known_clients";
1444                     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
1445                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1446                         my $host_name = $hit->{hostname};
1447                         my $host_key = $hit->{hostkey};
1448                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1449                         &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1450                     }
1451                 }
1453                 # targets of msg are all gosa-si-server in known_server_db
1454                 elsif( $answer_target eq "KNOWN_SERVER" ) {
1455                     # answer is for all server in known_server
1456                     my $sql_statement= "SELECT * FROM $known_server_tn";
1457                     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
1458                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1459                         my $host_name = $hit->{hostname};
1460                         my $host_key = $hit->{hostkey};
1461                         $answer =~ s/<target>\S+<\/target>/<target>$host_name<\/target>/g;
1462                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1463                         &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1464                     }
1465                 }
1467                 # target of msg is GOsa
1468                                 elsif( $answer_target eq "GOSA" ) {
1469                                         my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1470                                         my $add_on = "";
1471                     if( defined $session_id ) {
1472                         $add_on = ".session_id=$session_id";
1473                     }
1474                     # answer is for GOSA and has to returned to connected client
1475                     my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1476                     $client_answer = $gosa_answer.$add_on;
1477                 }
1479                 # target of msg is job queue at this host
1480                 elsif( $answer_target eq "JOBDB") {
1481                     $answer =~ /<header>(\S+)<\/header>/;   
1482                     my $header;
1483                     if( defined $1 ) { $header = $1; }
1484                     my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1485                     &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1486                 }
1488                 # Target of msg is a mac address
1489                 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 ) {
1490                     daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients and foreign_clients", 5);
1492                     # Looking for macaddress in known_clients
1493                     my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1494                     my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1495                     my $found_ip_flag = 0;
1496                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1497                         my $host_name = $hit->{hostname};
1498                         my $host_key = $hit->{hostkey};
1499                         $answer =~ s/$answer_target/$host_name/g;
1500                         daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1501                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1502                         &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1503                         $found_ip_flag++ ;
1504                     }   
1506                     # Looking for macaddress in foreign_clients
1507                     if ($found_ip_flag == 0) {
1508                         my $sql = "SELECT * FROM $foreign_clients_tn WHERE macaddress LIKE '$answer_target'";
1509                         my $res = $foreign_clients_db->select_dbentry($sql);
1510                         while( my ($hit_num, $hit) = each %{ $res } ) {
1511                             my $host_name = $hit->{hostname};
1512                             my $reg_server = $hit->{regserver};
1513                             daemon_log("$session_id INFO: found host '$host_name' with mac '$answer_target', registered at '$reg_server'", 5);
1514                             
1515                             # Fetch key for reg_server
1516                             my $reg_server_key;
1517                             my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$reg_server'";
1518                             my $res = $known_server_db->select_dbentry($sql);
1519                             if (exists $res->{1}) {
1520                                 $reg_server_key = $res->{1}->{'hostkey'}; 
1521                             } else {
1522                                 daemon_log("$session_id ERROR: cannot find hostkey for '$host_name' in '$known_server_tn'", 1); 
1523                                 daemon_log("$session_id ERROR: unable to forward answer to correct registration server, processing is aborted!", 1); 
1524                                 $reg_server_key = undef;
1525                             }
1527                             # Send answer to server where client is registered
1528                             if (defined $reg_server_key) {
1529                                 $answer =~ s/$answer_target/$host_name/g;
1530                                 my $error = &send_msg_to_target($answer, $reg_server, $reg_server_key, $answer_header, $session_id);
1531                                 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1532                                 $found_ip_flag++ ;
1533                             }
1534                         }
1535                     }
1537                     # No mac to ip matching found
1538                     if( $found_ip_flag == 0) {
1539                         daemon_log("$session_id WARNING: no host found in known_clients or foreign_clients with mac address '$answer_target'", 3);
1540                         &reactivate_job_with_delay($session_id, $answer_target, $answer_header, 30);
1541                     }
1543                 # Answer is for one specific host   
1544                 } else {
1545                     # get encrypt_key
1546                     my $encrypt_key = &get_encrypt_key($answer_target);
1547                     if( not defined $encrypt_key ) {
1548                         # unknown target
1549                         daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1550                         next;
1551                     }
1552                     my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1553                     &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1554                 }
1555             }
1556         }
1557     }
1559     my $filter = POE::Filter::Reference->new();
1560     my %result = ( 
1561             status => "seems ok to me",
1562             answer => $client_answer,
1563             );
1565     my $output = $filter->put( [ \%result ] );
1566     print @$output;
1571 sub session_start {
1572     my ($kernel) = $_[KERNEL];
1573     $global_kernel = $kernel;
1574     $kernel->yield('register_at_foreign_servers');
1575         $kernel->yield('create_fai_server_db', $fai_server_tn );
1576         $kernel->yield('create_fai_release_db', $fai_release_tn );
1577     $kernel->yield('watch_for_next_tasks');
1578         $kernel->sig(USR1 => "sig_handler");
1579         $kernel->sig(USR2 => "recreate_packages_db");
1580         $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1581         $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay); 
1582     $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay); 
1583         $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1584     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1585         $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1586     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1588     # Start opsi check
1589     if ($opsi_enabled eq "true") {
1590         $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay); 
1591     }
1596 sub watch_for_done_jobs {
1597         #CHECK: $heap for what?
1598         my ($kernel,$heap) = @_[KERNEL, HEAP];
1600         my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((status='done') AND (modified='0'))";
1601         my $res = $job_db->select_dbentry( $sql_statement );
1603         while( my ($id, $hit) = each %{$res} ) {
1604                 my $jobdb_id = $hit->{id};
1605                 my $sql_statement = "DELETE FROM $job_queue_tn WHERE id=$jobdb_id"; 
1606                 my $res = $job_db->del_dbentry($sql_statement); 
1607         }
1609         $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1613 sub watch_for_opsi_jobs {
1614     my ($kernel) = $_[KERNEL];
1616     # 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 
1617     # opsi install job is to parse the xml message. There is still the correct header.
1618     my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((xmlmessage LIKE '%opsi_install_client</header>%') AND (status='processing') AND (siserver='localhost'))";
1619         my $res = $job_db->select_dbentry( $sql_statement );
1621     # Ask OPSI for an update of the running jobs
1622     while (my ($id, $hit) = each %$res ) {
1623         # Determine current parameters of the job
1624         my $hostId = $hit->{'plainname'};
1625         my $macaddress = $hit->{'macaddress'};
1626         my $progress = $hit->{'progress'};
1628         my $result= {};
1629         
1630         # For hosts, only return the products that are or get installed
1631         my $callobj;
1632         $callobj = {
1633             method  => 'getProductStates_hash',
1634             params  => [ $hostId ],
1635             id  => 1,
1636         };
1637         
1638         my $hres = $opsi_client->call($opsi_url, $callobj);
1639         #my ($hres_err, $hres_err_string) = &check_opsi_res($hres);
1640         if (not &check_opsi_res($hres)) {
1641             my $htmp= $hres->result->{$hostId};
1642         
1643             # Check state != not_installed or action == setup -> load and add
1644             my $products= 0;
1645             my $installed= 0;
1646             my $installing = 0;
1647             my $error= 0;  
1648             my @installed_list;
1649             my @error_list;
1650             my $act_status = "none";
1651             foreach my $product (@{$htmp}){
1653                 if ($product->{'installationStatus'} ne "not_installed" or
1654                         $product->{'actionRequest'} eq "setup"){
1656                     # Increase number of products for this host
1657                     $products++;
1658         
1659                     if ($product->{'installationStatus'} eq "failed"){
1660                         $result->{$product->{'productId'}}= "error";
1661                         unshift(@error_list, $product->{'productId'});
1662                         $error++;
1663                     }
1664                     if ($product->{'installationStatus'} eq "installed" && $product->{'actionRequest'}  eq "none"){
1665                         $result->{$product->{'productId'}}= "installed";
1666                         unshift(@installed_list, $product->{'productId'});
1667                         $installed++;
1668                     }
1669                     if ($product->{'installationStatus'} eq "installing"){
1670                         $result->{$product->{'productId'}}= "installing";
1671                         $installing++;
1672                         $act_status = "installing - ".$product->{'productId'};
1673                     }
1674                 }
1675             }
1676         
1677             # Estimate "rough" progress, avoid division by zero
1678             if ($products == 0) {
1679                 $result->{'progress'}= 0;
1680             } else {
1681                 $result->{'progress'}= int($installed * 100 / $products);
1682             }
1684             # Set updates in job queue
1685             if ((not $error) && (not $installing) && ($installed)) {
1686                 $act_status = "installed - ".join(", ", @installed_list);
1687             }
1688             if ($error) {
1689                 $act_status = "error - ".join(", ", @error_list);
1690             }
1691             if ($progress ne $result->{'progress'} ) {
1692                 # Updating progress and result 
1693                 my $update_statement = "UPDATE $job_queue_tn SET modified='1', progress='".$result->{'progress'}."', result='$act_status' WHERE macaddress='$macaddress' AND siserver='localhost'";
1694                 my $update_res = $job_db->update_dbentry($update_statement);
1695             }
1696             if ($progress eq 100) { 
1697                 # Updateing status
1698                 my $done_statement = "UPDATE $job_queue_tn SET modified='1', ";
1699                 if ($error) {
1700                     $done_statement .= "status='error'";
1701                 } else {
1702                     $done_statement .= "status='done'";
1703                 }
1704                 $done_statement .= " WHERE macaddress='$macaddress' AND siserver='localhost'";
1705                 my $done_res = $job_db->update_dbentry($done_statement);
1706             }
1709         }
1710     }
1712     $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay);
1716 # If a job got an update or was modified anyway, send to all other si-server an update message of this jobs.
1717 sub watch_for_modified_jobs {
1718     my ($kernel,$heap) = @_[KERNEL, HEAP];
1720     my $sql_statement = "SELECT * FROM $job_queue_tn WHERE (modified='1')"; 
1721     my $res = $job_db->select_dbentry( $sql_statement );
1722     
1723     # if db contains no jobs which should be update, do nothing
1724     if (keys %$res != 0) {
1726         if ($job_synchronization  eq "true") {
1727             # make out of the db result a gosa-si message   
1728             my $update_msg = &db_res2si_msg ($res, "foreign_job_updates", "KNOWN_SERVER", "MY_LOCAL_ADDRESS");
1729  
1730             # update all other SI-server
1731             &inform_all_other_si_server($update_msg);
1732         }
1734         # set jobs all jobs to modified = 0, wait until the next modification for updates of other si-server
1735         $sql_statement = "UPDATE $job_queue_tn SET modified='0' ";
1736         $res = $job_db->update_dbentry($sql_statement);
1737     }
1739     $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay);
1743 sub watch_for_new_jobs {
1744         if($watch_for_new_jobs_in_progress == 0) {
1745                 $watch_for_new_jobs_in_progress = 1;
1746                 my ($kernel,$heap) = @_[KERNEL, HEAP];
1748                 # check gosa job queue for jobs with executable timestamp
1749                 my $timestamp = &get_time();
1750                 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE siserver='localhost' AND status='waiting' AND (CAST(timestamp AS UNSIGNED)) < $timestamp ORDER BY timestamp";
1751                 my $res = $job_db->exec_statement( $sql_statement );
1753                 # Merge all new jobs that would do the same actions
1754                 my @drops;
1755                 my $hits;
1756                 foreach my $hit (reverse @{$res} ) {
1757                         my $macaddress= lc @{$hit}[8];
1758                         my $headertag= @{$hit}[5];
1759                         if(
1760                                 defined($hits->{$macaddress}) &&
1761                                 defined($hits->{$macaddress}->{$headertag}) &&
1762                                 defined($hits->{$macaddress}->{$headertag}[0])
1763                         ) {
1764                                 push @drops, "DELETE FROM $job_queue_tn WHERE id = $hits->{$macaddress}->{$headertag}[0]";
1765                         }
1766                         $hits->{$macaddress}->{$headertag}= $hit;
1767                 }
1769                 # Delete new jobs with a matching job in state 'processing'
1770                 foreach my $macaddress (keys %{$hits}) {
1771                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1772                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1773                                 if(defined($jobdb_id)) {
1774                                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND headertag='$jobdb_headertag' AND status='processing'";
1775                                         my $res = $job_db->exec_statement( $sql_statement );
1776                                         foreach my $hit (@{$res}) {
1777                                                 push @drops, "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1778                                         }
1779                                 } else {
1780                                         daemon_log("J ERROR: Job without id exists for macaddress $macaddress!", 1);
1781                                 }
1782                         }
1783                 }
1785                 # Commit deletion
1786                 $job_db->exec_statementlist(\@drops);
1788                 # Look for new jobs that could be executed
1789                 foreach my $macaddress (keys %{$hits}) {
1791                         # Look if there is an executing job
1792                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='processing'";
1793                         my $res = $job_db->exec_statement( $sql_statement );
1795                         # Skip new jobs for host if there is a processing job
1796                         if(defined($res) and defined @{$res}[0]) {
1797                                 # Prevent race condition if there is a trigger_activate job waiting and a goto-activation job processing
1798                                 my $row = @{$res}[0] if (ref $res eq 'ARRAY');
1799                                 if(@{$row}[5] eq 'trigger_action_reinstall') {
1800                                         my $sql_statement_2 =  "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='waiting' AND headertag = 'trigger_activate_new'"; 
1801                                         my $res_2 = $job_db->exec_statement( $sql_statement_2 );
1802                                         if(defined($res_2) and defined @{$res_2}[0]) {
1803                                                 # Set status from goto-activation to 'waiting' and update timestamp
1804                                                 $job_db->exec_statement("UPDATE $job_queue_tn SET status='waiting' WHERE macaddress LIKE '$macaddress' AND headertag = 'trigger_action_reinstall'");
1805                                                 $job_db->exec_statement("UPDATE $job_queue_tn SET timestamp='".&calc_timestamp(&get_time(), 'plus', 30)."' WHERE macaddress LIKE '$macaddress' AND headertag = 'trigger_action_reinstall'");
1806                                         }
1807                                 }
1808                                 next;
1809                         }
1811                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1812                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1813                                 if(defined($jobdb_id)) {
1814                                         my $job_msg = @{$hits->{$macaddress}->{$jobdb_headertag}}[7];
1816                                         daemon_log("J DEBUG: its time to execute $job_msg", 7);
1817                                         my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1818                                         my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1820                                         # expect macaddress is unique!!!!!!
1821                                         my $target = $res_hash->{1}->{hostname};
1823                                         # change header
1824                                         $job_msg =~ s/<header>job_/<header>gosa_/;
1826                                         # add sqlite_id
1827                                         $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1829                                         $job_msg =~ /<header>(\S+)<\/header>/;
1830                                         my $header = $1 ;
1831                                         my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");                    
1833                                         # update status in job queue to ...
1834                     # ... 'processing', for jobs: 'reinstall', 'update'
1835                     if (($header =~ /gosa_trigger_action_reinstall/) 
1836                             || ($header =~ /gosa_trigger_activate_new/)
1837                             || ($header =~ /gosa_trigger_action_update/)) {
1838                         my $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
1839                         my $dbres = $job_db->update_dbentry($sql_statement);
1840                     }
1842                     # ... 'done', for all other jobs, they are no longer needed in the jobqueue
1843                     else {
1844                         my $sql_statement = "UPDATE $job_queue_tn SET status='done' WHERE id=$jobdb_id";
1845                         my $dbres = $job_db->update_dbentry($sql_statement);
1846                     }
1847                 
1849                                         # We don't want parallel processing
1850                                         last;
1851                                 }
1852                         }
1853                 }
1855                 $watch_for_new_jobs_in_progress = 0;
1856                 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1857         }
1861 sub watch_for_new_messages {
1862     my ($kernel,$heap) = @_[KERNEL, HEAP];
1863     my @coll_user_msg;   # collection list of outgoing messages
1864     
1865     # check messaging_db for new incoming messages with executable timestamp
1866     my $timestamp = &get_time();
1867     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS UNSIGNED))<$timestamp AND flag='n' AND direction='in' )";
1868     my $res = $messaging_db->exec_statement( $sql_statement );
1869         foreach my $hit (@{$res}) {
1871         # create outgoing messages
1872         my $message_to = @{$hit}[3];
1873         # translate message_to to plain login name
1874         my @message_to_l = split(/,/, $message_to);  
1875                 my %receiver_h; 
1876                 foreach my $receiver (@message_to_l) {
1877                         if ($receiver =~ /^u_([\s\S]*)$/) {
1878                                 $receiver_h{$1} = 0;
1879                         } elsif ($receiver =~ /^g_([\s\S]*)$/) {
1880                                 my $group_name = $1;
1881                                 # fetch all group members from ldap and add them to receiver hash
1882                                 my $ldap_handle = &get_ldap_handle();
1883                                 if (defined $ldap_handle) {
1884                                                 my $mesg = $ldap_handle->search(
1885                                                                                 base => $ldap_base,
1886                                                                                 scope => 'sub',
1887                                                                                 attrs => ['memberUid'],
1888                                                                                 filter => "cn=$group_name",
1889                                                                                 );
1890                                                 &release_ldap_handle($ldap_handle);
1891                                                 if ($mesg->count) {
1892                                                                 my @entries = $mesg->entries;
1893                                                                 foreach my $entry (@entries) {
1894                                                                                 my @receivers= $entry->get_value("memberUid");
1895                                                                                 foreach my $receiver (@receivers) { 
1896                                                                                                 $receiver_h{$receiver} = 0;
1897                                                                                 }
1898                                                                 }
1899                                                 } 
1900                                                 # translating errors ?
1901                                                 if ($mesg->code) {
1902                                                                 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: $mesg->error", 1);
1903                                                 }
1904                                 # ldap handle error ?           
1905                                 } else {
1906                                         daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: no ldap handle available", 1);
1907                                 }
1908                         } else {
1909                                 my $sbjct = &encode_base64(@{$hit}[1]);
1910                                 my $msg = &encode_base64(@{$hit}[7]);
1911                                 &daemon_log("M WARNING: unknown receiver '$receiver' for a user-message '$sbjct - $msg'", 3); 
1912                         }
1913                 }
1914                 my @receiver_l = keys(%receiver_h);
1916         my $message_id = @{$hit}[0];
1918         #add each outgoing msg to messaging_db
1919         my $receiver;
1920         foreach $receiver (@receiver_l) {
1921             my $sql_statement = "INSERT INTO $messaging_tn (id, subject, message_from, message_to, flag, direction, delivery_time, message, timestamp) ".
1922                 "VALUES ('".
1923                 $message_id."', '".    # id
1924                 @{$hit}[1]."', '".     # subject
1925                 @{$hit}[2]."', '".     # message_from
1926                 $receiver."', '".      # message_to
1927                 "none"."', '".         # flag
1928                 "out"."', '".          # direction
1929                 @{$hit}[6]."', '".     # delivery_time
1930                 @{$hit}[7]."', '".     # message
1931                 $timestamp."'".     # timestamp
1932                 ")";
1933             &daemon_log("M DEBUG: $sql_statement", 1);
1934             my $res = $messaging_db->exec_statement($sql_statement);
1935             &daemon_log("M INFO: message '".@{$hit}[0]."' is prepared for delivery to receiver '$receiver'", 5);
1936         }
1938         # set incoming message to flag d=deliverd
1939         $sql_statement = "UPDATE $messaging_tn SET flag='p' WHERE id='$message_id'"; 
1940         &daemon_log("M DEBUG: $sql_statement", 7);
1941         $res = $messaging_db->update_dbentry($sql_statement);
1942         &daemon_log("M INFO: message '$message_id' is set to flag 'p' (processed)", 5);
1943     }
1945     $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay); 
1946     return;
1949 sub watch_for_delivery_messages {
1950     my ($kernel, $heap) = @_[KERNEL, HEAP];
1952     # select outgoing messages
1953     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( flag='p' AND direction='out' )";
1954     #&daemon_log("0 DEBUG: $sql", 7);
1955     my $res = $messaging_db->exec_statement( $sql_statement );
1956     
1957     # build out msg for each    usr
1958     foreach my $hit (@{$res}) {
1959         my $receiver = @{$hit}[3];
1960         my $msg_id = @{$hit}[0];
1961         my $subject = @{$hit}[1];
1962         my $message = @{$hit}[7];
1964         # resolve usr -> host where usr is logged in
1965         my $sql = "SELECT * FROM $login_users_tn WHERE (user='$receiver')"; 
1966         #&daemon_log("0 DEBUG: $sql", 7);
1967         my $res = $login_users_db->exec_statement($sql);
1969         # receiver is logged in nowhere
1970         if (not ref(@$res[0]) eq "ARRAY") { next; }    
1972         # receiver ist logged in at a client registered at local server
1973                 my $send_succeed = 0;
1974                 foreach my $hit (@$res) {
1975                                 my $receiver_host = @$hit[0];
1976                 my $delivered2host = 0;
1977                                 &daemon_log("M DEBUG: user '$receiver' is logged in at host '$receiver_host'", 7);
1979                                 # Looking for host in know_clients_db 
1980                                 my $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$receiver_host')";
1981                                 my $res = $known_clients_db->exec_statement($sql);
1983                 # Host is known in known_clients_db
1984                 if (ref(@$res[0]) eq "ARRAY") {
1985                     my $receiver_key = @{@{$res}[0]}[2];
1986                     my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
1987                     my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
1988                     my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0); 
1989                     if ($error == 0 ) {
1990                         $send_succeed++ ;
1991                         $delivered2host++ ;
1992                         &daemon_log("M DEBUG: send message for user '$receiver' to host '$receiver_host'", 7); 
1993                     } else {
1994                         &daemon_log("M DEBUG: cannot send message for user '$receiver' to host '$receiver_host'", 7); 
1995                     }
1996                 }
1997                 
1998                 # Message already send, do not need to do anything more, otherwise ...
1999                 if ($delivered2host) { next;}
2000     
2001                 # ...looking for host in foreign_clients_db
2002                 $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$receiver_host')";
2003                 $res = $foreign_clients_db->exec_statement($sql);
2004   
2005                                 # Host is known in foreign_clients_db 
2006                                 if (ref(@$res[0]) eq "ARRAY") { 
2007                     my $registration_server = @{@{$res}[0]}[2];
2008                     
2009                     # Fetch encryption key for registration server
2010                     my $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$registration_server')";
2011                     my $res = $known_server_db->exec_statement($sql);
2012                     if (ref(@$res[0]) eq "ARRAY") { 
2013                         my $registration_server_key = @{@{$res}[0]}[3];
2014                         my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
2015                         my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
2016                         my $error = &send_msg_to_target($out_msg, $registration_server, $registration_server_key, "usr_msg", 0); 
2017                         if ($error == 0 ) {
2018                             $send_succeed++ ;
2019                             $delivered2host++ ;
2020                             &daemon_log("M DEBUG: send message for user '$receiver' to server '$registration_server'", 7); 
2021                         } else {
2022                             &daemon_log("M ERROR: cannot send message for user '$receiver' to server '$registration_server'", 1); 
2023                         }
2025                     } else {
2026                         &daemon_log("M ERROR: host '$receiver_host' is reported to be ".
2027                                 "registrated at server '$registration_server', ".
2028                                 "but no data available in known_server_db ", 1); 
2029                     }
2030                 }
2031                 
2032                 if (not $delivered2host) {
2033                     &daemon_log("M ERROR: unable to send user message to host '$receiver_host'", 1);
2034                 }
2035                 }
2037                 if ($send_succeed) {
2038                                 # set outgoing msg at db to deliverd
2039                                 my $sql = "UPDATE $messaging_tn SET flag='d' WHERE (id='$msg_id' AND direction='out' AND message_to='$receiver')"; 
2040                                 my $res = $messaging_db->exec_statement($sql); 
2041                 &daemon_log("M INFO: send message for user '$receiver' to logged in hosts", 5);
2042                 } else {
2043             &daemon_log("M WARNING: failed to deliver message for user '$receiver'", 3); 
2044         }
2045         }
2047     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay); 
2048     return;
2052 sub watch_for_done_messages {
2053     my ($kernel,$heap) = @_[KERNEL, HEAP];
2055     my $sql = "SELECT * FROM $messaging_tn WHERE (flag='p' AND direction='in')"; 
2056     #&daemon_log("0 DEBUG: $sql", 7);
2057     my $res = $messaging_db->exec_statement($sql); 
2059     foreach my $hit (@{$res}) {
2060         my $msg_id = @{$hit}[0];
2062         my $sql = "SELECT * FROM $messaging_tn WHERE (id='$msg_id' AND direction='out' AND (NOT flag='s'))"; 
2063         #&daemon_log("0 DEBUG: $sql", 7); 
2064         my $res = $messaging_db->exec_statement($sql);
2066         # not all usr msgs have been seen till now
2067         if ( ref(@$res[0]) eq "ARRAY") { next; }
2068         
2069         $sql = "DELETE FROM $messaging_tn WHERE (id='$msg_id')"; 
2070         #&daemon_log("0 DEBUG: $sql", 7);
2071         $res = $messaging_db->exec_statement($sql);
2072     
2073     }
2075     $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay); 
2076     return;
2080 sub watch_for_old_known_clients {
2081     my ($kernel,$heap) = @_[KERNEL, HEAP];
2083     my $sql_statement = "SELECT * FROM $known_clients_tn";
2084     my $res = $known_clients_db->select_dbentry( $sql_statement );
2086     my $cur_time = int(&get_time());
2088     while ( my ($hit_num, $hit) = each %$res) {
2089         my $expired_timestamp = int($hit->{'timestamp'});
2090         $expired_timestamp =~ /(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/;
2091         my $dt = DateTime->new( year   => $1,
2092                 month  => $2,
2093                 day    => $3,
2094                 hour   => $4,
2095                 minute => $5,
2096                 second => $6,
2097                 );
2099         $dt->add( seconds => 2 * int($hit->{'keylifetime'}) );
2100         $expired_timestamp = $dt->ymd('').$dt->hms('');
2101         if ($cur_time > $expired_timestamp) {
2102             my $hostname = $hit->{'hostname'};
2103             my $del_sql = "DELETE FROM $known_clients_tn WHERE hostname='$hostname'"; 
2104             my $del_res = $known_clients_db->exec_statement($del_sql);
2106             &main::daemon_log("0 INFO: timestamp '".$hit->{'timestamp'}."' of client '$hostname' is expired('$expired_timestamp'), client will be deleted from known_clients_db", 5);
2107         }
2109     }
2111     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
2115 sub watch_for_next_tasks {
2116     my ($kernel,$heap) = @_[KERNEL, HEAP];
2118     my $sql = "SELECT * FROM $incoming_tn";
2119     my $res = $incoming_db->select_dbentry($sql);
2120     
2121     while ( my ($hit_num, $hit) = each %$res) {
2122         my $headertag = $hit->{'headertag'};
2123         if ($headertag =~ /^answer_(\d+)/) {
2124             # do not start processing, this message is for a still running POE::Wheel
2125             next;
2126         }
2127         my $message_id = $hit->{'id'};
2128         my $session_id = $hit->{'sessionid'};
2129         &daemon_log("$session_id DEBUG: start processing for message with incoming id: '$message_id'", 7);
2131                 my $ldap_handle = &get_ldap_handle();
2132                 if (not defined $ldap_handle) { next; }
2133         $kernel->yield('next_task', $hit, $ldap_handle);
2135         my $sql = "DELETE FROM $incoming_tn WHERE id=$message_id";
2136         my $res = $incoming_db->exec_statement($sql);
2137     }
2139     $kernel->delay_set('watch_for_next_tasks', 1); 
2143 sub get_ldap_handle {
2144         my ($session_id) = @_;
2145         my $heap;
2147         if (not defined $session_id ) { $session_id = 0 };
2148         if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
2150         (my $package, my $file, my $row, my $subroutine, my $hasArgs, my $wantArray, my $evalText, my $isRequire) = caller(1);
2151         my $caller_text = "subroutin $subroutine";
2152         if ($subroutine eq "(eval)") {
2153                 $caller_text = "eval block within file '$file' for '$evalText'"; 
2154         }
2155         daemon_log("$session_id INFO: new ldap handle for $caller_text required", 9);
2157         my $ldap_handle = $ldap_pool->get();
2158         
2159         if (not defined $ldap_handle) {
2160                 daemon_log("$session_id ERROR: ldap handle for $caller_text not available", 1);
2161         }
2162         return $ldap_handle;
2165 sub release_ldap_handle {
2166         my ($ldap_handle) = @_ ;
2167         $ldap_pool->free($ldap_handle);
2168         return;
2172 sub change_fai_state {
2173     my ($st, $targets, $session_id) = @_;
2174     $session_id = 0 if not defined $session_id;
2175     # Set FAI state to localboot
2176     my %mapActions= (
2177         reboot    => '',
2178         update    => 'softupdate',
2179         localboot => 'localboot',
2180         reinstall => 'install',
2181         rescan    => '',
2182         wake      => '',
2183         memcheck  => 'memcheck',
2184         sysinfo   => 'sysinfo',
2185         install   => 'install',
2186     );
2188     # Return if this is unknown
2189     if (!exists $mapActions{ $st }){
2190         daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1); 
2191       return;
2192     }
2194     my $state= $mapActions{ $st };
2196     #if( defined($ldap_handle) ) {
2198       # Build search filter for hosts
2199         my $search= "(&(objectClass=GOhard)";
2200         foreach (@{$targets}){
2201             $search.= "(macAddress=$_)";
2202         }
2203         $search.= ")";
2205       # If there's any host inside of the search string, procress them
2206         if (!($search =~ /macAddress/)){
2207             daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);    
2208             return;
2209         }
2211         my $ldap_handle = &get_ldap_handle($session_id);
2212       # Perform search for Unit Tag
2213       my $mesg = $ldap_handle->search(
2214           base   => $ldap_base,
2215           scope  => 'sub',
2216           attrs  => ['dn', 'FAIstate', 'objectClass'],
2217           filter => "$search"
2218           );
2220           if ($mesg->count) {
2221                   my @entries = $mesg->entries;
2222                   if (0 == @entries) {
2223                                   daemon_log("$session_id ERROR: ldap search failed: ldap_base=$ldap_base, filter=$search", 1); 
2224                   }
2226                   foreach my $entry (@entries) {
2227                           # Only modify entry if it is not set to '$state'
2228                           if ($entry->get_value("FAIstate") ne "$state"){
2229                                   daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
2230                                   my $result;
2231                                   my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
2232                                   if (exists $tmp{'FAIobject'}){
2233                                           if ($state eq ''){
2234                                                   $result= $ldap_handle->modify($entry->dn, changes => [ delete => [ FAIstate => [] ] ]);
2235                                           } else {
2236                                                   $result= $ldap_handle->modify($entry->dn, changes => [ replace => [ FAIstate => $state ] ]);
2237                                           }
2238                                   } elsif ($state ne ''){
2239                                           $result= $ldap_handle->modify($entry->dn, changes => [ add => [ objectClass => 'FAIobject' ], add => [ FAIstate => $state ] ]);
2240                                   }
2242                                   # Errors?
2243                                   if ($result->code){
2244                                           daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2245                                   }
2246                           } else {
2247                                   daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 7); 
2248                           }  
2249                   }
2250           } else {
2251                 daemon_log("$session_id ERROR: LDAP search failed: ldap_base=$ldap_base, filter=$search", 1);
2252           }
2253           &release_ldap_handle($ldap_handle);             
2255     # if no ldap handle defined
2256     #} else {
2257     #    daemon_log("$session_id ERROR: no LDAP handle defined for update FAIstate", 1); 
2258     #}
2260         return;
2264 sub change_goto_state {
2265     my ($st, $targets, $session_id) = @_;
2266     $session_id = 0  if not defined $session_id;
2268     # Switch on or off?
2269     my $state= $st eq 'active' ? 'active': 'locked';
2271     my $ldap_handle = &get_ldap_handle($session_id);
2272     if( defined($ldap_handle) ) {
2274       # Build search filter for hosts
2275       my $search= "(&(objectClass=GOhard)";
2276       foreach (@{$targets}){
2277         $search.= "(macAddress=$_)";
2278       }
2279       $search.= ")";
2281       # If there's any host inside of the search string, procress them
2282       if (!($search =~ /macAddress/)){
2283         return;
2284       }
2286       # Perform search for Unit Tag
2287       my $mesg = $ldap_handle->search(
2288           base   => $ldap_base,
2289           scope  => 'sub',
2290           attrs  => ['dn', 'gotoMode'],
2291           filter => "$search"
2292           );
2294       if ($mesg->count) {
2295         my @entries = $mesg->entries;
2296         foreach my $entry (@entries) {
2298           # Only modify entry if it is not set to '$state'
2299           if ($entry->get_value("gotoMode") ne $state){
2301             daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
2302             my $result;
2303             $result= $ldap_handle->modify($entry->dn, changes => [replace => [ gotoMode => $state ] ]);
2305             # Errors?
2306             if ($result->code){
2307               &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2308             }
2310           }
2311         }
2312       } else {
2313                 daemon_log("$session_id ERROR: LDAP search failed in function change_goto_state: ldap_base=$ldap_base, filter=$search", 1);
2314           }
2316     }
2317         &release_ldap_handle($ldap_handle);
2318         return;
2322 sub run_recreate_packages_db {
2323     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2324     my $session_id = $session->ID;
2325         &main::daemon_log("$session_id INFO: Recreating FAI Packages DB ('$fai_release_tn', '$fai_server_tn', '$packages_list_tn')", 5);
2326         $kernel->yield('create_fai_release_db', $fai_release_tn);
2327         $kernel->yield('create_fai_server_db', $fai_server_tn);
2328         return;
2332 sub run_create_fai_server_db {
2333     my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2334     my $session_id = $session->ID;
2335         my $ldap_handle = &get_ldap_handle();
2336         if (not defined $ldap_handle) {
2337                 $kernel->delay_set('create_fai_server_db', 1, $table_name);
2338                 return;
2339         }
2340     my $task = POE::Wheel::Run->new(
2341             Program => sub { &create_fai_server_db($table_name,$kernel, undef, $session_id, $ldap_handle) },
2342             StdoutEvent  => "session_run_result",
2343             StderrEvent  => "session_run_debug",
2344             CloseEvent   => "session_run_done",
2345             );
2347     $heap->{task}->{ $task->ID } = $task;
2348         $heap->{ldap_handle}->{$task->ID} = $ldap_handle;
2349     return;
2353 sub create_fai_server_db {
2354         my ($table_name, $kernel, $dont_create_packages_list, $session_id, $ldap_handle) = @_;
2355         my $result;
2357         if (not defined $session_id) { $session_id = 0; }
2358         if(defined($ldap_handle)) {
2359                 daemon_log("$session_id INFO: create_fai_server_db: start", 5);
2360                 my $mesg= $ldap_handle->search(
2361                         base   => $ldap_base,
2362                         scope  => 'sub',
2363                         attrs  => ['FAIrepository', 'gosaUnitTag'],
2364                         filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
2365                 );
2366                 if($mesg->{'resultCode'} == 0 &&
2367                         $mesg->count != 0) {
2368                         foreach my $entry (@{$mesg->{entries}}) {
2369                                 if($entry->exists('FAIrepository')) {
2370                                         # Add an entry for each Repository configured for server
2371                                         foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
2372                                                 my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
2373                                                 my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
2374                                                 $result= $fai_server_db->add_dbentry( { 
2375                                                                 table => $table_name,
2376                                                                 primkey => ['server', 'fai_release', 'tag'],
2377                                                                 server => $tmp_url,
2378                                                                 fai_release => $tmp_release,
2379                                                                 sections => $tmp_sections,
2380                                                                 tag => (length($tmp_tag)>0)?$tmp_tag:"",
2381                                                         } );
2382                                         }
2383                                 }
2384                         }
2385                 }
2386                 daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
2388                 # TODO: Find a way to post the 'create_packages_list_db' event
2389                 if(not defined($dont_create_packages_list)) {
2390                         &create_packages_list_db(undef, $session_id);
2391                 }
2392         }       
2394         return $result;
2398 sub run_create_fai_release_db {
2399         my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2400         my $session_id = $session->ID;
2401         my $ldap_handle = &get_ldap_handle();
2402         if (not defined $ldap_handle) {
2403                 $kernel->delay_set('create_fai_release_db', 1, $table_name);
2404                 return;
2405         }
2406         my $task = POE::Wheel::Run->new(
2407                 Program => sub { &create_fai_release_db($table_name, $session_id, $ldap_handle) },
2408                 StdoutEvent  => "session_run_result",
2409                 StderrEvent  => "session_run_debug",
2410                 CloseEvent   => "session_run_done",
2411         );
2413         $heap->{task}->{ $task->ID } = $task;
2414         $heap->{ldap_handle}->{$task->ID} = $ldap_handle;
2415         return;
2419 sub create_fai_release_db {
2420         my ($table_name, $session_id, $ldap_handle) = @_;
2421         my $result;
2423         # used for logging
2424         if (not defined $session_id) { $session_id = 0; }
2426         #my $ldap_handle = &get_ldap_handle();
2427         if(defined($ldap_handle)) {
2428                 daemon_log("$session_id INFO: create_fai_release_db: start",5);
2429                 my $mesg= $ldap_handle->search(
2430                         base   => $ldap_base,
2431                         scope  => 'sub',
2432                         attrs  => [],
2433                         filter => "(&(objectClass=organizationalUnit)(ou=fai))",
2434                 );
2435                 if(($mesg->code == 0) && ($mesg->count != 0))
2436                 {
2437                         daemon_log("$session_id DEBUG: create_fai_release_db: count " . $mesg->count,8);
2439                         # Walk through all possible FAI container ou's
2440                         my @sql_list;
2441                         my $timestamp= &get_time();
2442                         foreach my $ou (@{$mesg->{entries}}) {
2443                                 my $tmp_classes= resolve_fai_classes($ou->dn, $ldap_handle, $session_id);
2444                                 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
2445                                         my @tmp_array=get_fai_release_entries($tmp_classes);
2446                                         if(@tmp_array) {
2447                                                 foreach my $entry (@tmp_array) {
2448                                                         if(defined($entry) && ref($entry) eq 'HASH') {
2449                                                                 my $sql= 
2450                                                                 "INSERT INTO $table_name "
2451                                                                 ."(timestamp, fai_release, class, type, state) VALUES ("
2452                                                                 .$timestamp.","
2453                                                                 ."'".$entry->{'release'}."',"
2454                                                                 ."'".$entry->{'class'}."',"
2455                                                                 ."'".$entry->{'type'}."',"
2456                                                                 ."'".$entry->{'state'}."')";
2457                                                                 push @sql_list, $sql;
2458                                                         }
2459                                                 }
2460                                         }
2461                                 }
2462                         }
2464                         daemon_log("$session_id DEBUG: create_fai_release_db: Inserting ".scalar @sql_list." entries to DB",8);
2465                         if(@sql_list) {
2466                                 unshift @sql_list, "VACUUM";
2467                                 unshift @sql_list, "DELETE FROM $table_name";
2468                                 $fai_release_db->exec_statementlist(\@sql_list);
2469                         }
2470                         daemon_log("$session_id DEBUG: create_fai_release_db: Done with inserting",7);
2471                 } else {
2472                         daemon_log("$session_id INFO: create_fai_release_db: error: " . $mesg->code, 5);
2473                 }
2474                 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
2475         }
2476         #&release_ldap_handle($ldap_handle);
2477         return $result;
2480 sub get_fai_types {
2481         my $tmp_classes = shift || return undef;
2482         my @result;
2484         foreach my $type(keys %{$tmp_classes}) {
2485                 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
2486                         my $entry = {
2487                                 type => $type,
2488                                 state => $tmp_classes->{$type}[0],
2489                         };
2490                         push @result, $entry;
2491                 }
2492         }
2494         return @result;
2497 sub get_fai_state {
2498         my $result = "";
2499         my $tmp_classes = shift || return $result;
2501         foreach my $type(keys %{$tmp_classes}) {
2502                 if(defined($tmp_classes->{$type}[0])) {
2503                         $result = $tmp_classes->{$type}[0];
2504                         
2505                 # State is equal for all types in class
2506                         last;
2507                 }
2508         }
2510         return $result;
2513 sub resolve_fai_classes {
2514         my ($fai_base, $ldap_handle, $session_id) = @_;
2515         if (not defined $session_id) { $session_id = 0; }
2516         my $result;
2517         my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
2518         my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
2519         my $fai_classes;
2521         daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base",7);
2522         my $mesg= $ldap_handle->search(
2523                 base   => $fai_base,
2524                 scope  => 'sub',
2525                 attrs  => ['cn','objectClass','FAIstate'],
2526                 filter => $fai_filter,
2527         );
2528         daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries",7);
2530         if($mesg->{'resultCode'} == 0 &&
2531                 $mesg->count != 0) {
2532                 foreach my $entry (@{$mesg->{entries}}) {
2533                         if($entry->exists('cn')) {
2534                                 my $tmp_dn= $entry->dn();
2535                                 $tmp_dn= substr( $tmp_dn, 0, length($tmp_dn)
2536                                         - length($fai_base) - 1 );
2538                                 # Skip classname and ou dn parts for class
2539                                 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?)$/;
2541                                 # Skip classes without releases
2542                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2543                                         next;
2544                                 }
2546                                 my $tmp_cn= $entry->get_value('cn');
2547                                 my $tmp_state= $entry->get_value('FAIstate');
2549                                 my $tmp_type;
2550                                 # Get FAI type
2551                                 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
2552                                         if(grep $_ eq $oclass, @possible_fai_classes) {
2553                                                 $tmp_type= $oclass;
2554                                                 last;
2555                                         }
2556                                 }
2558                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2559                                         # A Subrelease
2560                                         my @sub_releases = split(/,/, $tmp_release);
2562                                         # Walk through subreleases and build hash tree
2563                                         my $hash;
2564                                         while(my $tmp_sub_release = pop @sub_releases) {
2565                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2566                                         }
2567                                         eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
2568                                 } else {
2569                                         # A branch, no subrelease
2570                                         push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
2571                                 }
2572                         } elsif (!$entry->exists('cn')) {
2573                                 my $tmp_dn= $entry->dn();
2574                                 $tmp_dn= substr( $tmp_dn, 0, length($tmp_dn)
2575                                         - length($fai_base) - 1 );
2576                                 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?)$/;
2578                                 # Skip classes without releases
2579                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2580                                         next;
2581                                 }
2583                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2584                                         # A Subrelease
2585                                         my @sub_releases= split(/,/, $tmp_release);
2587                                         # Walk through subreleases and build hash tree
2588                                         my $hash;
2589                                         while(my $tmp_sub_release = pop @sub_releases) {
2590                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2591                                         }
2592                                         # Remove the last two characters
2593                                         chop($hash);
2594                                         chop($hash);
2596                                         eval('$fai_classes->'.$hash.'= {}');
2597                                 } else {
2598                                         # A branch, no subrelease
2599                                         if(!exists($fai_classes->{$tmp_release})) {
2600                                                 $fai_classes->{$tmp_release} = {};
2601                                         }
2602                                 }
2603                         }
2604                 }
2606                 # The hash is complete, now we can honor the copy-on-write based missing entries
2607                 foreach my $release (keys %$fai_classes) {
2608                         $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
2609                 }
2610         }
2611         return $result;
2614 sub apply_fai_inheritance {
2615        my $fai_classes = shift || return {};
2616        my $tmp_classes;
2618        # Get the classes from the branch
2619        foreach my $class (keys %{$fai_classes}) {
2620                # Skip subreleases
2621                if($class =~ /^ou=.*$/) {
2622                        next;
2623                } else {
2624                        $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
2625                }
2626        }
2628        # Apply to each subrelease
2629        foreach my $subrelease (keys %{$fai_classes}) {
2630                if($subrelease =~ /ou=/) {
2631                        foreach my $tmp_class (keys %{$tmp_classes}) {
2632                                if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
2633                                        $fai_classes->{$subrelease}->{$tmp_class} =
2634                                        deep_copy($tmp_classes->{$tmp_class});
2635                                } else {
2636                                        foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
2637                                                if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
2638                                                        $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
2639                                                        deep_copy($tmp_classes->{$tmp_class}->{$type});
2640                                                }
2641                                        }
2642                                }
2643                        }
2644                }
2645        }
2647        # Find subreleases in deeper levels
2648        foreach my $subrelease (keys %{$fai_classes}) {
2649                if($subrelease =~ /ou=/) {
2650                        foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
2651                                if($subsubrelease =~ /ou=/) {
2652                                        apply_fai_inheritance($fai_classes->{$subrelease});
2653                                }
2654                        }
2655                }
2656        }
2658        return $fai_classes;
2661 sub get_fai_release_entries {
2662         my $tmp_classes = shift || return;
2663         my $parent = shift || "";
2664         my @result = shift || ();
2666         foreach my $entry (keys %{$tmp_classes}) {
2667                 if(defined($entry)) {
2668                         if($entry =~ /^ou=.*$/) {
2669                                 my $release_name = $entry;
2670                                 $release_name =~ s/ou=//g;
2671                                 if(length($parent)>0) {
2672                                         $release_name = $parent."/".$release_name;
2673                                 }
2674                                 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2675                                 foreach my $bufentry(@bufentries) {
2676                                         push @result, $bufentry;
2677                                 }
2678                         } else {
2679                                 my @types = get_fai_types($tmp_classes->{$entry});
2680                                 foreach my $type (@types) {
2681                                         push @result, 
2682                                         {
2683                                                 'class' => $entry,
2684                                                 'type' => $type->{'type'},
2685                                                 'release' => $parent,
2686                                                 'state' => $type->{'state'},
2687                                         };
2688                                 }
2689                         }
2690                 }
2691         }
2693         return @result;
2696 sub deep_copy {
2697         my $this = shift;
2698         if (not ref $this) {
2699                 $this;
2700         } elsif (ref $this eq "ARRAY") {
2701                 [map deep_copy($_), @$this];
2702         } elsif (ref $this eq "HASH") {
2703                 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2704         } else { die "what type is $_?" }
2708 sub session_run_result {
2709     my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];    
2710     $kernel->sig(CHLD => "child_reap");
2713 sub session_run_debug {
2714     my $result = $_[ARG0];
2715     print STDERR "$result\n";
2718 sub session_run_done {
2719     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2720     delete $heap->{task}->{$task_id};
2721         if (exists $heap->{ldap_handle}->{$task_id}) {
2722                 &release_ldap_handle($heap->{ldap_handle}->{$task_id});
2723         }
2724         delete $heap->{ldap_handle}->{$task_id};
2728 sub create_sources_list {
2729         my $session_id = shift;
2730         my $result="/tmp/gosa_si_tmp_sources_list";
2732         # Remove old file
2733         if(stat($result)) {
2734                 unlink($result);
2735                 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7); 
2736         }
2738         my $fh;
2739         open($fh, ">$result");
2740         if (not defined $fh) {
2741                 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7); 
2742                 return undef;
2743         }
2744         if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2745                 my $ldap_handle = &get_ldap_handle();
2746                 my $mesg=$ldap_handle->search(
2747                         base    => $main::ldap_server_dn,
2748                         scope   => 'base',
2749                         attrs   => 'FAIrepository',
2750                         filter  => 'objectClass=FAIrepositoryServer'
2751                 );
2752                 &release_ldap_handle($ldap_handle);
2753                 if($mesg->count) {
2754                         foreach my $entry(@{$mesg->{'entries'}}) {
2755                                 foreach my $value(@{$entry->get_value('FAIrepository', asref => 1)}) {
2756                                         my ($server, $tag, $release, $sections)= split /\|/, $value;
2757                                         my $line = "deb $server $release";
2758                                         $sections =~ s/,/ /g;
2759                                         $line.= " $sections";
2760                                         print $fh $line."\n";
2761                                 }
2762                         }
2763                 }
2764         } else {
2765                 if (defined $main::ldap_server_dn){
2766                         &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1); 
2767                 } else {
2768                         &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2769                 }
2770         }
2771         close($fh);
2773         return $result;
2777 sub run_create_packages_list_db {
2778     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2779         my $session_id = $session->ID;
2780         my $task = POE::Wheel::Run->new(
2781                                         Priority => +20,
2782                                         Program => sub {&create_packages_list_db(undef, $session_id)},
2783                                         StdoutEvent  => "session_run_result",
2784                                         StderrEvent  => "session_run_debug",
2785                                         CloseEvent   => "session_run_done",
2786                                         );
2787         $heap->{task}->{ $task->ID } = $task;
2791 sub create_packages_list_db {
2792         my ($sources_file, $session_id) = @_;
2793         
2794         # it should not be possible to trigger a recreation of packages_list_db
2795         # while packages_list_db is under construction, so set flag packages_list_under_construction
2796         # which is tested befor recreation can be started
2797         if (-r $packages_list_under_construction) {
2798                 daemon_log("$session_id WARNING: packages_list_db is right now under construction, please wait until this process is finished", 3);
2799                 return;
2800         } else {
2801                 daemon_log("$session_id INFO: create_packages_list_db: start", 5); 
2802                 # set packages_list_under_construction to true
2803                 system("touch $packages_list_under_construction");
2804                 @packages_list_statements=();
2805         }
2807         if (not defined $session_id) { $session_id = 0; }
2809         if (not defined $sources_file) { 
2810                 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5); 
2811                 $sources_file = &create_sources_list($session_id);
2812         }
2814         if (not defined $sources_file) {
2815                 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1); 
2816                 unlink($packages_list_under_construction);
2817                 return;
2818         }
2820         my $line;
2822         open(CONFIG, "<$sources_file") or do {
2823                 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2824                 unlink($packages_list_under_construction);
2825                 return;
2826         };
2828         # Read lines
2829         while ($line = <CONFIG>){
2830                 # Unify
2831                 chop($line);
2832                 $line =~ s/^\s+//;
2833                 $line =~ s/^\s+/ /;
2835                 # Strip comments
2836                 $line =~ s/#.*$//g;
2838                 # Skip empty lines
2839                 if ($line =~ /^\s*$/){
2840                         next;
2841                 }
2843                 # Interpret deb line
2844                 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2845                         my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2846                         my $section;
2847                         foreach $section (split(' ', $sections)){
2848                                 &parse_package_info( $baseurl, $dist, $section, $session_id );
2849                         }
2850                 }
2851         }
2853         close (CONFIG);
2855         if(keys(%repo_dirs)) {
2856                 find(\&cleanup_and_extract, keys( %repo_dirs ));
2857                 &main::strip_packages_list_statements();
2858                 $packages_list_db->exec_statementlist(\@packages_list_statements);
2859         }
2860         unlink($packages_list_under_construction);
2861         daemon_log("$session_id INFO: create_packages_list_db: finished", 5); 
2862         return;
2865 # This function should do some intensive task to minimize the db-traffic
2866 sub strip_packages_list_statements {
2867         my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2868         my @new_statement_list=();
2869         my $hash;
2870         my $insert_hash;
2871         my $update_hash;
2872         my $delete_hash;
2873         my $known_packages_hash;
2874         my $local_timestamp=get_time();
2876         foreach my $existing_entry (@existing_entries) {
2877                 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
2878         }
2880         foreach my $statement (@packages_list_statements) {
2881                 if($statement =~ /^INSERT/i) {
2882                         # Assign the values from the insert statement
2883                         my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~ 
2884                         /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
2885                         if(exists($hash->{$distribution}->{$package}->{$version})) {
2886                                 # If section or description has changed, update the DB
2887                                 if( 
2888                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or 
2889                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
2890                                 ) {
2891                                         @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
2892                                 } else {
2893                                         # package is already present in database. cache this knowledge for later use
2894                                         @{$known_packages_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2895                                 }
2896                         } else {
2897                                 # Insert a non-existing entry to db
2898                                 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2899                         }
2900                 } elsif ($statement =~ /^UPDATE/i) {
2901                         my ($template,$package,$version) = ($1,$2,$3) if $statement =~
2902                         /^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;
2903                         foreach my $distribution (keys %{$hash}) {
2904                                 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
2905                                         # update the insertion hash to execute only one query per package (insert instead insert+update)
2906                                         @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
2907                                 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
2908                                         if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
2909                                                 my $section;
2910                                                 my $description;
2911                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
2912                                                         length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
2913                                                         $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
2914                                                 }
2915                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2916                                                         $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
2917                                                 }
2918                                                 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2919                                         }
2920                                 }
2921                         }
2922                 }
2923         }
2925         # Check for orphaned entries
2926         foreach my $existing_entry (@existing_entries) {
2927                 my $distribution= @{$existing_entry}[0];
2928                 my $package= @{$existing_entry}[1];
2929                 my $version= @{$existing_entry}[2];
2930                 my $section= @{$existing_entry}[3];
2932                 if(
2933                         exists($insert_hash->{$distribution}->{$package}->{$version}) ||
2934                         exists($update_hash->{$distribution}->{$package}->{$version}) ||
2935                         exists($known_packages_hash->{$distribution}->{$package}->{$version})
2936                 ) {
2937                         next;
2938                 } else {
2939                         # Insert entry to delete hash
2940                         @{$delete_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section);
2941                 }
2942         }
2944         # unroll the insert hash
2945         foreach my $distribution (keys %{$insert_hash}) {
2946                 foreach my $package (keys %{$insert_hash->{$distribution}}) {
2947                         foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
2948                                 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
2949                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
2950                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
2951                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
2952                                 ."'$local_timestamp')";
2953                         }
2954                 }
2955         }
2957         # unroll the update hash
2958         foreach my $distribution (keys %{$update_hash}) {
2959                 foreach my $package (keys %{$update_hash->{$distribution}}) {
2960                         foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
2961                                 my $set = "";
2962                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
2963                                         $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
2964                                 }
2965                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2966                                         $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
2967                                 }
2968                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
2969                                         $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
2970                                 }
2971                                 if(defined($set) and length($set) > 0) {
2972                                         $set .= "timestamp = '$local_timestamp'";
2973                                 } else {
2974                                         next;
2975                                 }
2976                                 push @new_statement_list, 
2977                                 "UPDATE $main::packages_list_tn SET $set WHERE"
2978                                 ." distribution = '$distribution'"
2979                                 ." AND package = '$package'"
2980                                 ." AND version = '$version'";
2981                         }
2982                 }
2983         }
2984         
2985         # unroll the delete hash
2986         foreach my $distribution (keys %{$delete_hash}) {
2987                 foreach my $package (keys %{$delete_hash->{$distribution}}) {
2988                         foreach my $version (keys %{$delete_hash->{$distribution}->{$package}}) {
2989                                 my $section = @{$delete_hash->{$distribution}->{$package}->{$version}}[3];
2990                                 push @new_statement_list, "DELETE FROM $main::packages_list_tn WHERE distribution='$distribution' AND package='$package' AND version='$version' AND section='$section'";
2991                         }
2992                 }
2993         }
2995         unshift(@new_statement_list, "VACUUM");
2997         @packages_list_statements = @new_statement_list;
3001 sub parse_package_info {
3002     my ($baseurl, $dist, $section, $session_id)= @_;
3003     my ($package);
3004     if (not defined $session_id) { $session_id = 0; }
3005     my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
3006     $repo_dirs{ "${repo_path}/pool" } = 1;
3008     foreach $package ("Packages.gz"){
3009         daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
3010         get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
3011         parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
3012     }
3013     
3017 sub get_package {
3018     my ($url, $dest, $session_id)= @_;
3019     if (not defined $session_id) { $session_id = 0; }
3021     my $tpath = dirname($dest);
3022     -d "$tpath" || mkpath "$tpath";
3024     # This is ugly, but I've no time to take a look at "how it works in perl"
3025     if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
3026         system("gunzip -cd '$dest' > '$dest.in'");
3027         daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 5);
3028         unlink($dest);
3029         daemon_log("$session_id DEBUG: delete file '$dest'", 5); 
3030     } else {
3031         daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' into '$dest' failed!", 1);
3032     }
3033     return 0;
3037 sub parse_package {
3038     my ($path, $dist, $srv_path, $session_id)= @_;
3039     if (not defined $session_id) { $session_id = 0;}
3040     my ($package, $version, $section, $description);
3041     my $PACKAGES;
3042     my $timestamp = &get_time();
3044     if(not stat("$path.in")) {
3045         daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
3046         return;
3047     }
3049     open($PACKAGES, "<$path.in");
3050     if(not defined($PACKAGES)) {
3051         daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1); 
3052         return;
3053     }
3055     # Read lines
3056     while (<$PACKAGES>){
3057         my $line = $_;
3058         # Unify
3059         chop($line);
3061         # Use empty lines as a trigger
3062         if ($line =~ /^\s*$/){
3063             my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
3064             push(@packages_list_statements, $sql);
3065             $package = "none";
3066             $version = "none";
3067             $section = "none";
3068             $description = "none"; 
3069             next;
3070         }
3072         # Trigger for package name
3073         if ($line =~ /^Package:\s/){
3074             ($package)= ($line =~ /^Package: (.*)$/);
3075             next;
3076         }
3078         # Trigger for version
3079         if ($line =~ /^Version:\s/){
3080             ($version)= ($line =~ /^Version: (.*)$/);
3081             next;
3082         }
3084         # Trigger for description
3085         if ($line =~ /^Description:\s/){
3086             ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
3087             next;
3088         }
3090         # Trigger for section
3091         if ($line =~ /^Section:\s/){
3092             ($section)= ($line =~ /^Section: (.*)$/);
3093             next;
3094         }
3096         # Trigger for filename
3097         if ($line =~ /^Filename:\s/){
3098             my ($filename) = ($line =~ /^Filename: (.*)$/);
3099             store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
3100             next;
3101         }
3102     }
3104     close( $PACKAGES );
3105     unlink( "$path.in" );
3109 sub store_fileinfo {
3110     my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
3112     my %fileinfo = (
3113         'package' => $package,
3114         'dist' => $dist,
3115         'version' => $vers,
3116     );
3118     $repo_files{ "${srvdir}/$file" } = \%fileinfo;
3122 sub cleanup_and_extract {
3123         my $fileinfo = $repo_files{ $File::Find::name };
3125         if( defined $fileinfo ) {
3126                 my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
3127                 my $sql;
3128                 my $package = $fileinfo->{ 'package' };
3129                 my $newver = $fileinfo->{ 'version' };
3131                 mkpath($dir);
3132                 system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
3134                 if( -f "$dir/DEBIAN/templates" ) {
3136                         daemon_log("0 DEBUG: Found debconf templates in '$package' - $newver", 7);
3138                         my $tmpl= ""; {
3139                                 local $/=undef;
3140                                 open FILE, "$dir/DEBIAN/templates";
3141                                 $tmpl = &encode_base64(<FILE>);
3142                                 close FILE;
3143                         }
3144                         rmtree("$dir/DEBIAN/templates");
3146                         $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
3147                         push @packages_list_statements, $sql;
3148                 }
3149         }
3151         return;
3155 sub register_at_foreign_servers {   
3156     my ($kernel) = $_[KERNEL];
3158     # hole alle bekannten server aus known_server_db
3159     my $server_sql = "SELECT * FROM $known_server_tn";
3160     my $server_res = $known_server_db->exec_statement($server_sql);
3162     # no entries in known_server_db
3163     if (not ref(@$server_res[0]) eq "ARRAY") { 
3164         # TODO
3165     }
3167     # detect already connected clients
3168     my $client_sql = "SELECT * FROM $known_clients_tn"; 
3169     my $client_res = $known_clients_db->exec_statement($client_sql);
3171     # send my server details to all other gosa-si-server within the network
3172     foreach my $hit (@$server_res) {
3173         my $hostname = @$hit[0];
3174         my $hostkey = &create_passwd;
3176         # add already connected clients to registration message 
3177         my $myhash = &create_xml_hash('new_server', $server_address, $hostname);
3178         &add_content2xml_hash($myhash, 'key', $hostkey);
3179         map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res);
3181         # add locally loaded gosa-si modules to registration message
3182         my $loaded_modules = {};
3183         while (my ($package, $pck_info) = each %$known_modules) {
3184                                                 next if ((!defined(@$pck_info[2])) || (!(ref (@$pck_info[2]) eq 'HASH')));
3185                                                 foreach my $act_module (keys(%{@$pck_info[2]})) {
3186                                                         $loaded_modules->{$act_module} = ""; 
3187                                                 }
3188         }
3190         map(&add_content2xml_hash($myhash, "loaded_modules", $_), keys(%$loaded_modules));
3192         # add macaddress to registration message
3193         my ($host_ip, $host_port) = split(/:/, $hostname);
3194         my $local_ip = &get_local_ip_for_remote_ip($host_ip);
3195         my $network_interface= &get_interface_for_ip($local_ip);
3196         my $host_mac = &get_mac_for_interface($network_interface);
3197         &add_content2xml_hash($myhash, 'macaddress', $host_mac);
3198         
3199         # build registration message and send it
3200         my $foreign_server_msg = &create_xml_string($myhash);
3201         my $error = &send_msg_to_target($foreign_server_msg, $hostname, $ServerPackages_key, "new_server", 0); 
3202     }
3203     
3204     $kernel->delay_set("register_at_foreign_servers", $foreign_servers_register_delay); 
3205     return;
3209 #==== MAIN = main ==============================================================
3210 #  parse commandline options
3211 Getopt::Long::Configure( "bundling" );
3212 GetOptions("h|help" => \&usage,
3213         "c|config=s" => \$cfg_file,
3214         "f|foreground" => \$foreground,
3215         "v|verbose+" => \$verbose,
3216         "no-arp+" => \$no_arp,
3217            );
3219 #  read and set config parameters
3220 &check_cmdline_param ;
3221 &read_configfile($cfg_file, %cfg_defaults);
3222 &check_pid;
3224 $SIG{CHLD} = 'IGNORE';
3226 # forward error messages to logfile
3227 if( ! $foreground ) {
3228   open( STDIN,  '+>/dev/null' );
3229   open( STDOUT, '+>&STDIN'    );
3230   open( STDERR, '+>&STDIN'    );
3233 # Just fork, if we are not in foreground mode
3234 if( ! $foreground ) { 
3235     chdir '/'                 or die "Can't chdir to /: $!";
3236     $pid = fork;
3237     setsid                    or die "Can't start a new session: $!";
3238     umask 0;
3239 } else { 
3240     $pid = $$; 
3243 # Do something useful - put our PID into the pid_file
3244 if( 0 != $pid ) {
3245     open( LOCK_FILE, ">$pid_file" );
3246     print LOCK_FILE "$pid\n";
3247     close( LOCK_FILE );
3248     if( !$foreground ) { 
3249         exit( 0 ) 
3250     };
3253 # parse head url and revision from svn
3254 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
3255 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
3256 $server_headURL = defined $1 ? $1 : 'unknown' ;
3257 $server_revision = defined $2 ? $2 : 'unknown' ;
3258 if ($server_headURL =~ /\/tag\// || 
3259         $server_headURL =~ /\/branches\// ) {
3260     $server_status = "stable"; 
3261 } else {
3262     $server_status = "developmental" ;
3264 # Prepare log file and set permissions
3265 $root_uid = getpwnam('root');
3266 $adm_gid = getgrnam('adm');
3267 open(FH, ">>$log_file");
3268 close FH;
3269 chmod(0440, $log_file);
3270 chown($root_uid, $adm_gid, $log_file);
3271 chown($root_uid, $adm_gid, "/var/lib/gosa-si");
3273 daemon_log(" ", 1);
3274 daemon_log("$0 started!", 1);
3275 daemon_log("status: $server_status", 1);
3276 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1); 
3278 # Create a pool of LDAP handles
3279 $ldap_factory =  ResourcePool::Factory::Net::LDAP->new($ldap_uri, version => $ldap_version);
3280 $ldap_factory->bind($ldap_admin_dn, password=>$ldap_admin_password);
3281 $ldap_pool = ResourcePool->new($ldap_factory,
3282                 Max         => 10,
3283                 #MaxTry      => 1,
3284                 #SleepOnFail    => [0, 0, 1, 1],
3285                 PreCreate       => 5,
3286 );
3289 # Buildup data bases
3291     no strict "refs";
3293     if ($db_module eq "DBmysql") {
3294         # connect to incoming_db
3295         $incoming_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3297         # connect to gosa-si job queue
3298         $job_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3300         # connect to known_clients_db
3301         $known_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3303         # connect to foreign_clients_db
3304         $foreign_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3306         # connect to known_server_db
3307         $known_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3309         # connect to login_usr_db
3310         $login_users_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3312         # connect to fai_server_db 
3313         $fai_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3315         # connect to fai_release_db
3316         $fai_release_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3318         # connect to packages_list_db
3319         $packages_list_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3321         # connect to messaging_db
3322         $messaging_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3324     } elsif ($db_module eq "DBsqlite") {
3325         # connect to incoming_db
3326         unlink($incoming_file_name);
3327         $incoming_db = GOSA::DBsqlite->new($incoming_file_name);
3328         
3329         # connect to gosa-si job queue
3330         unlink($job_queue_file_name);  ## just for debugging
3331         $job_db = GOSA::DBsqlite->new($job_queue_file_name);
3332         chmod(0640, $job_queue_file_name);
3333         chown($root_uid, $adm_gid, $job_queue_file_name);
3334         
3335         # connect to known_clients_db
3336         unlink($known_clients_file_name);   ## just for debugging
3337         $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
3338         chmod(0640, $known_clients_file_name);
3339         chown($root_uid, $adm_gid, $known_clients_file_name);
3340         
3341         # connect to foreign_clients_db
3342         unlink($foreign_clients_file_name);
3343         $foreign_clients_db = GOSA::DBsqlite->new($foreign_clients_file_name);
3344         chmod(0640, $foreign_clients_file_name);
3345         chown($root_uid, $adm_gid, $foreign_clients_file_name);
3346         
3347         # connect to known_server_db
3348         unlink($known_server_file_name);
3349         $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
3350         chmod(0640, $known_server_file_name);
3351         chown($root_uid, $adm_gid, $known_server_file_name);
3352         
3353         # connect to login_usr_db
3354         unlink($login_users_file_name);
3355         $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
3356         chmod(0640, $login_users_file_name);
3357         chown($root_uid, $adm_gid, $login_users_file_name);
3358         
3359         # connect to fai_server_db
3360         unlink($fai_server_file_name);
3361         $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
3362         chmod(0640, $fai_server_file_name);
3363         chown($root_uid, $adm_gid, $fai_server_file_name);
3364         
3365         # connect to fai_release_db
3366         unlink($fai_release_file_name);
3367         $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
3368         chmod(0640, $fai_release_file_name);
3369         chown($root_uid, $adm_gid, $fai_release_file_name);
3370         
3371         # connect to packages_list_db
3372         #unlink($packages_list_file_name);
3373         unlink($packages_list_under_construction);
3374         $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
3375         chmod(0640, $packages_list_file_name);
3376         chown($root_uid, $adm_gid, $packages_list_file_name);
3377         
3378         # connect to messaging_db
3379         unlink($messaging_file_name);
3380         $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
3381         chmod(0640, $messaging_file_name);
3382         chown($root_uid, $adm_gid, $messaging_file_name);
3383     }
3387 # Creating tables
3388 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
3389 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
3390 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
3391 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
3392 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
3393 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
3394 $foreign_clients_db->create_table($foreign_clients_tn, \@foreign_clients_col_names);
3395 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
3396 $incoming_db->create_table($incoming_tn, \@incoming_col_names);
3397 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
3399 # create xml object used for en/decrypting
3400 $xml = new XML::Simple();
3403 # foreign servers 
3404 my @foreign_server_list;
3406 # add foreign server from cfg file
3407 if ($foreign_server_string ne "") {
3408     my @cfg_foreign_server_list = split(",", $foreign_server_string);
3409     foreach my $foreign_server (@cfg_foreign_server_list) {
3410         push(@foreign_server_list, $foreign_server);
3411     }
3413     daemon_log("0 INFO: found foreign server in config file: ".join(", ", @foreign_server_list), 5);
3416 # Perform a DNS lookup for server registration if flag is true
3417 if ($dns_lookup eq "true") {
3418     # Add foreign server from dns
3419     my @tmp_servers;
3420     if (not $server_domain) {
3421         # Try our DNS Searchlist
3422         for my $domain(get_dns_domains()) {
3423             chomp($domain);
3424             my ($tmp_domains, $error_string) = &get_server_addresses($domain);
3425             if(@$tmp_domains) {
3426                 for my $tmp_server(@$tmp_domains) {
3427                     push @tmp_servers, $tmp_server;
3428                 }
3429             }
3430         }
3431         if(@tmp_servers && length(@tmp_servers)==0) {
3432             daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3433         }
3434     } else {
3435         @tmp_servers = &get_server_addresses($server_domain);
3436         if( 0 == @tmp_servers ) {
3437             daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3438         }
3439     }
3441     daemon_log("0 INFO: found foreign server via DNS ".join(", ", @tmp_servers), 5);    
3443     foreach my $server (@tmp_servers) { 
3444         unshift(@foreign_server_list, $server); 
3445     }
3446 } else {
3447     daemon_log("0 INFO: DNS lookup for server registration is disabled", 5);
3451 # eliminate duplicate entries
3452 @foreign_server_list = &del_doubles(@foreign_server_list);
3453 my $all_foreign_server = join(", ", @foreign_server_list);
3454 daemon_log("0 INFO: found foreign server in config file and DNS: '$all_foreign_server'", 5);
3456 # add all found foreign servers to known_server
3457 my $cur_timestamp = &get_time();
3458 foreach my $foreign_server (@foreign_server_list) {
3460         # do not add myself to known_server_db
3461         if (&is_local($foreign_server)) { next; }
3462         ######################################
3464     my $res = $known_server_db->add_dbentry( {table=>$known_server_tn, 
3465             primkey=>['hostname'],
3466             hostname=>$foreign_server,
3467             macaddress=>"",
3468             status=>'not_yet_registered',
3469             hostkey=>"none",
3470             loaded_modules => "none", 
3471             timestamp=>$cur_timestamp,
3472             } );
3476 # Import all modules
3477 &import_modules;
3479 # Check wether all modules are gosa-si valid passwd check
3480 &password_check;
3482 # Prepare for using Opsi 
3483 if ($opsi_enabled eq "true") {
3484     use JSON::RPC::Client;
3485     use XML::Quote qw(:all);
3486     $opsi_url= "https://".$opsi_admin.":".$opsi_password."@".$opsi_server.":4447/rpc";
3487     $opsi_client = new JSON::RPC::Client;
3491 POE::Component::Server::TCP->new(
3492         Alias => "TCP_SERVER",
3493         Port => $server_port,
3494         ClientInput => sub {
3495                 my ($kernel, $input, $heap, $session) = @_[KERNEL, ARG0, HEAP, SESSION];
3496         my $session_id = $session->ID;
3497         my $remote_ip = $heap->{'remote_ip'};
3498                 push(@msgs_to_decrypt, $input);
3499         &daemon_log("$session_id DEBUG: incoming message from '$remote_ip'", 7);
3500                 $kernel->yield("msg_to_decrypt");
3501         },
3502         InlineStates => {
3503                 msg_to_decrypt => \&msg_to_decrypt,
3504                 next_task => \&next_task,
3505                 task_result => \&handle_task_result,
3506                 task_done   => \&handle_task_done,
3507                 task_debug  => \&handle_task_debug,
3508                 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3509         }
3510 );
3512 daemon_log("0 INFO: start socket for incoming xml messages at port '$server_port' ", 1);
3514 # create session for repeatedly checking the job queue for jobs
3515 POE::Session->create(
3516         inline_states => {
3517                 _start => \&session_start,
3518         register_at_foreign_servers => \&register_at_foreign_servers,
3519         sig_handler => \&sig_handler,
3520         next_task => \&next_task,
3521         task_result => \&handle_task_result,
3522         task_done   => \&handle_task_done,
3523         task_debug  => \&handle_task_debug,
3524         watch_for_next_tasks => \&watch_for_next_tasks,
3525         watch_for_new_messages => \&watch_for_new_messages,
3526         watch_for_delivery_messages => \&watch_for_delivery_messages,
3527         watch_for_done_messages => \&watch_for_done_messages,
3528                 watch_for_new_jobs => \&watch_for_new_jobs,
3529         watch_for_modified_jobs => \&watch_for_modified_jobs,
3530         watch_for_done_jobs => \&watch_for_done_jobs,
3531         watch_for_opsi_jobs => \&watch_for_opsi_jobs,
3532         watch_for_old_known_clients => \&watch_for_old_known_clients,
3533         create_packages_list_db => \&run_create_packages_list_db,
3534         create_fai_server_db => \&run_create_fai_server_db,
3535         create_fai_release_db => \&run_create_fai_release_db,
3536                 recreate_packages_db => \&run_recreate_packages_db,
3537         session_run_result => \&session_run_result,
3538         session_run_debug => \&session_run_debug,
3539         session_run_done => \&session_run_done,
3540         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3541         }
3542 );
3545 POE::Kernel->run();
3546 exit;