Code

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