Code

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