Code

ceb9572e283e4fb4918177d9fb4e317580792e3c
[gosa.git] / gosa-si / gosa-si-server
1 #!/usr/bin/perl
2 #===============================================================================
3 #
4 #         FILE:  gosa-sd
5 #
6 #        USAGE:  ./gosa-sd
7 #
8 #  DESCRIPTION:
9 #
10 #      OPTIONS:  ---
11 # REQUIREMENTS:  libconfig-inifiles-perl libcrypt-rijndael-perl libxml-simple-perl 
12 #                libdata-dumper-simple-perl libdbd-sqlite3-perl libnet-ldap-perl
13 #                libpoe-perl
14 #         BUGS:  ---
15 #        NOTES:
16 #       AUTHOR:   (Andreas Rettenberger), <rettenberger@gonicus.de>
17 #      COMPANY:
18 #      VERSION:  1.0
19 #      CREATED:  12.09.2007 08:54:41 CEST
20 #     REVISION:  ---
21 #===============================================================================
23 my $server_version = '$HeadURL: https://oss.gonicus.de/repositories/gosa/trunk/gosa-si/gosa-si-server $:$Rev$';
25 use strict;
26 use warnings;
27 use Getopt::Long;
28 use Config::IniFiles;
29 use POSIX;
31 use Fcntl qw/:flock/;
32 use IO::Socket::INET;
33 use IO::Handle;
34 use IO::Select;
35 use Symbol qw(qualify_to_ref);
36 use Crypt::Rijndael;
37 use MIME::Base64;
38 use Digest::MD5  qw(md5 md5_hex md5_base64);
39 use XML::Simple;
40 use Data::Dumper;
41 use Sys::Syslog qw( :DEFAULT setlogsock);
42 use Time::HiRes qw( usleep);
43 use Cwd;
44 use File::Spec;
45 use File::Basename;
46 use File::Find;
47 use File::Copy;
48 use File::Path;
49 use GOSA::GosaSupportDaemon;
50 use POE qw(Component::Server::TCP Wheel::Run Filter::Reference);
51 use Net::LDAP;
52 use Net::LDAP::Util qw(:escape);
54 # revision number of server and program name
55 my $server_headURL;
56 my $server_revision;
57 my $server_status;
58 our $prg= basename($0);
59 our $verbose= 0;
61 my $db_module = "DBsqlite";
62 {
63 no strict "refs";
64 require ("GOSA/".$db_module.".pm");
65 ("GOSA/".$db_module)->import;
66 daemon_log("0 INFO: importing database module '$db_module'", 1);
67 }
69 my $modules_path = "/usr/lib/gosa-si/modules";
70 use lib "/usr/lib/gosa-si/modules";
72 our $global_kernel;
73 my ($foreground, $ping_timeout);
74 my ($server);
75 my ($gosa_server, $job_queue_timeout, $job_queue_loop_delay);
76 my ($messaging_db_loop_delay);
77 my ($procid, $pid);
78 my $arp_fifo;
79 my ($xml);
80 my $sources_list;
81 my $max_clients;
82 my %repo_files=();
83 my $repo_path;
84 my %repo_dirs=();
86 # Variables declared in config file are always set to 'our'
87 our (%cfg_defaults, $log_file, $pid_file, 
88     $server_ip, $server_port, $ClientPackages_key, $dns_lookup,
89     $arp_activ, $gosa_unit_tag,
90     $GosaPackages_key, $gosa_timeout,
91     $foreign_server_string, $server_domain, $ServerPackages_key, $foreign_servers_register_delay,
92     $wake_on_lan_passwd, $job_synchronization, $modified_jobs_loop_delay,
93     $arp_enabled, $arp_interface,
94     $opsi_enabled, $opsi_server, $opsi_admin, $opsi_password,
95                 $new_systems_ou,
96 );
98 # additional variable which should be globaly accessable
99 our $server_address;
100 our $server_mac_address;
101 our $gosa_address;
102 our $no_arp;
103 our $forground;
104 our $cfg_file;
105 our ($ldap_uri, $ldap_base, $ldap_admin_dn, $ldap_admin_password, $ldap_server_dn, $ldap_version);
106 our ($mysql_username, $mysql_password, $mysql_database, $mysql_host);
107 our $known_modules;
108 our $root_uid;
109 our $adm_gid;
111 # if foreground is not null, script will be not forked to background
112 $foreground = 0 ;
114 # specifies the timeout seconds while checking the online status of a registrating client
115 $ping_timeout = 5;
117 $no_arp = 0;
118 my $packages_list_under_construction = "/tmp/packages_list_creation_in_progress";
119 my @packages_list_statements;
120 my $watch_for_new_jobs_in_progress = 0;
122 # holds all incoming decrypted messages
123 our $incoming_db;
124 our $incoming_tn = 'incoming';
125 my $incoming_file_name;
126 my @incoming_col_names = ("id INTEGER PRIMARY KEY",
127         "timestamp VARCHAR(14) DEFAULT 'none'", 
128         "headertag VARCHAR(255) DEFAULT 'none'",
129         "targettag VARCHAR(255) DEFAULT 'none'",
130         "xmlmessage TEXT",
131         "module VARCHAR(255) DEFAULT 'none'",
132         "sessionid VARCHAR(255) DEFAULT '0'",
133 );
135 # holds all gosa jobs
136 our $job_db;
137 our $job_queue_tn = 'jobs';
138 my $job_queue_file_name;
139 my @job_queue_col_names = ("id INTEGER PRIMARY KEY",
140         "timestamp VARCHAR(14) DEFAULT 'none'", 
141         "status VARCHAR(255) DEFAULT 'none'", 
142         "result TEXT",
143         "progress VARCHAR(255) DEFAULT 'none'",
144         "headertag VARCHAR(255) DEFAULT 'none'",
145         "targettag VARCHAR(255) DEFAULT 'none'", 
146         "xmlmessage TEXT", 
147         "macaddress VARCHAR(17) DEFAULT 'none'",
148         "plainname VARCHAR(255) DEFAULT 'none'",
149         "siserver VARCHAR(255) DEFAULT 'none'",
150         "modified INTEGER DEFAULT '0'",
151 );
153 # holds all other gosa-si-server
154 our $known_server_db;
155 our $known_server_tn = "known_server";
156 my $known_server_file_name;
157 my @known_server_col_names = ("hostname VARCHAR(255)", "macaddress VARCHAR(17)", "status VARCHAR(255)", "hostkey VARCHAR(255)", "loaded_modules TEXT", "timestamp VARCHAR(14)");
159 # holds all registrated clients
160 our $known_clients_db;
161 our $known_clients_tn = "known_clients";
162 my $known_clients_file_name;
163 my @known_clients_col_names = ("hostname VARCHAR(255)", "status VARCHAR(255)", "hostkey VARCHAR(255)", "timestamp VARCHAR(14)", "macaddress VARCHAR(17)", "events TEXT", "keylifetime VARCHAR(255)");
165 # holds all registered clients at a foreign server
166 our $foreign_clients_db;
167 our $foreign_clients_tn = "foreign_clients"; 
168 my $foreign_clients_file_name;
169 my @foreign_clients_col_names = ("hostname VARCHAR(255)", "macaddress VARCHAR(17)", "regserver VARCHAR(255)", "timestamp VARCHAR(14)");
171 # holds all logged in user at each client 
172 our $login_users_db;
173 our $login_users_tn = "login_users";
174 my $login_users_file_name;
175 my @login_users_col_names = ("client VARCHAR(255)", "user VARCHAR(255)", "timestamp VARCHAR(14)", "regserver VARCHAR(255) DEFAULT 'localhost'");
177 # holds all fai server, the debian release and tag
178 our $fai_server_db;
179 our $fai_server_tn = "fai_server"; 
180 my $fai_server_file_name;
181 our @fai_server_col_names = ("timestamp VARCHAR(14)", "server VARCHAR(255)", "fai_release VARCHAR(255)", "sections VARCHAR(255)", "tag VARCHAR(255)"); 
183 our $fai_release_db;
184 our $fai_release_tn = "fai_release"; 
185 my $fai_release_file_name;
186 our @fai_release_col_names = ("timestamp VARCHAR(14)", "fai_release VARCHAR(255)", "class VARCHAR(255)", "type VARCHAR(255)", "state VARCHAR(255)"); 
188 # holds all packages available from different repositories
189 our $packages_list_db;
190 our $packages_list_tn = "packages_list";
191 my $packages_list_file_name;
192 our @packages_list_col_names = ("distribution VARCHAR(255)", "package VARCHAR(255)", "version VARCHAR(255)", "section VARCHAR(255)", "description TEXT", "template LONGBLOB", "timestamp VARCHAR(14)");
193 my $outdir = "/tmp/packages_list_db";
194 my $arch = "i386"; 
196 # holds all messages which should be delivered to a user
197 our $messaging_db;
198 our $messaging_tn = "messaging"; 
199 our @messaging_col_names = ("id INTEGER", "subject TEXT", "message_from VARCHAR(255)", "message_to VARCHAR(255)", 
200         "flag VARCHAR(255)", "direction VARCHAR(255)", "delivery_time VARCHAR(255)", "message TEXT", "timestamp VARCHAR(14)" );
201 my $messaging_file_name;
203 # path to directory to store client install log files
204 our $client_fai_log_dir = "/var/log/fai"; 
206 # queue which stores taskes until one of the $max_children children are ready to process the task
207 #my @tasks = qw();
208 my @msgs_to_decrypt = qw();
209 my $max_children = 2;
212 # loop delay for job queue to look for opsi jobs
213 my $job_queue_opsi_delay = 10;
214 our $opsi_client;
215 our $opsi_url;
216  
217 # Lifetime of logged in user information. If no update information comes after n seconds, 
218 # the user is expeceted to be no longer logged in or the host is no longer running. Because
219 # of this, the user is deleted from login_users_db
220 our $logged_in_user_date_of_expiry = 600;
223 %cfg_defaults = (
224 "general" => {
225     "log-file" => [\$log_file, "/var/run/".$prg.".log"],
226     "pid-file" => [\$pid_file, "/var/run/".$prg.".pid"],
227     },
228 "server" => {
229     "ip"                    => [\$server_ip, "0.0.0.0"],
230     "port"                  => [\$server_port, "20081"],
231     "known-clients"         => [\$known_clients_file_name, '/var/lib/gosa-si/clients.db' ],
232     "known-servers"         => [\$known_server_file_name, '/var/lib/gosa-si/servers.db'],
233     "incoming"              => [\$incoming_file_name, '/var/lib/gosa-si/incoming.db'],
234     "login-users"           => [\$login_users_file_name, '/var/lib/gosa-si/users.db'],
235     "fai-server"            => [\$fai_server_file_name, '/var/lib/gosa-si/fai_server.db'],
236     "fai-release"           => [\$fai_release_file_name, '/var/lib/gosa-si/fai_release.db'],
237     "packages-list"         => [\$packages_list_file_name, '/var/lib/gosa-si/packages.db'],
238     "messaging"             => [\$messaging_file_name, '/var/lib/gosa-si/messaging.db'],
239     "foreign-clients"       => [\$foreign_clients_file_name, '/var/lib/gosa-si/foreign_clients.db'],
240     "source-list"           => [\$sources_list, '/etc/apt/sources.list'],
241     "repo-path"             => [\$repo_path, '/srv/www/repository'],
242     "ldap-uri"              => [\$ldap_uri, ""],
243     "ldap-base"             => [\$ldap_base, ""],
244     "ldap-admin-dn"         => [\$ldap_admin_dn, ""],
245     "ldap-admin-password"   => [\$ldap_admin_password, ""],
246         "ldap-version"                  => [\$ldap_version, 3],
247         "max-ldap-handle"               => [\$max_ldap_handle, 10],
248         "precreate-ldap-handle" => [\$precreate_ldap_handle, 5],
249     "gosa-unit-tag"         => [\$gosa_unit_tag, ""],
250     "max-clients"           => [\$max_clients, 10],
251     "wol-password"          => [\$wake_on_lan_passwd, ""],
252         "mysql-username"        => [\$mysql_username, "gosa_si"],
253         "mysql-password"        => [\$mysql_password, ""],
254         "mysql-database"        => [\$mysql_database, "gosa_si"],
255         "mysql-host"            => [\$mysql_host, "127.0.0.1"],
256     },
257 "GOsaPackages" => {
258     "job-queue" => [\$job_queue_file_name, '/var/lib/gosa-si/jobs.db'],
259     "job-queue-loop-delay" => [\$job_queue_loop_delay, 3],
260     "messaging-db-loop-delay" => [\$messaging_db_loop_delay, 3],
261     "key" => [\$GosaPackages_key, "none"],
262                 "new-systems-ou" => [\$new_systems_ou, 'ou=workstations,ou=systems'],
263     },
264 "ClientPackages" => {
265     "key" => [\$ClientPackages_key, "none"],
266     "user-date-of-expiry" => [\$logged_in_user_date_of_expiry, 600],
267     },
268 "ServerPackages"=> {
269     "address"      => [\$foreign_server_string, ""],
270     "dns-lookup"            => [\$dns_lookup, "true"],
271     "domain"  => [\$server_domain, ""],
272     "key"     => [\$ServerPackages_key, "none"],
273     "key-lifetime" => [\$foreign_servers_register_delay, 120],
274     "job-synchronization-enabled" => [\$job_synchronization, "true"],
275     "synchronization-loop" => [\$modified_jobs_loop_delay, 5],
276     },
277 "ArpHandler" => {
278     "enabled"   => [\$arp_enabled, "true"],
279     "interface" => [\$arp_interface, "all"],
280         },
281 "Opsi" => {
282     "enabled"  => [\$opsi_enabled, "false"], 
283     "server"   => [\$opsi_server, "localhost"],
284     "admin"    => [\$opsi_admin, "opsi-admin"],
285     "password" => [\$opsi_password, "secret"],
286    },
288 );
291 #===  FUNCTION  ================================================================
292 #         NAME:  usage
293 #   PARAMETERS:  nothing
294 #      RETURNS:  nothing
295 #  DESCRIPTION:  print out usage text to STDERR
296 #===============================================================================
297 sub usage {
298     print STDERR << "EOF" ;
299 usage: $prg [-hvf] [-c config]
301            -h        : this (help) message
302            -c <file> : config file
303            -f        : foreground, process will not be forked to background
304            -v        : be verbose (multiple to increase verbosity)
305            -no-arp   : starts $prg without connection to arp module
306  
307 EOF
308     print "\n" ;
312 #===  FUNCTION  ================================================================
313 #         NAME:  logging
314 #   PARAMETERS:  level - string - default 'info'
315 #                msg - string -
316 #                facility - string - default 'LOG_DAEMON'
317 #      RETURNS:  nothing
318 #  DESCRIPTION:  function for logging
319 #===============================================================================
320 sub daemon_log {
321     # log into log_file
322     my( $msg, $level ) = @_;
323     if(not defined $msg) { return }
324     if(not defined $level) { $level = 1 }
325                 if($level > $verbose) { return }
326     if(defined $log_file){
327         my $open_log_fh = sysopen(LOG_HANDLE, $log_file, O_WRONLY | O_CREAT | O_APPEND , 0440);
328         if(not $open_log_fh) {
329             print STDERR "cannot open $log_file: $!";
330             return;
331         }
332         # check owner and group of log_file and update settings if necessary
333         my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = stat($log_file);
334         if((not $uid eq $root_uid) || (not $gid eq $adm_gid)) {
335             chown($root_uid, $adm_gid, $log_file);
336                 }
338         chomp($msg);
339         #$msg =~s/\n//g;   # no newlines are allowed in log messages, this is important for later log parsing
340         if($level <= $verbose){
341             my ($seconds, $minutes, $hours, $monthday, $month,
342                     $year, $weekday, $yearday, $sommertime) = localtime(time);
343             $hours = $hours < 10 ? $hours = "0".$hours : $hours;
344             $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
345             $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
346             my @monthnames = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
347             $month = $monthnames[$month];
348             $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
349             $year+=1900;
350             my $name = $prg;
352             my $log_msg = "$month $monthday $hours:$minutes:$seconds $name $msg\n";
353                         flock(LOG_HANDLE, LOCK_EX);
354                         seek(LOG_HANDLE, 0, 2);
355             print LOG_HANDLE $log_msg;
356                         flock(LOG_HANDLE, LOCK_UN);
357             if( $foreground ) { 
358                 print STDERR $log_msg;
359             }
360         }
361         close( LOG_HANDLE );
362     }
366 #===  FUNCTION  ================================================================
367 #         NAME:  check_cmdline_param
368 #   PARAMETERS:  nothing
369 #      RETURNS:  nothing
370 #  DESCRIPTION:  validates commandline parameter
371 #===============================================================================
372 sub check_cmdline_param () {
373     my $err_config;
374     my $err_counter = 0;
375         if(not defined($cfg_file)) {
376                 $cfg_file = "/etc/gosa-si/server.conf";
377                 if(! -r $cfg_file) {
378                         $err_config = "please specify a config file";
379                         $err_counter += 1;
380                 }
381     }
382     if( $err_counter > 0 ) {
383         &usage( "", 1 );
384         if( defined( $err_config)) { print STDERR "$err_config\n"}
385         print STDERR "\n";
386         exit( -1 );
387     }
391 #===  FUNCTION  ================================================================
392 #         NAME:  check_pid
393 #   PARAMETERS:  nothing
394 #      RETURNS:  nothing
395 #  DESCRIPTION:  handels pid processing
396 #===============================================================================
397 sub check_pid {
398     $pid = -1;
399     # Check, if we are already running
400     if( open(LOCK_FILE, "<$pid_file") ) {
401         $pid = <LOCK_FILE>;
402         if( defined $pid ) {
403             chomp( $pid );
404             if( -f "/proc/$pid/stat" ) {
405                 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
406                 if( $stat ) {
407                                         print STDERR "\nERROR: Already running!\n";
408                     close( LOCK_FILE );
409                     exit -1;
410                 }
411             }
412         }
413         close( LOCK_FILE );
414         unlink( $pid_file );
415     }
417     # create a syslog msg if it is not to possible to open PID file
418     if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
419         my($msg) = "Couldn't obtain lockfile '$pid_file' ";
420         if (open(LOCK_FILE, '<', $pid_file)
421                 && ($pid = <LOCK_FILE>))
422         {
423             chomp($pid);
424             $msg .= "(PID $pid)\n";
425         } else {
426             $msg .= "(unable to read PID)\n";
427         }
428         if( ! ($foreground) ) {
429             openlog( $0, "cons,pid", "daemon" );
430             syslog( "warning", $msg );
431             closelog();
432         }
433         else {
434             print( STDERR " $msg " );
435         }
436         exit( -1 );
437     }
440 #===  FUNCTION  ================================================================
441 #         NAME:  import_modules
442 #   PARAMETERS:  module_path - string - abs. path to the directory the modules 
443 #                are stored
444 #      RETURNS:  nothing
445 #  DESCRIPTION:  each file in module_path which ends with '.pm' and activation 
446 #                state is on is imported by "require 'file';"
447 #===============================================================================
448 sub import_modules {
449     if (not -e $modules_path) {
450         daemon_log("0 ERROR: cannot find directory or directory is not readable: $modules_path", 1);   
451     }
453     opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
455     while (defined (my $file = readdir (DIR))) {
456         if (not $file =~ /(\S*?).pm$/) {
457             next;
458         }
459                 my $mod_name = $1;
461         # ArpHandler switch
462         if( $file =~ /ArpHandler.pm/ ) {
463             if( $arp_enabled eq "false" ) { next; }
464         }
465         
466         eval { require $file; };
467         if ($@) {
468             daemon_log("0 ERROR: gosa-si-server could not load module $file", 1);
469             daemon_log("$@", 1);
470             exit;
471                 } else {
472                         my $info = eval($mod_name.'::get_module_info()');
473                         # Only load module if get_module_info() returns a non-null object
474                         if( $info ) {
475                                 my ($input_address, $input_key, $event_hash) = @{$info};
476                                 $known_modules->{$mod_name} = $info;
477                                 daemon_log("0 INFO: module $mod_name loaded", 5);
478                         }
479                 }
480     }   
481     close (DIR);
484 #===  FUNCTION  ================================================================
485 #         NAME:  password_check
486 #   PARAMETERS:  nothing
487 #      RETURNS:  nothing
488 #  DESCRIPTION:  escalates an critical error if two modules exist which are avaialable by 
489 #                the same password
490 #===============================================================================
491 sub password_check {
492     my $passwd_hash = {};
493     while (my ($mod_name, $mod_info) = each %$known_modules) {
494         my $mod_passwd = @$mod_info[1];
495         if (not defined $mod_passwd) { next; }
496         if (not exists $passwd_hash->{$mod_passwd}) {
497             $passwd_hash->{$mod_passwd} = $mod_name;
499         # escalates critical error
500         } else {
501             &daemon_log("0 ERROR: two loaded modules do have the same password. Please modify the 'key'-parameter in config file");
502             &daemon_log("0 ERROR: module='$mod_name' and module='".$passwd_hash->{$mod_passwd}."'");
503             exit( -1 );
504         }
505     }
510 #===  FUNCTION  ================================================================
511 #         NAME:  sig_int_handler
512 #   PARAMETERS:  signal - string - signal arose from system
513 #      RETURNS:  nothing
514 #  DESCRIPTION:  handels tasks to be done befor signal becomes active
515 #===============================================================================
516 sub sig_int_handler {
517     my ($signal) = @_;
519 #       if (defined($ldap_handle)) {
520 #               $ldap_handle->disconnect;
521 #       }
522     # TODO alle verbliebenden ldap verbindungen aus allen heaps beenden
523     
525     daemon_log("shutting down gosa-si-server", 1);
526     system("kill `ps -C gosa-si-server -o pid=`");
528 $SIG{INT} = \&sig_int_handler;
531 sub check_key_and_xml_validity {
532     my ($crypted_msg, $module_key, $session_id) = @_;
533     my $msg;
534     my $msg_hash;
535     my $error_string;
536     eval{
537         $msg = &decrypt_msg($crypted_msg, $module_key);
539         if ($msg =~ /<xml>/i){
540             $msg =~ s/\s+/ /g;  # just for better daemon_log
541             daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 9);
542             $msg_hash = $xml->XMLin($msg, ForceArray=>1);
544             ##############
545             # check header
546             if( not exists $msg_hash->{'header'} ) { die "no header specified"; }
547             my $header_l = $msg_hash->{'header'};
548             if( (1 > @{$header_l}) || ( ( 'HASH' eq ref @{$header_l}[0]) && (1 > keys %{@{$header_l}[0]}) ) ) { die 'empty header tag'; }
549             if( 1 < @{$header_l} ) { die 'more than one header specified'; }
550             my $header = @{$header_l}[0];
551             if( 0 == length $header) { die 'empty string in header tag'; }
553             ##############
554             # check source
555             if( not exists $msg_hash->{'source'} ) { die "no source specified"; }
556             my $source_l = $msg_hash->{'source'};
557             if( (1 > @{$source_l}) || ( ( 'HASH' eq ref @{$source_l}[0]) && (1 > keys %{@{$source_l}[0]}) ) ) { die 'empty source tag'; }
558             if( 1 < @{$source_l} ) { die 'more than one source specified'; }
559             my $source = @{$source_l}[0];
560             if( 0 == length $source) { die 'source error'; }
562             ##############
563             # check target
564             if( not exists $msg_hash->{'target'} ) { die "no target specified"; }
565             my $target_l = $msg_hash->{'target'};
566             if( (1 > @{$target_l}) || ( ('HASH' eq ref @{$target_l}[0]) && (1 > keys %{@{$target_l}[0]}) ) ) { die 'empty target tag'; }
567         }
568     };
569     if($@) {
570         daemon_log("$session_id ERROR: do not understand the message: $@", 1);
571         $msg = undef;
572         $msg_hash = undef;
573     }
575     return ($msg, $msg_hash);
579 sub check_outgoing_xml_validity {
580     my ($msg, $session_id) = @_;
582     my $msg_hash;
583     eval{
584         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
586         ##############
587         # check header
588         my $header_l = $msg_hash->{'header'};
589         if( 1 != @{$header_l} ) {
590             die 'no or more than one headers specified';
591         }
592         my $header = @{$header_l}[0];
593         if( 0 == length $header) {
594             die 'header has length 0';
595         }
597         ##############
598         # check source
599         my $source_l = $msg_hash->{'source'};
600         if( 1 != @{$source_l} ) {
601             die 'no or more than 1 sources specified';
602         }
603         my $source = @{$source_l}[0];
604         if( 0 == length $source) {
605             die 'source has length 0';
606         }
608                                 # Check if source contains hostname instead of ip address
609                                 if($source =~ /^[a-z][a-z0-9\.]+:\d+$/i) {
610                                                 my ($hostname,$port) = split(/:/, $source);
611                                                 my $ip_address = inet_ntoa(scalar gethostbyname($hostname));
612                                                 if(defined($ip_address) && $ip_address =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/ && $port =~ /^\d+$/) {
613                                                         # Write ip address to $source variable
614                                                         $source = "$ip_address:$port";
615                                                 }
616                                 }
617         unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
618                 $source =~ /^GOSA$/i) {
619             die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
620         }
621         
622         ##############
623         # check target  
624         my $target_l = $msg_hash->{'target'};
625         if( 0 == @{$target_l} ) {
626             die "no targets specified";
627         }
628         foreach my $target (@$target_l) {
629             if( 0 == length $target) {
630                 die "target has length 0";
631             }
632             unless( $target =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
633                     $target =~ /^GOSA$/i ||
634                     $target =~ /^\*$/ ||
635                     $target =~ /KNOWN_SERVER/i ||
636                     $target =~ /JOBDB/i ||
637                     $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 ){
638                 die "target '$target' is not a complete ip-address with port or a valid target name or a mac-address";
639             }
640         }
641     };
642     if($@) {
643         daemon_log("$session_id ERROR: outgoing msg is not gosa-si envelope conform: $@", 1);
644         daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 1);
645         $msg_hash = undef;
646     }
648     return ($msg_hash);
652 sub input_from_known_server {
653     my ($input, $remote_ip, $session_id) = @_ ;  
654     my ($msg, $msg_hash, $module);
656     my $sql_statement= "SELECT * FROM known_server";
657     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
659     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
660         my $host_name = $hit->{hostname};
661         if( not $host_name =~ "^$remote_ip") {
662             next;
663         }
664         my $host_key = $hit->{hostkey};
665         daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 7);
666         daemon_log("$session_id DEBUG: input_from_known_server: host_key: $host_key", 7);
668         # check if module can open msg envelope with module key
669         my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
670         if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
671             daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 7);
672             daemon_log("$@", 8);
673             next;
674         }
675         else {
676             $msg = $tmp_msg;
677             $msg_hash = $tmp_msg_hash;
678             $module = "ServerPackages";
679             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 7);
680             last;
681         }
682     }
684     if( (!$msg) || (!$msg_hash) || (!$module) ) {
685         daemon_log("$session_id DEBUG: Incoming message is not from a known server", 7);
686     }
687   
688     return ($msg, $msg_hash, $module);
692 sub input_from_known_client {
693     my ($input, $remote_ip, $session_id) = @_ ;  
694     my ($msg, $msg_hash, $module);
696     my $sql_statement= "SELECT * FROM known_clients";
697     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
698     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
699         my $host_name = $hit->{hostname};
700         if( not $host_name =~ /^$remote_ip:\d*$/) {
701                 next;
702                 }
703         my $host_key = $hit->{hostkey};
704         &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 7);
705         &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 7);
707         # check if module can open msg envelope with module key
708         ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
710         if( (!$msg) || (!$msg_hash) ) {
711             &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 7);
712             &daemon_log("$@", 8);
713             next;
714         }
715         else {
716             $module = "ClientPackages";
717             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 7);
718             last;
719         }
720     }
722     if( (!$msg) || (!$msg_hash) || (!$module) ) {
723         &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 7);
724     }
726     return ($msg, $msg_hash, $module);
730 sub input_from_unknown_host {
731         no strict "refs";
732         my ($input, $session_id) = @_ ;
733         my ($msg, $msg_hash, $module);
734         my $error_string;
736         my %act_modules = %$known_modules;
738         while( my ($mod, $info) = each(%act_modules)) {
740                 # check a key exists for this module
741                 my $module_key = ${$mod."_key"};
742                 if( not defined $module_key ) {
743                         if( $mod eq 'ArpHandler' ) {
744                                 next;
745                         }
746                         daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
747                         next;
748                 }
749                 daemon_log("$session_id DEBUG: $mod: $module_key", 7);
751                 # check if module can open msg envelope with module key
752                 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
753                 if( (not defined $msg) || (not defined $msg_hash) ) {
754                         next;
755                 } else {
756                         $module = $mod;
757             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 7);
758                         last;
759                 }
760         }
762         if( (!$msg) || (!$msg_hash) || (!$module)) {
763                 daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 7);
764         }
766         return ($msg, $msg_hash, $module);
770 sub create_ciphering {
771     my ($passwd) = @_;
772         if((!defined($passwd)) || length($passwd)==0) {
773                 $passwd = "";
774         }
775     $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
776     my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
777     my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
778     $my_cipher->set_iv($iv);
779     return $my_cipher;
783 sub encrypt_msg {
784     my ($msg, $key) = @_;
785     my $my_cipher = &create_ciphering($key);
786     my $len;
787     {
788             use bytes;
789             $len= 16-length($msg)%16;
790     }
791     $msg = "\0"x($len).$msg;
792     $msg = $my_cipher->encrypt($msg);
793     chomp($msg = &encode_base64($msg));
794     # there are no newlines allowed inside msg
795     $msg=~ s/\n//g;
796     return $msg;
800 sub decrypt_msg {
802     my ($msg, $key) = @_ ;
803     $msg = &decode_base64($msg);
804     my $my_cipher = &create_ciphering($key);
805     $msg = $my_cipher->decrypt($msg); 
806     $msg =~ s/\0*//g;
807     return $msg;
811 sub get_encrypt_key {
812     my ($target) = @_ ;
813     my $encrypt_key;
814     my $error = 0;
816     # target can be in known_server
817     if( not defined $encrypt_key ) {
818         my $sql_statement= "SELECT * FROM known_server WHERE hostname='$target'";
819         my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
820         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
821             my $host_name = $hit->{hostname};
822             if( $host_name ne $target ) {
823                 next;
824             }
825             $encrypt_key = $hit->{hostkey};
826             last;
827         }
828     }
830     # target can be in known_client
831     if( not defined $encrypt_key ) {
832         my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$target'";
833         my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
834         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
835             my $host_name = $hit->{hostname};
836             if( $host_name ne $target ) {
837                 next;
838             }
839             $encrypt_key = $hit->{hostkey};
840             last;
841         }
842     }
844     return $encrypt_key;
848 #===  FUNCTION  ================================================================
849 #         NAME:  open_socket
850 #   PARAMETERS:  PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
851 #                [PeerPort] string necessary if port not appended by PeerAddr
852 #      RETURNS:  socket IO::Socket::INET
853 #  DESCRIPTION:  open a socket to PeerAddr
854 #===============================================================================
855 sub open_socket {
856     my ($PeerAddr, $PeerPort) = @_ ;
857     if(defined($PeerPort)){
858         $PeerAddr = $PeerAddr.":".$PeerPort;
859     }
860     my $socket;
861     $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
862             Porto => "tcp",
863             Type => SOCK_STREAM,
864             Timeout => 5,
865             );
866     if(not defined $socket) {
867         return;
868     }
869 #    &daemon_log("DEBUG: open_socket: $PeerAddr", 7);
870     return $socket;
874 sub send_msg_to_target {
875     my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
876     my $error = 0;
877     my $header;
878     my $timestamp = &get_time();
879     my $new_status;
880     my $act_status;
881     my ($sql_statement, $res);
882   
883     if( $msg_header ) {
884         $header = "'$msg_header'-";
885     } else {
886         $header = "";
887     }
889         # Patch the source ip
890         if($msg =~ /<source>0\.0\.0\.0:\d*?<\/source>/) {
891                 my $remote_ip = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
892                 $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$remote_ip:$2<\/source>/s;
893         }
895     # encrypt xml msg
896     my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
898     # opensocket
899     my $socket = &open_socket($address);
900     if( !$socket ) {
901         daemon_log("$session_id WARNING: cannot send ".$header."msg to $address , host not reachable", 3);
902         $error++;
903     }
904     
905     if( $error == 0 ) {
906         # send xml msg
907         print $socket $crypted_msg."\n";
909         daemon_log("$session_id INFO: send ".$header."msg to $address", 5);
910         daemon_log("$session_id DEBUG: message:\n$msg", 9);
911         
912     }
914     # close socket in any case
915     if( $socket ) {
916         close $socket;
917     }
919     if( $error > 0 ) { $new_status = "down"; }
920     else { $new_status = $msg_header; }
923     # known_clients
924     $sql_statement = "SELECT * FROM $known_clients_tn WHERE hostname='$address'";
925     $res = $known_clients_db->select_dbentry($sql_statement);
926     if( keys(%$res) == 1) {
927         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
928         if ($act_status eq "down" && $new_status eq "down") {
929             $sql_statement = "DELETE FROM known_clients WHERE hostname='$address'";
930             $res = $known_clients_db->del_dbentry($sql_statement);
931             daemon_log("$session_id WARNING: failed 2x to send msg to host '$address', delete host from known_clients", 3);
932         } else { 
933             $sql_statement = "UPDATE known_clients SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
934             $res = $known_clients_db->update_dbentry($sql_statement);
935             if($new_status eq "down"){
936                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
937             } else {
938                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
939             }
940         }
941     }
943     # known_server
944     $sql_statement = "SELECT * FROM $known_server_tn WHERE hostname='$address'";
945     $res = $known_server_db->select_dbentry($sql_statement);
946     if( keys(%$res) == 1) {
947         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
948         if ($act_status eq "down" && $new_status eq "down") {
949             $sql_statement = "DELETE FROM known_server WHERE hostname='$address'";
950             $res = $known_server_db->del_dbentry($sql_statement);
951             daemon_log("$session_id WARNING: failed 2x to send a message to host '$address', delete host from known_server", 3);
952         } 
953         else { 
954             $sql_statement = "UPDATE known_server SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
955             $res = $known_server_db->update_dbentry($sql_statement);
956             if($new_status eq "down"){
957                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
958             } else {
959                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
960             }
961         }
962     }
963     return $error; 
967 sub update_jobdb_status_for_send_msgs {
968     my ($session_id, $answer, $error) = @_;
969     &daemon_log("$session_id DEBUG: try to update job status", 7); 
970     if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
971         my $jobdb_id = $1;
972     
973         $answer =~ /<header>(.*)<\/header>/;
974         my $job_header = $1;
976         $answer =~ /<target>(.*)<\/target>/;
977         my $job_target = $1;
978             
979         # Sending msg failed
980         if( $error ) {
982             # Set jobs to done, jobs do not need to deliver their message in any case
983             if (($job_header eq "trigger_action_localboot")
984                     ||($job_header eq "trigger_action_lock")
985                     ||($job_header eq "trigger_action_halt") 
986                     ) {
987                 my $sql_statement = "UPDATE $job_queue_tn SET status='done' WHERE id=$jobdb_id";
988                 &daemon_log("$session_id DEBUG: $sql_statement", 7); 
989                 my $res = $job_db->update_dbentry($sql_statement);
990                 
991             # Reactivate jobs, jobs need to deliver their message
992             } elsif (($job_header eq "trigger_action_activate")
993                     ||($job_header eq "trigger_action_update")
994                     ||($job_header eq "trigger_action_reinstall") 
995                     ||($job_header eq "trigger_activate_new")
996                     ) {
997                 &reactivate_job_with_delay($session_id, $job_target, $job_header, 30 );
999             # For all other messages
1000             } else {
1001                 my $sql_statement = "UPDATE $job_queue_tn ".
1002                     "SET status='error', result='can not deliver msg, please consult log file' ".
1003                     "WHERE id=$jobdb_id";
1004                 &daemon_log("$session_id DEBUG: $sql_statement", 7); 
1005                 my $res = $job_db->update_dbentry($sql_statement);
1006             }
1008         # Sending msg was successful
1009         } else {
1010             # Set jobs localboot, lock, activate, halt, reboot and wake to done
1011             # jobs reinstall, update, inst_update do themself setting to done
1012             if (($job_header eq "trigger_action_localboot")
1013                     ||($job_header eq "trigger_action_lock")
1014                     ||($job_header eq "trigger_action_activate")
1015                     ||($job_header eq "trigger_action_halt") 
1016                     ||($job_header eq "trigger_action_reboot")
1017                     ||($job_header eq "trigger_action_wake")
1018                     ||($job_header eq "trigger_wake")
1019                     ) {
1021                 my $sql_statement = "UPDATE $job_queue_tn ".
1022                     "SET status='done' ".
1023                     "WHERE id=$jobdb_id AND status='processed'";
1024                 &daemon_log("$session_id DEBUG: $sql_statement", 7); 
1025                 my $res = $job_db->update_dbentry($sql_statement);
1026             } else { 
1027                 &daemon_log("$session_id DEBUG: sending message succeed but cannot update job status.", 7); 
1028             } 
1029         } 
1030     } else { 
1031         &daemon_log("$session_id DEBUG: cannot update job status, msg has no jobdb_id-tag: $answer", 7); 
1032     }
1035 sub reactivate_job_with_delay {
1036     my ($session_id, $target, $header, $delay) = @_ ;
1037     # Sometimes the client is still booting or does not wake up, in this case reactivate the job (if it exists) with a delay of n sec
1038     
1039     if (not defined $delay) { $delay = 30 } ;
1040     my $delay_timestamp = &calc_timestamp(&get_time(), "plus", $delay);
1042     my $sql = "UPDATE $job_queue_tn Set timestamp='$delay_timestamp', status='waiting' WHERE (macaddress LIKE 'target' AND headertag='$header')"; 
1043     my $res = $job_db->update_dbentry($sql);
1044     daemon_log("$session_id INFO: '$header'-job will be reactivated at '$delay_timestamp' ".
1045             "cause client '$target' is currently not available", 5);
1046     daemon_log("$session_id $sql", 7);                             
1047     return;
1051 sub sig_handler {
1052         my ($kernel, $signal) = @_[KERNEL, ARG0] ;
1053         daemon_log("0 INFO got signal '$signal'", 1); 
1054         $kernel->sig_handled();
1055         return;
1059 sub msg_to_decrypt {
1060         my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
1061         my $session_id = $session->ID;
1062         my ($msg, $msg_hash, $module);
1063         my $error = 0;
1065         # fetch new msg out of @msgs_to_decrypt
1066         my $tmp_next_msg = shift @msgs_to_decrypt;
1067     my ($next_msg, $msg_source) = split(/;/, $tmp_next_msg);
1069         # msg is from a new client or gosa
1070         ($msg, $msg_hash, $module) = &input_from_unknown_host($next_msg, $session_id);
1072         # msg is from a gosa-si-server
1073         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1074                 ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $heap->{'remote_ip'}, $session_id);
1075         }
1076         # msg is from a gosa-si-client
1077         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1078                 ($msg, $msg_hash, $module) = &input_from_known_client($next_msg, $heap->{'remote_ip'}, $session_id);
1079         }
1080         # an error occurred
1081         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1082                 # if an incoming msg could not be decrypted (maybe a wrong key), send client a ping. If the client
1083                 # could not understand a msg from its server the client cause a re-registering process
1084         my $remote_ip = $heap->{'remote_ip'};
1085         my $remote_port = $heap->{'remote_port'};
1086         my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source><target>$msg_source</target></xml>";
1087         my ($test_error, $test_error_string) = &send_msg_to_target($ping_msg, "$msg_source", "dummy-key", "gosa_ping", $session_id);
1089                 daemon_log("$session_id WARNING cannot understand incoming msg, send 'ping'-msg to all host with ip '".$heap->{remote_ip}.
1090                         "' to cause a re-registering of the client if necessary", 3);
1091                 $error++;
1092         }
1095         my $header;
1096         my $target;
1097         my $source;
1098         my $done = 0;
1099         my $sql;
1100         my $res;
1102         # check whether this message should be processed here
1103         if ($error == 0) {
1104                 $header = @{$msg_hash->{'header'}}[0];
1105                 $target = @{$msg_hash->{'target'}}[0];
1106                 $source = @{$msg_hash->{'source'}}[0];
1107                 my $not_found_in_known_clients_db = 0;
1108                 my $not_found_in_known_server_db = 0;
1109                 my $not_found_in_foreign_clients_db = 0;
1110                 my $local_address;
1111                 my $local_mac;
1112                 my ($target_ip, $target_port) = split(':', $target);
1114                 # Determine the local ip address if target is an ip address
1115                 if ($target =~ /^\d+\.\d+\.\d+\.\d+:\d+$/) {
1116                         $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1117                 } else {
1118                         $local_address = $server_address;
1119                 }
1121                 # Determine the local mac address if target is a mac address
1122                 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) {
1123                         my $loc_ip = &get_local_ip_for_remote_ip($heap->{'remote_ip'});
1124                         my $network_interface= &get_interface_for_ip($loc_ip);
1125                         $local_mac = &get_mac_for_interface($network_interface);
1126                 } else {
1127                         $local_mac = $server_mac_address;
1128                 }
1130                 # target and source is equal to GOSA -> process here
1131                 if (not $done) {
1132                         if ($target eq "GOSA" && $source eq "GOSA") {
1133                                 $done = 1;                    
1134                                 &daemon_log("$session_id DEBUG: target and source is 'GOSA' -> process here", 7);
1135                         }
1136                 }
1138                 # target is own address without forward_to_gosa-tag -> process here
1139                 if (not $done) {
1140                         #if ((($target eq $local_address) || ($target eq $local_mac) ) && (not exists $msg_hash->{'forward_to_gosa'})) {
1141                         if (($target eq $local_address) && (not exists $msg_hash->{'forward_to_gosa'})) {
1142                                 $done = 1;
1143                                 if ($source eq "GOSA") {
1144                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1145                                 }
1146                                 &daemon_log("$session_id DEBUG: target is own address without forward_to_gosa-tag -> process here", 7);
1147                         }
1148                 }
1150                 # target is a client address in known_clients -> process here
1151                 if (not $done) {
1152                         $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')"; 
1153                         $res = $known_clients_db->select_dbentry($sql);
1154                         if (keys(%$res) > 0) {
1155                                 $done = 1; 
1156                                 my $hostname = $res->{1}->{'hostname'};
1157                                 $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1158                                 my $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1159                                 if ($source eq "GOSA") {
1160                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1161                                 }
1162                                 &daemon_log("$session_id DEBUG: target is a client address in known_clients -> process here", 7);
1164                         } else {
1165                                 $not_found_in_known_clients_db = 1;
1166                         }
1167                 }
1169                 # target ist own address with forward_to_gosa-tag not pointing to myself -> process here
1170                 if (not $done) {
1171                         my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1172                         my $gosa_at;
1173                         my $gosa_session_id;
1174                         if (($target eq $local_address) && (defined $forward_to_gosa)){
1175                                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1176                                 if ($gosa_at ne $local_address) {
1177                                         $done = 1;
1178                                         &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag not pointing to myself -> process here", 7); 
1179                                 }
1180                         }
1181                 }
1183                 # if message should be processed here -> add message to incoming_db
1184                 if ($done) {
1185                         # if a job or a gosa message comes from a foreign server, fake module to GosaPackages
1186                         # so gosa-si-server knows how to process this kind of messages
1187                         if ($header =~ /^gosa_/ || $header =~ /^job_/) {
1188                                 $module = "GosaPackages";
1189                         }
1191                         my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1192                                         primkey=>[],
1193                                         headertag=>$header,
1194                                         targettag=>$target,
1195                                         xmlmessage=>&encode_base64($msg),
1196                                         timestamp=>&get_time,
1197                                         module=>$module,
1198                                         sessionid=>$session_id,
1199                                 } );
1201                 }
1203                 # target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa
1204                 if (not $done) {
1205                         my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1206                         my $gosa_at;
1207                         my $gosa_session_id;
1208                         if (($target eq $local_address) && (defined $forward_to_gosa)){
1209                                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1210                                 if ($gosa_at eq $local_address) {
1211                                         my $session_reference = $kernel->ID_id_to_session($gosa_session_id);
1212                                         if( defined $session_reference ) {
1213                                                 $heap = $session_reference->get_heap();
1214                                         }
1215                                         if(exists $heap->{'client'}) {
1216                                                 $msg = &encrypt_msg($msg, $GosaPackages_key);
1217                                                 $heap->{'client'}->put($msg);
1218                                                 &daemon_log("$session_id INFO: incoming '$header' message forwarded to GOsa", 5); 
1219                                         }
1220                                         $done = 1;
1221                                         &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa", 7);
1222                                 }
1223                         }
1225                 }
1227                 # target is a client address in foreign_clients -> forward to registration server
1228                 if (not $done) {
1229                         $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1230                         $res = $foreign_clients_db->select_dbentry($sql);
1231                         if (keys(%$res) > 0) {
1232                                 my $hostname = $res->{1}->{'hostname'};
1233                                 my ($host_ip, $host_port) = split(/:/, $hostname);
1234                                 my $local_address =  &get_local_ip_for_remote_ip($host_ip).":$server_port";
1235                                 my $regserver = $res->{1}->{'regserver'};
1236                                 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$regserver'"; 
1237                                 my $res = $known_server_db->select_dbentry($sql);
1238                                 if (keys(%$res) > 0) {
1239                                         my $regserver_key = $res->{1}->{'hostkey'};
1240                                         $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1241                                         $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1242                                         if ($source eq "GOSA") {
1243                                                 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1244                                         }
1245                                         my $error= &send_msg_to_target($msg, $regserver, $regserver_key, $header, $session_id);
1246                                         if ($error) {
1247                                                 &daemon_log("$session_id ERROR: some problems (error=$error) occurred while trying to send msg to registration server: $msg", 1); 
1248                                         }
1249                                 }
1250                                 $done = 1;
1251                                 &daemon_log("$session_id DEBUG: target is a client address in foreign_clients -> forward to registration server", 7);
1252                         } else {
1253                                 $not_found_in_foreign_clients_db = 1;
1254                         }
1255                 }
1257                 # target is a server address -> forward to server
1258                 if (not $done) {
1259                         $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1260                         $res = $known_server_db->select_dbentry($sql);
1261                         if (keys(%$res) > 0) {
1262                                 my $hostkey = $res->{1}->{'hostkey'};
1264                                 if ($source eq "GOSA") {
1265                                         $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1266                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1268                                 }
1270                                 &send_msg_to_target($msg, $target, $hostkey, $header, $session_id);
1271                                 $done = 1;
1272                                 &daemon_log("$session_id DEBUG: target is a server address -> forward to server", 7);
1273                         } else {
1274                                 $not_found_in_known_server_db = 1;
1275                         }
1276                 }
1279                 # target is not in foreign_clients_db, known_server_db or known_clients_db, maybe it is a complete new one -> process here
1280                 if ( $not_found_in_foreign_clients_db 
1281                         && $not_found_in_known_server_db
1282                         && $not_found_in_known_clients_db) {
1283                         &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);
1284             if ($header =~ /^gosa_/ || $header =~ /^job_/) { 
1285                 $module = "GosaPackages"; 
1286             }
1287                         my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1288                                         primkey=>[],
1289                                         headertag=>$header,
1290                                         targettag=>$target,
1291                                         xmlmessage=>&encode_base64($msg),
1292                                         timestamp=>&get_time,
1293                                         module=>$module,
1294                                         sessionid=>$session_id,
1295                                 } );
1296                         $done = 1;
1297                 }
1300                 if (not $done) {
1301                         daemon_log("$session_id ERROR: do not know what to do with this message: $msg", 1);
1302                         if ($source eq "GOSA") {
1303                                 my %data = ('error_msg' => &encode_base64($msg), 'error_string' => "Do not know what to do with this message!");
1304                                 my $error_msg = &build_msg("error", $local_address, "GOSA", \%data ); 
1306                                 my $session_reference = $kernel->ID_id_to_session($session_id);
1307                                 if( defined $session_reference ) {
1308                                         $heap = $session_reference->get_heap();
1309                                 }
1310                                 if(exists $heap->{'client'}) {
1311                                         $error_msg = &encrypt_msg($error_msg, $GosaPackages_key);
1312                                         $heap->{'client'}->put($error_msg);
1313                                 }
1314                         }
1315                 }
1317         }
1319         return;
1323 sub next_task {
1324     my ($session, $heap, $task) = @_[SESSION, HEAP, ARG0, ARG1];
1325     my $running_task = POE::Wheel::Run->new(
1326             Program => sub { process_task($session, $heap, $task) },
1327             StdioFilter => POE::Filter::Reference->new(),
1328             StdoutEvent  => "task_result",
1329             StderrEvent  => "task_debug",
1330             CloseEvent   => "task_done",
1331             );
1332     $heap->{task}->{ $running_task->ID } = $running_task;
1335 sub handle_task_result {
1336     my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
1337     my $client_answer = $result->{'answer'};
1338     if( $client_answer =~ s/session_id=(\d+)$// ) {
1339         my $session_id = $1;
1340         if( defined $session_id ) {
1341             my $session_reference = $kernel->ID_id_to_session($session_id);
1342             if( defined $session_reference ) {
1343                 $heap = $session_reference->get_heap();
1344             }
1345         }
1347         if(exists $heap->{'client'}) {
1348             $heap->{'client'}->put($client_answer);
1349         }
1350     }
1351     $kernel->sig(CHLD => "child_reap");
1354 sub handle_task_debug {
1355     my $result = $_[ARG0];
1356     print STDERR "$result\n";
1359 sub handle_task_done {
1360     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1361     delete $heap->{task}->{$task_id};
1362         if (exists $heap->{ldap_handle}->{$task_id}) {
1363                 &release_ldap_handle($heap->{ldap_handle}->{$task_id});
1364         }
1367 sub process_task {
1368     no strict "refs";
1369     #CHECK: Not @_[...]?
1370     my ($session, $heap, $task) = @_;
1371     my $error = 0;
1372     my $answer_l;
1373     my ($answer_header, @answer_target_l, $answer_source);
1374     my $client_answer = "";
1376     # prepare all variables needed to process message
1377     #my $msg = $task->{'xmlmessage'};
1378     my $msg = &decode_base64($task->{'xmlmessage'});
1379     my $incoming_id = $task->{'id'};
1380     my $module = $task->{'module'};
1381     my $header =  $task->{'headertag'};
1382     my $session_id = $task->{'sessionid'};
1383                 my $msg_hash;
1384                 eval {
1385         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
1386                 }; 
1387                 daemon_log("ERROR: XML failure '$@'") if ($@);
1388     my $source = @{$msg_hash->{'source'}}[0];
1389     
1390     # set timestamp of incoming client uptodate, so client will not 
1391     # be deleted from known_clients because of expiration
1392     my $cur_time = &get_time();
1393     my $sql = "UPDATE $known_clients_tn SET timestamp='$cur_time' WHERE hostname='$source'"; 
1394     my $res = $known_clients_db->exec_statement($sql);
1396     ######################
1397     # process incoming msg
1398     if( $error == 0) {
1399         daemon_log("$session_id INFO: Incoming msg (session_id=$session_id) with header '".@{$msg_hash->{'header'}}[0]."'", 5); 
1400         daemon_log("$session_id DEBUG: Processing module ".$module, 7);
1401         $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1403         if ( 0 < @{$answer_l} ) {
1404             my $answer_str = join("\n", @{$answer_l});
1405             while ($answer_str =~ /<header>(\w+)<\/header>/g) {
1406                 daemon_log("$session_id INFO: got answer message with header '$1'", 5);
1407             }
1408             daemon_log("$session_id DEBUG: $module: got answer from module: \n".$answer_str,9);
1409         } else {
1410             daemon_log("$session_id DEBUG: $module: got no answer from module!" ,7);
1411         }
1413     }
1414     if( !$answer_l ) { $error++ };
1416     ########
1417     # answer
1418     if( $error == 0 ) {
1420         foreach my $answer ( @{$answer_l} ) {
1421             # check outgoing msg to xml validity
1422             my $answer_hash = &check_outgoing_xml_validity($answer, $session_id);
1423             if( not defined $answer_hash ) { next; }
1424             
1425             $answer_header = @{$answer_hash->{'header'}}[0];
1426             @answer_target_l = @{$answer_hash->{'target'}};
1427             $answer_source = @{$answer_hash->{'source'}}[0];
1429             # deliver msg to all targets 
1430             foreach my $answer_target ( @answer_target_l ) {
1432                 # targets of msg are all gosa-si-clients in known_clients_db
1433                 if( $answer_target eq "*" ) {
1434                     # answer is for all clients
1435                     my $sql_statement= "SELECT * FROM known_clients";
1436                     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
1437                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1438                         my $host_name = $hit->{hostname};
1439                         my $host_key = $hit->{hostkey};
1440                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1441                         &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1442                     }
1443                 }
1445                 # targets of msg are all gosa-si-server in known_server_db
1446                 elsif( $answer_target eq "KNOWN_SERVER" ) {
1447                     # answer is for all server in known_server
1448                     my $sql_statement= "SELECT * FROM $known_server_tn";
1449                     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
1450                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1451                         my $host_name = $hit->{hostname};
1452                         my $host_key = $hit->{hostkey};
1453                         $answer =~ s/<target>\S+<\/target>/<target>$host_name<\/target>/g;
1454                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1455                         &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1456                     }
1457                 }
1459                 # target of msg is GOsa
1460                                 elsif( $answer_target eq "GOSA" ) {
1461                                         my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1462                                         my $add_on = "";
1463                     if( defined $session_id ) {
1464                         $add_on = ".session_id=$session_id";
1465                     }
1466                     # answer is for GOSA and has to returned to connected client
1467                     my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1468                     $client_answer = $gosa_answer.$add_on;
1469                 }
1471                 # target of msg is job queue at this host
1472                 elsif( $answer_target eq "JOBDB") {
1473                     $answer =~ /<header>(\S+)<\/header>/;   
1474                     my $header;
1475                     if( defined $1 ) { $header = $1; }
1476                     my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1477                     &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1478                 }
1480                 # Target of msg is a mac address
1481                 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 ) {
1482                     daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients and foreign_clients", 5);
1484                     # Looking for macaddress in known_clients
1485                     my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1486                     my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1487                     my $found_ip_flag = 0;
1488                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1489                         my $host_name = $hit->{hostname};
1490                         my $host_key = $hit->{hostkey};
1491                         $answer =~ s/$answer_target/$host_name/g;
1492                         daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1493                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1494                         &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1495                         $found_ip_flag++ ;
1496                     }   
1498                     # Looking for macaddress in foreign_clients
1499                     if ($found_ip_flag == 0) {
1500                         my $sql = "SELECT * FROM $foreign_clients_tn WHERE macaddress LIKE '$answer_target'";
1501                         my $res = $foreign_clients_db->select_dbentry($sql);
1502                         while( my ($hit_num, $hit) = each %{ $res } ) {
1503                             my $host_name = $hit->{hostname};
1504                             my $reg_server = $hit->{regserver};
1505                             daemon_log("$session_id INFO: found host '$host_name' with mac '$answer_target', registered at '$reg_server'", 5);
1506                             
1507                             # Fetch key for reg_server
1508                             my $reg_server_key;
1509                             my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$reg_server'";
1510                             my $res = $known_server_db->select_dbentry($sql);
1511                             if (exists $res->{1}) {
1512                                 $reg_server_key = $res->{1}->{'hostkey'}; 
1513                             } else {
1514                                 daemon_log("$session_id ERROR: cannot find hostkey for '$host_name' in '$known_server_tn'", 1); 
1515                                 daemon_log("$session_id ERROR: unable to forward answer to correct registration server, processing is aborted!", 1); 
1516                                 $reg_server_key = undef;
1517                             }
1519                             # Send answer to server where client is registered
1520                             if (defined $reg_server_key) {
1521                                 $answer =~ s/$answer_target/$host_name/g;
1522                                 my $error = &send_msg_to_target($answer, $reg_server, $reg_server_key, $answer_header, $session_id);
1523                                 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1524                                 $found_ip_flag++ ;
1525                             }
1526                         }
1527                     }
1529                     # No mac to ip matching found
1530                     if( $found_ip_flag == 0) {
1531                         daemon_log("$session_id WARNING: no host found in known_clients or foreign_clients with mac address '$answer_target'", 3);
1532                         &reactivate_job_with_delay($session_id, $answer_target, $answer_header, 30);
1533                     }
1535                 # Answer is for one specific host   
1536                 } else {
1537                     # get encrypt_key
1538                     my $encrypt_key = &get_encrypt_key($answer_target);
1539                     if( not defined $encrypt_key ) {
1540                         # unknown target
1541                         daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1542                         next;
1543                     }
1544                     my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1545                     &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1546                 }
1547             }
1548         }
1549     }
1551     my $filter = POE::Filter::Reference->new();
1552     my %result = ( 
1553             status => "seems ok to me",
1554             answer => $client_answer,
1555             );
1557     my $output = $filter->put( [ \%result ] );
1558     print @$output;
1563 sub session_start {
1564     my ($kernel) = $_[KERNEL];
1565     $global_kernel = $kernel;
1566     $kernel->yield('register_at_foreign_servers');
1567         $kernel->yield('create_fai_server_db', $fai_server_tn );
1568         $kernel->yield('create_fai_release_db', $fai_release_tn );
1569     $kernel->yield('watch_for_next_tasks');
1570         $kernel->sig(USR1 => "sig_handler");
1571         $kernel->sig(USR2 => "recreate_packages_db");
1572         $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1573         $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay); 
1574     $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay); 
1575         $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1576     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1577         $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1578     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1580     # Start opsi check
1581     if ($opsi_enabled eq "true") {
1582         $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay); 
1583     }
1588 sub watch_for_done_jobs {
1589         #CHECK: $heap for what?
1590         my ($kernel,$heap) = @_[KERNEL, HEAP];
1592         my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((status='done') AND (modified='0'))";
1593         my $res = $job_db->select_dbentry( $sql_statement );
1595         while( my ($id, $hit) = each %{$res} ) {
1596                 my $jobdb_id = $hit->{id};
1597                 my $sql_statement = "DELETE FROM $job_queue_tn WHERE id=$jobdb_id"; 
1598                 my $res = $job_db->del_dbentry($sql_statement); 
1599         }
1601         $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1605 sub watch_for_opsi_jobs {
1606     my ($kernel) = $_[KERNEL];
1608     # 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 
1609     # opsi install job is to parse the xml message. There is still the correct header.
1610     my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((xmlmessage LIKE '%opsi_install_client</header>%') AND (status='processing') AND (siserver='localhost'))";
1611         my $res = $job_db->select_dbentry( $sql_statement );
1613     # Ask OPSI for an update of the running jobs
1614     while (my ($id, $hit) = each %$res ) {
1615         # Determine current parameters of the job
1616         my $hostId = $hit->{'plainname'};
1617         my $macaddress = $hit->{'macaddress'};
1618         my $progress = $hit->{'progress'};
1620         my $result= {};
1621         
1622         # For hosts, only return the products that are or get installed
1623         my $callobj;
1624         $callobj = {
1625             method  => 'getProductStates_hash',
1626             params  => [ $hostId ],
1627             id  => 1,
1628         };
1629         
1630         my $hres = $opsi_client->call($opsi_url, $callobj);
1631         #my ($hres_err, $hres_err_string) = &check_opsi_res($hres);
1632         if (not &check_opsi_res($hres)) {
1633             my $htmp= $hres->result->{$hostId};
1634         
1635             # Check state != not_installed or action == setup -> load and add
1636             my $products= 0;
1637             my $installed= 0;
1638             my $installing = 0;
1639             my $error= 0;  
1640             my @installed_list;
1641             my @error_list;
1642             my $act_status = "none";
1643             foreach my $product (@{$htmp}){
1645                 if ($product->{'installationStatus'} ne "not_installed" or
1646                         $product->{'actionRequest'} eq "setup"){
1648                     # Increase number of products for this host
1649                     $products++;
1650         
1651                     if ($product->{'installationStatus'} eq "failed"){
1652                         $result->{$product->{'productId'}}= "error";
1653                         unshift(@error_list, $product->{'productId'});
1654                         $error++;
1655                     }
1656                     if ($product->{'installationStatus'} eq "installed" && $product->{'actionRequest'}  eq "none"){
1657                         $result->{$product->{'productId'}}= "installed";
1658                         unshift(@installed_list, $product->{'productId'});
1659                         $installed++;
1660                     }
1661                     if ($product->{'installationStatus'} eq "installing"){
1662                         $result->{$product->{'productId'}}= "installing";
1663                         $installing++;
1664                         $act_status = "installing - ".$product->{'productId'};
1665                     }
1666                 }
1667             }
1668         
1669             # Estimate "rough" progress, avoid division by zero
1670             if ($products == 0) {
1671                 $result->{'progress'}= 0;
1672             } else {
1673                 $result->{'progress'}= int($installed * 100 / $products);
1674             }
1676             # Set updates in job queue
1677             if ((not $error) && (not $installing) && ($installed)) {
1678                 $act_status = "installed - ".join(", ", @installed_list);
1679             }
1680             if ($error) {
1681                 $act_status = "error - ".join(", ", @error_list);
1682             }
1683             if ($progress ne $result->{'progress'} ) {
1684                 # Updating progress and result 
1685                 my $update_statement = "UPDATE $job_queue_tn SET modified='1', progress='".$result->{'progress'}."', result='$act_status' WHERE macaddress LIKE '$macaddress' AND siserver='localhost'";
1686                 my $update_res = $job_db->update_dbentry($update_statement);
1687             }
1688             if ($progress eq 100) { 
1689                 # Updateing status
1690                 my $done_statement = "UPDATE $job_queue_tn SET modified='1', ";
1691                 if ($error) {
1692                     $done_statement .= "status='error'";
1693                 } else {
1694                     $done_statement .= "status='done'";
1695                 }
1696                 $done_statement .= " WHERE macaddress LIKE '$macaddress' AND siserver='localhost'";
1697                 my $done_res = $job_db->update_dbentry($done_statement);
1698             }
1701         }
1702     }
1704     $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay);
1708 # If a job got an update or was modified anyway, send to all other si-server an update message of this jobs.
1709 sub watch_for_modified_jobs {
1710     my ($kernel,$heap) = @_[KERNEL, HEAP];
1712     my $sql_statement = "SELECT * FROM $job_queue_tn WHERE (modified='1')"; 
1713     my $res = $job_db->select_dbentry( $sql_statement );
1714     
1715     # if db contains no jobs which should be update, do nothing
1716     if (keys %$res != 0) {
1718         if ($job_synchronization  eq "true") {
1719             # make out of the db result a gosa-si message   
1720             my $update_msg = &db_res2si_msg ($res, "foreign_job_updates", "KNOWN_SERVER", "MY_LOCAL_ADDRESS");
1721  
1722             # update all other SI-server
1723             &inform_all_other_si_server($update_msg);
1724         }
1726         # set jobs all jobs to modified = 0, wait until the next modification for updates of other si-server
1727         $sql_statement = "UPDATE $job_queue_tn SET modified='0' ";
1728         $res = $job_db->update_dbentry($sql_statement);
1729     }
1731     $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay);
1735 sub watch_for_new_jobs {
1736         if($watch_for_new_jobs_in_progress == 0) {
1737                 $watch_for_new_jobs_in_progress = 1;
1738                 my ($kernel,$heap) = @_[KERNEL, HEAP];
1740                 # check gosa job queue for jobs with executable timestamp
1741                 my $timestamp = &get_time();
1742                 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE siserver='localhost' AND status='waiting' AND (CAST(timestamp AS UNSIGNED)) < $timestamp ORDER BY timestamp";
1743                 my $res = $job_db->exec_statement( $sql_statement );
1745                 # Merge all new jobs that would do the same actions
1746                 my @drops;
1747                 my $hits;
1748                 foreach my $hit (reverse @{$res} ) {
1749                         my $macaddress= lc @{$hit}[8];
1750                         my $headertag= @{$hit}[5];
1751                         if(
1752                                 defined($hits->{$macaddress}) &&
1753                                 defined($hits->{$macaddress}->{$headertag}) &&
1754                                 defined($hits->{$macaddress}->{$headertag}[0])
1755                         ) {
1756                                 push @drops, "DELETE FROM $job_queue_tn WHERE id = $hits->{$macaddress}->{$headertag}[0]";
1757                         }
1758                         $hits->{$macaddress}->{$headertag}= $hit;
1759                 }
1761                 # Delete new jobs with a matching job in state 'processing'
1762                 foreach my $macaddress (keys %{$hits}) {
1763                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1764                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1765                                 if(defined($jobdb_id)) {
1766                                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND headertag='$jobdb_headertag' AND status='processing'";
1767                                         my $res = $job_db->exec_statement( $sql_statement );
1768                                         foreach my $hit (@{$res}) {
1769                                                 push @drops, "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1770                                         }
1771                                 } else {
1772                                         daemon_log("J ERROR: Job without id exists for macaddress $macaddress!", 1);
1773                                 }
1774                         }
1775                 }
1777                 # Commit deletion
1778                 $job_db->exec_statementlist(\@drops);
1780                 # Look for new jobs that could be executed
1781                 foreach my $macaddress (keys %{$hits}) {
1783                         # Look if there is an executing job
1784                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='processing'";
1785                         my $res = $job_db->exec_statement( $sql_statement );
1787                         # Skip new jobs for host if there is a processing job
1788                         if(defined($res) and defined @{$res}[0]) {
1789                                 # Prevent race condition if there is a trigger_activate job waiting and a goto-activation job processing
1790                                 my $row = @{$res}[0] if (ref $res eq 'ARRAY');
1791                                 if(@{$row}[5] eq 'trigger_action_reinstall') {
1792                                         my $sql_statement_2 =  "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='waiting' AND headertag = 'trigger_activate_new'"; 
1793                                         my $res_2 = $job_db->exec_statement( $sql_statement_2 );
1794                                         if(defined($res_2) and defined @{$res_2}[0]) {
1795                                                 # Set status from goto-activation to 'waiting' and update timestamp
1796                                                 $job_db->exec_statement("UPDATE $job_queue_tn SET status='waiting' WHERE macaddress LIKE '$macaddress' AND headertag = 'trigger_action_reinstall'");
1797                                                 $job_db->exec_statement("UPDATE $job_queue_tn SET timestamp='".&calc_timestamp(&get_time(), 'plus', 30)."' WHERE macaddress LIKE '$macaddress' AND headertag = 'trigger_action_reinstall'");
1798                                         }
1799                                 }
1800                                 next;
1801                         }
1803                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1804                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1805                                 if(defined($jobdb_id)) {
1806                                         my $job_msg = @{$hits->{$macaddress}->{$jobdb_headertag}}[7];
1808                                         daemon_log("J DEBUG: its time to execute $job_msg", 7);
1809                                         my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1810                                         my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1812                                         # expect macaddress is unique!!!!!!
1813                                         my $target = $res_hash->{1}->{hostname};
1815                                         # change header
1816                                         $job_msg =~ s/<header>job_/<header>gosa_/;
1818                                         # add sqlite_id
1819                                         $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1821                                         $job_msg =~ /<header>(\S+)<\/header>/;
1822                                         my $header = $1 ;
1823                                         my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");                    
1825                                         # update status in job queue to ...
1826                     # ... 'processing', for jobs: 'reinstall', 'update'
1827                     if (($header =~ /gosa_trigger_action_reinstall/) 
1828                             || ($header =~ /gosa_trigger_activate_new/)
1829                             || ($header =~ /gosa_trigger_action_update/)) {
1830                         my $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
1831                         my $dbres = $job_db->update_dbentry($sql_statement);
1832                     }
1834                     # ... 'done', for all other jobs, they are no longer needed in the jobqueue
1835                     else {
1836                         my $sql_statement = "UPDATE $job_queue_tn SET status='done' WHERE id=$jobdb_id";
1837                         my $dbres = $job_db->update_dbentry($sql_statement);
1838                     }
1839                 
1841                                         # We don't want parallel processing
1842                                         last;
1843                                 }
1844                         }
1845                 }
1847                 $watch_for_new_jobs_in_progress = 0;
1848                 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1849         }
1853 sub watch_for_new_messages {
1854     my ($kernel,$heap) = @_[KERNEL, HEAP];
1855     my @coll_user_msg;   # collection list of outgoing messages
1856     
1857     # check messaging_db for new incoming messages with executable timestamp
1858     my $timestamp = &get_time();
1859     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS UNSIGNED))<$timestamp AND flag='n' AND direction='in' )";
1860     my $res = $messaging_db->exec_statement( $sql_statement );
1861         foreach my $hit (@{$res}) {
1863         # create outgoing messages
1864         my $message_to = @{$hit}[3];
1865         # translate message_to to plain login name
1866         my @message_to_l = split(/,/, $message_to);  
1867                 my %receiver_h; 
1868                 foreach my $receiver (@message_to_l) {
1869                         if ($receiver =~ /^u_([\s\S]*)$/) {
1870                                 $receiver_h{$1} = 0;
1871                         } elsif ($receiver =~ /^g_([\s\S]*)$/) {
1872                                 my $group_name = $1;
1873                                 # fetch all group members from ldap and add them to receiver hash
1874                                 my $ldap_handle = &get_ldap_handle();
1875                                 if (defined $ldap_handle) {
1876                                                 my $mesg = $ldap_handle->search(
1877                                                                                 base => $ldap_base,
1878                                                                                 scope => 'sub',
1879                                                                                 attrs => ['memberUid'],
1880                                                                                 filter => "cn=$group_name",
1881                                                                                 );
1882                                                 if ($mesg->count) {
1883                                                                 my @entries = $mesg->entries;
1884                                                                 foreach my $entry (@entries) {
1885                                                                                 my @receivers= $entry->get_value("memberUid");
1886                                                                                 foreach my $receiver (@receivers) { 
1887                                                                                                 $receiver_h{$receiver} = 0;
1888                                                                                 }
1889                                                                 }
1890                                                 } 
1891                                                 # translating errors ?
1892                                                 if ($mesg->code) {
1893                                                                 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: $mesg->error", 1);
1894                                                 }
1895                                                 &release_ldap_handle($ldap_handle);
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 $sql_statement = "SELECT * FROM $messaging_tn WHERE ( flag='p' AND direction='out' )";
1946     #&daemon_log("0 DEBUG: $sql", 7);
1947     my $res = $messaging_db->exec_statement( $sql_statement );
1948     
1949     # build out msg for each    usr
1950     foreach my $hit (@{$res}) {
1951         my $receiver = @{$hit}[3];
1952         my $msg_id = @{$hit}[0];
1953         my $subject = @{$hit}[1];
1954         my $message = @{$hit}[7];
1956         # resolve usr -> host where usr is logged in
1957         my $sql = "SELECT * FROM $login_users_tn WHERE (user='$receiver')"; 
1958         #&daemon_log("0 DEBUG: $sql", 7);
1959         my $res = $login_users_db->exec_statement($sql);
1961         # receiver is logged in nowhere
1962         if (not ref(@$res[0]) eq "ARRAY") { next; }    
1964         # receiver ist logged in at a client registered at local server
1965                 my $send_succeed = 0;
1966                 foreach my $hit (@$res) {
1967                                 my $receiver_host = @$hit[0];
1968                 my $delivered2host = 0;
1969                                 &daemon_log("M DEBUG: user '$receiver' is logged in at host '$receiver_host'", 7);
1971                                 # Looking for host in know_clients_db 
1972                                 my $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$receiver_host')";
1973                                 my $res = $known_clients_db->exec_statement($sql);
1975                 # Host is known in known_clients_db
1976                 if (ref(@$res[0]) eq "ARRAY") {
1977                     my $receiver_key = @{@{$res}[0]}[2];
1978                     my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
1979                     my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
1980                     my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0); 
1981                     if ($error == 0 ) {
1982                         $send_succeed++ ;
1983                         $delivered2host++ ;
1984                         &daemon_log("M DEBUG: send message for user '$receiver' to host '$receiver_host'", 7); 
1985                     } else {
1986                         &daemon_log("M DEBUG: cannot send message for user '$receiver' to host '$receiver_host'", 7); 
1987                     }
1988                 }
1989                 
1990                 # Message already send, do not need to do anything more, otherwise ...
1991                 if ($delivered2host) { next;}
1992     
1993                 # ...looking for host in foreign_clients_db
1994                 $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$receiver_host')";
1995                 $res = $foreign_clients_db->exec_statement($sql);
1996   
1997                                 # Host is known in foreign_clients_db 
1998                                 if (ref(@$res[0]) eq "ARRAY") { 
1999                     my $registration_server = @{@{$res}[0]}[2];
2000                     
2001                     # Fetch encryption key for registration server
2002                     my $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$registration_server')";
2003                     my $res = $known_server_db->exec_statement($sql);
2004                     if (ref(@$res[0]) eq "ARRAY") { 
2005                         my $registration_server_key = @{@{$res}[0]}[3];
2006                         my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
2007                         my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
2008                         my $error = &send_msg_to_target($out_msg, $registration_server, $registration_server_key, "usr_msg", 0); 
2009                         if ($error == 0 ) {
2010                             $send_succeed++ ;
2011                             $delivered2host++ ;
2012                             &daemon_log("M DEBUG: send message for user '$receiver' to server '$registration_server'", 7); 
2013                         } else {
2014                             &daemon_log("M ERROR: cannot send message for user '$receiver' to server '$registration_server'", 1); 
2015                         }
2017                     } else {
2018                         &daemon_log("M ERROR: host '$receiver_host' is reported to be ".
2019                                 "registrated at server '$registration_server', ".
2020                                 "but no data available in known_server_db ", 1); 
2021                     }
2022                 }
2023                 
2024                 if (not $delivered2host) {
2025                     &daemon_log("M ERROR: unable to send user message to host '$receiver_host'", 1);
2026                 }
2027                 }
2029                 if ($send_succeed) {
2030                                 # set outgoing msg at db to deliverd
2031                                 my $sql = "UPDATE $messaging_tn SET flag='d' WHERE (id='$msg_id' AND direction='out' AND message_to='$receiver')"; 
2032                                 my $res = $messaging_db->exec_statement($sql); 
2033                 &daemon_log("M INFO: send message for user '$receiver' to logged in hosts", 5);
2034                 } else {
2035             &daemon_log("M WARNING: failed to deliver message for user '$receiver'", 3); 
2036         }
2037         }
2039     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay); 
2040     return;
2044 sub watch_for_done_messages {
2045     my ($kernel,$heap) = @_[KERNEL, HEAP];
2047     my $sql = "SELECT * FROM $messaging_tn WHERE (flag='p' AND direction='in')"; 
2048     #&daemon_log("0 DEBUG: $sql", 7);
2049     my $res = $messaging_db->exec_statement($sql); 
2051     foreach my $hit (@{$res}) {
2052         my $msg_id = @{$hit}[0];
2054         my $sql = "SELECT * FROM $messaging_tn WHERE (id='$msg_id' AND direction='out' AND (NOT flag='s'))"; 
2055         #&daemon_log("0 DEBUG: $sql", 7); 
2056         my $res = $messaging_db->exec_statement($sql);
2058         # not all usr msgs have been seen till now
2059         if ( ref(@$res[0]) eq "ARRAY") { next; }
2060         
2061         $sql = "DELETE FROM $messaging_tn WHERE (id='$msg_id')"; 
2062         #&daemon_log("0 DEBUG: $sql", 7);
2063         $res = $messaging_db->exec_statement($sql);
2064     
2065     }
2067     $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay); 
2068     return;
2072 sub watch_for_old_known_clients {
2073     my ($kernel,$heap) = @_[KERNEL, HEAP];
2075     my $sql_statement = "SELECT * FROM $known_clients_tn";
2076     my $res = $known_clients_db->select_dbentry( $sql_statement );
2078     my $cur_time = int(&get_time());
2080     while ( my ($hit_num, $hit) = each %$res) {
2081         my $expired_timestamp = int($hit->{'timestamp'});
2082         $expired_timestamp =~ /(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/;
2083         my $dt = DateTime->new( year   => $1,
2084                 month  => $2,
2085                 day    => $3,
2086                 hour   => $4,
2087                 minute => $5,
2088                 second => $6,
2089                 );
2091         $dt->add( seconds => 2 * int($hit->{'keylifetime'}) );
2092         $expired_timestamp = $dt->ymd('').$dt->hms('');
2093         if ($cur_time > $expired_timestamp) {
2094             my $hostname = $hit->{'hostname'};
2095             my $del_sql = "DELETE FROM $known_clients_tn WHERE hostname='$hostname'"; 
2096             my $del_res = $known_clients_db->exec_statement($del_sql);
2098             &main::daemon_log("0 INFO: timestamp '".$hit->{'timestamp'}."' of client '$hostname' is expired('$expired_timestamp'), client will be deleted from known_clients_db", 5);
2099         }
2101     }
2103     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
2107 sub watch_for_next_tasks {
2108     my ($kernel,$heap) = @_[KERNEL, HEAP];
2110     my $sql = "SELECT * FROM $incoming_tn";
2111     my $res = $incoming_db->select_dbentry($sql);
2112     
2113     while ( my ($hit_num, $hit) = each %$res) {
2114         my $headertag = $hit->{'headertag'};
2115         if ($headertag =~ /^answer_(\d+)/) {
2116             # do not start processing, this message is for a still running POE::Wheel
2117             next;
2118         }
2119         my $message_id = $hit->{'id'};
2120         my $session_id = $hit->{'sessionid'};
2121         &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;
2137         if (not defined $session_id ) { $session_id = 0 };
2138         if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
2140         my ($package, $file, $row, $subroutine, $hasArgs, $wantArray, $evalText, $isRequire) = caller(1);
2141         my $caller_text = "subroutine $subroutine";
2142         if ($subroutine eq "(eval)") {
2143                 $caller_text = "eval block within file '$file' for '$evalText'"; 
2144         }
2145         daemon_log("$session_id INFO: new ldap handle for '$caller_text' required!", 1);
2147 get_handle:
2148         my $ldap_handle = Net::LDAP->new( $ldap_uri );
2149         if (not ref $ldap_handle) {
2150                 daemon_log("$session_id ERROR: Connection to LDAP URI '$ldap_uri' failed! Retrying!", 1);
2151                 usleep(1000);
2152                 goto get_handle;
2153         } else {
2154                 daemon_log("$session_id DEBUG: Connection to LDAP URI '$ldap_uri' established.", 6);
2155         }
2157         $ldap_handle->bind($ldap_admin_dn, password => $ldap_admin_password) or &daemon_log("$session_id ERROR: Could not bind as '$ldap_admin_dn' to LDAP URI '$ldap_uri'!", 1);
2158         return $ldap_handle;
2162 sub release_ldap_handle {
2163         my ($ldap_handle) = @_ ;
2164         if(ref $ldap_handle) {
2165           $ldap_handle->disconnect();
2166   }
2167         &main::daemon_log("0 DEBUG: Released a ldap handle!", 6);
2168         return;
2172 sub change_fai_state {
2173         my ($st, $targets, $session_id) = @_;
2174         $session_id = 0 if not defined $session_id;
2175         # Set FAI state to localboot
2176         my %mapActions= (
2177                 reboot    => '',
2178                 update    => 'softupdate',
2179                 localboot => 'localboot',
2180                 reinstall => 'install',
2181                 rescan    => '',
2182                 wake      => '',
2183                 memcheck  => 'memcheck',
2184                 sysinfo   => 'sysinfo',
2185                 install   => 'install',
2186         );
2188         # Return if this is unknown
2189         if (!exists $mapActions{ $st }){
2190                 daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1); 
2191                 return;
2192         }
2194         my $state= $mapActions{ $st };
2196         # Build search filter for hosts
2197         my $search= "(&(objectClass=GOhard)";
2198         foreach (@{$targets}){
2199                 $search.= "(macAddress=$_)";
2200         }
2201         $search.= ")";
2203         # If there's any host inside of the search string, procress them
2204         if (!($search =~ /macAddress/)){
2205                 daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);    
2206                 return;
2207         }
2209         my $ldap_handle = &get_ldap_handle($session_id);
2210         # Perform search for Unit Tag
2211         my $mesg = $ldap_handle->search(
2212                 base   => $ldap_base,
2213                 scope  => 'sub',
2214                 attrs  => ['dn', 'FAIstate', 'objectClass'],
2215                 filter => "$search"
2216         );
2218         if ($mesg->count) {
2219                 my @entries = $mesg->entries;
2220                 if (0 == @entries) {
2221                         daemon_log("$session_id ERROR: ldap search failed: ldap_base=$ldap_base, filter=$search", 1); 
2222                 }
2224                 foreach my $entry (@entries) {
2225                         # Only modify entry if it is not set to '$state'
2226                         if ($entry->get_value("FAIstate") ne "$state"){
2227                                 daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
2228                                 my $result;
2229                                 my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
2230                                 if (exists $tmp{'FAIobject'}){
2231                                         if ($state eq ''){
2232                                                 $result= $ldap_handle->modify($entry->dn, changes => [ delete => [ FAIstate => [] ] ]);
2233                                         } else {
2234                                                 $result= $ldap_handle->modify($entry->dn, changes => [ replace => [ FAIstate => $state ] ]);
2235                                         }
2236                                 } elsif ($state ne ''){
2237                                         $result= $ldap_handle->modify($entry->dn, changes => [ add => [ objectClass => 'FAIobject' ], add => [ FAIstate => $state ] ]);
2238                                 }
2240                                 # Errors?
2241                                 if ($result->code){
2242                                         daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2243                                 }
2244                         } else {
2245                                 daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 7); 
2246                         }  
2247                 }
2248         } else {
2249                 daemon_log("$session_id ERROR: LDAP search failed: ldap_base=$ldap_base, filter=$search", 1);
2250         }
2251         &release_ldap_handle($ldap_handle);               
2253         return;
2257 sub change_goto_state {
2258     my ($st, $targets, $session_id) = @_;
2259     $session_id = 0  if not defined $session_id;
2261     # Switch on or off?
2262     my $state= $st eq 'active' ? 'active': 'locked';
2264     my $ldap_handle = &get_ldap_handle($session_id);
2265     if( defined($ldap_handle) ) {
2267       # Build search filter for hosts
2268       my $search= "(&(objectClass=GOhard)";
2269       foreach (@{$targets}){
2270         $search.= "(macAddress=$_)";
2271       }
2272       $search.= ")";
2274       # If there's any host inside of the search string, procress them
2275       if (!($search =~ /macAddress/)){
2276               &release_ldap_handle($ldap_handle);
2277         return;
2278       }
2280       # Perform search for Unit Tag
2281       my $mesg = $ldap_handle->search(
2282           base   => $ldap_base,
2283           scope  => 'sub',
2284           attrs  => ['dn', 'gotoMode'],
2285           filter => "$search"
2286           );
2288       if ($mesg->count) {
2289         my @entries = $mesg->entries;
2290         foreach my $entry (@entries) {
2292           # Only modify entry if it is not set to '$state'
2293           if ($entry->get_value("gotoMode") ne $state){
2295             daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
2296             my $result;
2297             $result= $ldap_handle->modify($entry->dn, changes => [replace => [ gotoMode => $state ] ]);
2299             # Errors?
2300             if ($result->code){
2301               &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2302             }
2304           }
2305         }
2306       } else {
2307                 daemon_log("$session_id ERROR: LDAP search failed in function change_goto_state: ldap_base=$ldap_base, filter=$search", 1);
2308           }
2310     }
2311         &release_ldap_handle($ldap_handle);
2312         return;
2316 sub run_recreate_packages_db {
2317     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2318     my $session_id = $session->ID;
2319         &main::daemon_log("$session_id INFO: Recreating FAI Packages DB ('$fai_release_tn', '$fai_server_tn', '$packages_list_tn')", 5);
2320         $kernel->yield('create_fai_release_db', $fai_release_tn);
2321         $kernel->yield('create_fai_server_db', $fai_server_tn);
2322         return;
2326 sub run_create_fai_server_db {
2327     my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2328     my $session_id = $session->ID;
2329     my $task = POE::Wheel::Run->new(
2330             Program => sub { &create_fai_server_db($table_name,$kernel, undef, $session_id) },
2331             StdoutEvent  => "session_run_result",
2332             StderrEvent  => "session_run_debug",
2333             CloseEvent   => "session_run_done",
2334             );
2336     $heap->{task}->{ $task->ID } = $task;
2337     return;
2341 sub create_fai_server_db {
2342         my ($table_name, $kernel, $dont_create_packages_list, $session_id) = @_;
2343         my $result;
2345         if (not defined $session_id) { $session_id = 0; }
2346         my $ldap_handle = &get_ldap_handle($session_id);
2347         if(defined($ldap_handle)) {
2348                 daemon_log("$session_id INFO: create_fai_server_db: start", 5);
2349                 my $mesg= $ldap_handle->search(
2350                         base   => $ldap_base,
2351                         scope  => 'sub',
2352                         attrs  => ['FAIrepository', 'gosaUnitTag'],
2353                         filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
2354                 );
2355                 if($mesg->{'resultCode'} == 0 &&
2356                         $mesg->count != 0) {
2357                         foreach my $entry (@{$mesg->{entries}}) {
2358                                 if($entry->exists('FAIrepository')) {
2359                                         # Add an entry for each Repository configured for server
2360                                         foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
2361                                                 my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
2362                                                 my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
2363                                                 $result= $fai_server_db->add_dbentry( { 
2364                                                                 table => $table_name,
2365                                                                 primkey => ['server', 'fai_release', 'tag'],
2366                                                                 server => $tmp_url,
2367                                                                 fai_release => $tmp_release,
2368                                                                 sections => $tmp_sections,
2369                                                                 tag => (length($tmp_tag)>0)?$tmp_tag:"",
2370                                                         } );
2371                                         }
2372                                 }
2373                         }
2374                 }
2375                 daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
2376                 &release_ldap_handle($ldap_handle);
2378                 # TODO: Find a way to post the 'create_packages_list_db' event
2379                 if(not defined($dont_create_packages_list)) {
2380                         &create_packages_list_db(undef, $session_id);
2381                 }
2382         }       
2384         return $result;
2388 sub run_create_fai_release_db {
2389         my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2390         my $session_id = $session->ID;
2391         my $task = POE::Wheel::Run->new(
2392                 Program => sub { &create_fai_release_db($table_name, $session_id) },
2393                 StdoutEvent  => "session_run_result",
2394                 StderrEvent  => "session_run_debug",
2395                 CloseEvent   => "session_run_done",
2396         );
2398         $heap->{task}->{ $task->ID } = $task;
2399         return;
2403 sub create_fai_release_db {
2404         my ($table_name, $session_id) = @_;
2405         my $result;
2407         # used for logging
2408         if (not defined $session_id) { $session_id = 0; }
2410         my $ldap_handle = &get_ldap_handle($session_id);
2411         if(defined($ldap_handle)) {
2412                 daemon_log("$session_id INFO: create_fai_release_db: start",5);
2413                 my $mesg= $ldap_handle->search(
2414                         base   => $ldap_base,
2415                         scope  => 'sub',
2416                         attrs  => [],
2417                         filter => "(&(objectClass=organizationalUnit)(ou=fai))",
2418                 );
2419                 if(($mesg->code == 0) && ($mesg->count != 0))
2420                 {
2421                         daemon_log("$session_id DEBUG: create_fai_release_db: count " . $mesg->count,8);
2423                         # Walk through all possible FAI container ou's
2424                         my @sql_list;
2425                         my $timestamp= &get_time();
2426                         foreach my $ou (@{$mesg->{entries}}) {
2427                                 my $tmp_classes= resolve_fai_classes($ou->dn, $ldap_handle, $session_id);
2428                                 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
2429                                         my @tmp_array=get_fai_release_entries($tmp_classes);
2430                                         if(@tmp_array) {
2431                                                 foreach my $entry (@tmp_array) {
2432                                                         if(defined($entry) && ref($entry) eq 'HASH') {
2433                                                                 my $sql= 
2434                                                                 "INSERT INTO $table_name "
2435                                                                 ."(timestamp, fai_release, class, type, state) VALUES ("
2436                                                                 .$timestamp.","
2437                                                                 ."'".$entry->{'release'}."',"
2438                                                                 ."'".$entry->{'class'}."',"
2439                                                                 ."'".$entry->{'type'}."',"
2440                                                                 ."'".$entry->{'state'}."')";
2441                                                                 push @sql_list, $sql;
2442                                                         }
2443                                                 }
2444                                         }
2445                                 }
2446                         }
2448                         daemon_log("$session_id DEBUG: create_fai_release_db: Inserting ".scalar @sql_list." entries to DB",8);
2449             &release_ldap_handle($ldap_handle);
2450                         if(@sql_list) {
2451                                 unshift @sql_list, "VACUUM";
2452                                 unshift @sql_list, "DELETE FROM $table_name";
2453                                 $fai_release_db->exec_statementlist(\@sql_list);
2454                         }
2455                         daemon_log("$session_id DEBUG: create_fai_release_db: Done with inserting",7);
2456                 } else {
2457                         daemon_log("$session_id INFO: create_fai_release_db: error: " . $mesg->code, 5);
2458                 }
2459                 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
2460         }
2461         return $result;
2464 sub get_fai_types {
2465         my $tmp_classes = shift || return undef;
2466         my @result;
2468         foreach my $type(keys %{$tmp_classes}) {
2469                 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
2470                         my $entry = {
2471                                 type => $type,
2472                                 state => $tmp_classes->{$type}[0],
2473                         };
2474                         push @result, $entry;
2475                 }
2476         }
2478         return @result;
2481 sub get_fai_state {
2482         my $result = "";
2483         my $tmp_classes = shift || return $result;
2485         foreach my $type(keys %{$tmp_classes}) {
2486                 if(defined($tmp_classes->{$type}[0])) {
2487                         $result = $tmp_classes->{$type}[0];
2488                         
2489                 # State is equal for all types in class
2490                         last;
2491                 }
2492         }
2494         return $result;
2497 sub resolve_fai_classes {
2498         my ($fai_base, $ldap_handle, $session_id) = @_;
2499         if (not defined $session_id) { $session_id = 0; }
2500         my $result;
2501         my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
2502         my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
2503         my $fai_classes;
2505         daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base",7);
2506         my $mesg= $ldap_handle->search(
2507                 base   => $fai_base,
2508                 scope  => 'sub',
2509                 attrs  => ['cn','objectClass','FAIstate'],
2510                 filter => $fai_filter,
2511         );
2512         daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries",7);
2514         if($mesg->{'resultCode'} == 0 &&
2515                 $mesg->count != 0) {
2516                 foreach my $entry (@{$mesg->{entries}}) {
2517                         if($entry->exists('cn')) {
2518                                 my $tmp_dn= $entry->dn();
2519                                 $tmp_dn= substr( $tmp_dn, 0, length($tmp_dn)
2520                                         - length($fai_base) - 1 );
2522                                 # Skip classname and ou dn parts for class
2523                                 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?)$/;
2525                                 # Skip classes without releases
2526                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2527                                         next;
2528                                 }
2530                                 my $tmp_cn= $entry->get_value('cn');
2531                                 my $tmp_state= $entry->get_value('FAIstate');
2533                                 my $tmp_type;
2534                                 # Get FAI type
2535                                 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
2536                                         if(grep $_ eq $oclass, @possible_fai_classes) {
2537                                                 $tmp_type= $oclass;
2538                                                 last;
2539                                         }
2540                                 }
2542                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2543                                         # A Subrelease
2544                                         my @sub_releases = split(/,/, $tmp_release);
2546                                         # Walk through subreleases and build hash tree
2547                                         my $hash;
2548                                         while(my $tmp_sub_release = pop @sub_releases) {
2549                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2550                                         }
2551                                         eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
2552                                 } else {
2553                                         # A branch, no subrelease
2554                                         push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
2555                                 }
2556                         } elsif (!$entry->exists('cn')) {
2557                                 my $tmp_dn= $entry->dn();
2558                                 $tmp_dn= substr( $tmp_dn, 0, length($tmp_dn)
2559                                         - length($fai_base) - 1 );
2560                                 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?)$/;
2562                                 # Skip classes without releases
2563                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2564                                         next;
2565                                 }
2567                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2568                                         # A Subrelease
2569                                         my @sub_releases= split(/,/, $tmp_release);
2571                                         # Walk through subreleases and build hash tree
2572                                         my $hash;
2573                                         while(my $tmp_sub_release = pop @sub_releases) {
2574                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2575                                         }
2576                                         # Remove the last two characters
2577                                         chop($hash);
2578                                         chop($hash);
2580                                         eval('$fai_classes->'.$hash.'= {}');
2581                                 } else {
2582                                         # A branch, no subrelease
2583                                         if(!exists($fai_classes->{$tmp_release})) {
2584                                                 $fai_classes->{$tmp_release} = {};
2585                                         }
2586                                 }
2587                         }
2588                 }
2590                 # The hash is complete, now we can honor the copy-on-write based missing entries
2591                 foreach my $release (keys %$fai_classes) {
2592                         $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
2593                 }
2594         }
2595         return $result;
2598 sub apply_fai_inheritance {
2599        my $fai_classes = shift || return {};
2600        my $tmp_classes;
2602        # Get the classes from the branch
2603        foreach my $class (keys %{$fai_classes}) {
2604                # Skip subreleases
2605                if($class =~ /^ou=.*$/) {
2606                        next;
2607                } else {
2608                        $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
2609                }
2610        }
2612        # Apply to each subrelease
2613        foreach my $subrelease (keys %{$fai_classes}) {
2614                if($subrelease =~ /ou=/) {
2615                        foreach my $tmp_class (keys %{$tmp_classes}) {
2616                                if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
2617                                        $fai_classes->{$subrelease}->{$tmp_class} =
2618                                        deep_copy($tmp_classes->{$tmp_class});
2619                                } else {
2620                                        foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
2621                                                if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
2622                                                        $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
2623                                                        deep_copy($tmp_classes->{$tmp_class}->{$type});
2624                                                }
2625                                        }
2626                                }
2627                        }
2628                }
2629        }
2631        # Find subreleases in deeper levels
2632        foreach my $subrelease (keys %{$fai_classes}) {
2633                if($subrelease =~ /ou=/) {
2634                        foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
2635                                if($subsubrelease =~ /ou=/) {
2636                                        apply_fai_inheritance($fai_classes->{$subrelease});
2637                                }
2638                        }
2639                }
2640        }
2642        return $fai_classes;
2645 sub get_fai_release_entries {
2646         my $tmp_classes = shift || return;
2647         my $parent = shift || "";
2648         my @result = shift || ();
2650         foreach my $entry (keys %{$tmp_classes}) {
2651                 if(defined($entry)) {
2652                         if($entry =~ /^ou=.*$/) {
2653                                 my $release_name = $entry;
2654                                 $release_name =~ s/ou=//g;
2655                                 if(length($parent)>0) {
2656                                         $release_name = $parent."/".$release_name;
2657                                 }
2658                                 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2659                                 foreach my $bufentry(@bufentries) {
2660                                         push @result, $bufentry;
2661                                 }
2662                         } else {
2663                                 my @types = get_fai_types($tmp_classes->{$entry});
2664                                 foreach my $type (@types) {
2665                                         push @result, 
2666                                         {
2667                                                 'class' => $entry,
2668                                                 'type' => $type->{'type'},
2669                                                 'release' => $parent,
2670                                                 'state' => $type->{'state'},
2671                                         };
2672                                 }
2673                         }
2674                 }
2675         }
2677         return @result;
2680 sub deep_copy {
2681         my $this = shift;
2682         if (not ref $this) {
2683                 $this;
2684         } elsif (ref $this eq "ARRAY") {
2685                 [map deep_copy($_), @$this];
2686         } elsif (ref $this eq "HASH") {
2687                 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2688         } else { die "what type is $_?" }
2692 sub session_run_result {
2693     my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];    
2694     $kernel->sig(CHLD => "child_reap");
2697 sub session_run_debug {
2698     my $result = $_[ARG0];
2699     print STDERR "$result\n";
2702 sub session_run_done {
2703     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2704     delete $heap->{task}->{$task_id};
2705         if (exists $heap->{ldap_handle}->{$task_id}) {
2706                 &release_ldap_handle($heap->{ldap_handle}->{$task_id});
2707         }
2708         delete $heap->{ldap_handle}->{$task_id};
2712 sub create_sources_list {
2713         my $session_id = shift || 0;
2714         my $result="/tmp/gosa_si_tmp_sources_list";
2716         # Remove old file
2717         if(stat($result)) {
2718                 unlink($result);
2719                 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7); 
2720         }
2722         my $fh;
2723         open($fh, ">$result");
2724         if (not defined $fh) {
2725                 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7); 
2726                 return undef;
2727         }
2728         if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2729                 my $ldap_handle = &get_ldap_handle($session_id);
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                 &release_ldap_handle($ldap_handle);
2748         } else {
2749                 if (defined $main::ldap_server_dn){
2750                         &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1); 
2751                 } else {
2752                         &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2753                 }
2754         }
2755         close($fh);
2757         return $result;
2761 sub run_create_packages_list_db {
2762     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2763         my $session_id = $session->ID;
2764         my $task = POE::Wheel::Run->new(
2765                                         Priority => +20,
2766                                         Program => sub {&create_packages_list_db(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 ($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; }
2793         if (not defined $sources_file) { 
2794                 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5); 
2795                 $sources_file = &create_sources_list($session_id);
2796         }
2798         if (not defined $sources_file) {
2799                 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1); 
2800                 unlink($packages_list_under_construction);
2801                 return;
2802         }
2804         my $line;
2806         open(CONFIG, "<$sources_file") or do {
2807                 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2808                 unlink($packages_list_under_construction);
2809                 return;
2810         };
2812         # Read lines
2813         while ($line = <CONFIG>){
2814                 # Unify
2815                 chop($line);
2816                 $line =~ s/^\s+//;
2817                 $line =~ s/^\s+/ /;
2819                 # Strip comments
2820                 $line =~ s/#.*$//g;
2822                 # Skip empty lines
2823                 if ($line =~ /^\s*$/){
2824                         next;
2825                 }
2827                 # Interpret deb line
2828                 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2829                         my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2830                         my $section;
2831                         foreach $section (split(' ', $sections)){
2832                                 &parse_package_info( $baseurl, $dist, $section, $session_id );
2833                         }
2834                 }
2835         }
2837         close (CONFIG);
2839         if(keys(%repo_dirs)) {
2840                 find(\&cleanup_and_extract, keys( %repo_dirs ));
2841                 &main::strip_packages_list_statements();
2842                 $packages_list_db->exec_statementlist(\@packages_list_statements);
2843         }
2844         unlink($packages_list_under_construction);
2845         daemon_log("$session_id INFO: create_packages_list_db: finished", 5); 
2846         return;
2849 # This function should do some intensive task to minimize the db-traffic
2850 sub strip_packages_list_statements {
2851         my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2852         my @new_statement_list=();
2853         my $hash;
2854         my $insert_hash;
2855         my $update_hash;
2856         my $delete_hash;
2857         my $known_packages_hash;
2858         my $local_timestamp=get_time();
2860         foreach my $existing_entry (@existing_entries) {
2861                 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
2862         }
2864         foreach my $statement (@packages_list_statements) {
2865                 if($statement =~ /^INSERT/i) {
2866                         # Assign the values from the insert statement
2867                         my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~ 
2868                         /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
2869                         if(exists($hash->{$distribution}->{$package}->{$version})) {
2870                                 # If section or description has changed, update the DB
2871                                 if( 
2872                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or 
2873                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
2874                                 ) {
2875                                         @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
2876                                 } else {
2877                                         # package is already present in database. cache this knowledge for later use
2878                                         @{$known_packages_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2879                                 }
2880                         } else {
2881                                 # Insert a non-existing entry to db
2882                                 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2883                         }
2884                 } elsif ($statement =~ /^UPDATE/i) {
2885                         my ($template,$package,$version) = ($1,$2,$3) if $statement =~
2886                         /^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;
2887                         foreach my $distribution (keys %{$hash}) {
2888                                 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
2889                                         # update the insertion hash to execute only one query per package (insert instead insert+update)
2890                                         @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
2891                                 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
2892                                         if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
2893                                                 my $section;
2894                                                 my $description;
2895                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
2896                                                         length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
2897                                                         $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
2898                                                 }
2899                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2900                                                         $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
2901                                                 }
2902                                                 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2903                                         }
2904                                 }
2905                         }
2906                 }
2907         }
2909         # Check for orphaned entries
2910         foreach my $existing_entry (@existing_entries) {
2911                 my $distribution= @{$existing_entry}[0];
2912                 my $package= @{$existing_entry}[1];
2913                 my $version= @{$existing_entry}[2];
2914                 my $section= @{$existing_entry}[3];
2916                 if(
2917                         exists($insert_hash->{$distribution}->{$package}->{$version}) ||
2918                         exists($update_hash->{$distribution}->{$package}->{$version}) ||
2919                         exists($known_packages_hash->{$distribution}->{$package}->{$version})
2920                 ) {
2921                         next;
2922                 } else {
2923                         # Insert entry to delete hash
2924                         @{$delete_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section);
2925                 }
2926         }
2928         # unroll the insert hash
2929         foreach my $distribution (keys %{$insert_hash}) {
2930                 foreach my $package (keys %{$insert_hash->{$distribution}}) {
2931                         foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
2932                                 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
2933                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
2934                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
2935                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
2936                                 ."'$local_timestamp')";
2937                         }
2938                 }
2939         }
2941         # unroll the update hash
2942         foreach my $distribution (keys %{$update_hash}) {
2943                 foreach my $package (keys %{$update_hash->{$distribution}}) {
2944                         foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
2945                                 my $set = "";
2946                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
2947                                         $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
2948                                 }
2949                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2950                                         $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
2951                                 }
2952                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
2953                                         $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
2954                                 }
2955                                 if(defined($set) and length($set) > 0) {
2956                                         $set .= "timestamp = '$local_timestamp'";
2957                                 } else {
2958                                         next;
2959                                 }
2960                                 push @new_statement_list, 
2961                                 "UPDATE $main::packages_list_tn SET $set WHERE"
2962                                 ." distribution = '$distribution'"
2963                                 ." AND package = '$package'"
2964                                 ." AND version = '$version'";
2965                         }
2966                 }
2967         }
2968         
2969         # unroll the delete hash
2970         foreach my $distribution (keys %{$delete_hash}) {
2971                 foreach my $package (keys %{$delete_hash->{$distribution}}) {
2972                         foreach my $version (keys %{$delete_hash->{$distribution}->{$package}}) {
2973                                 my $section = @{$delete_hash->{$distribution}->{$package}->{$version}}[3];
2974                                 push @new_statement_list, "DELETE FROM $main::packages_list_tn WHERE distribution='$distribution' AND package='$package' AND version='$version' AND section='$section'";
2975                         }
2976                 }
2977         }
2979         unshift(@new_statement_list, "VACUUM");
2981         @packages_list_statements = @new_statement_list;
2985 sub parse_package_info {
2986     my ($baseurl, $dist, $section, $session_id)= @_;
2987     my ($package);
2988     if (not defined $session_id) { $session_id = 0; }
2989     my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
2990     $repo_dirs{ "${repo_path}/pool" } = 1;
2992     foreach $package ("Packages.gz"){
2993         daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
2994         get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
2995         parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
2996     }
2997     
3001 sub get_package {
3002     my ($url, $dest, $session_id)= @_;
3003     if (not defined $session_id) { $session_id = 0; }
3005     my $tpath = dirname($dest);
3006     -d "$tpath" || mkpath "$tpath";
3008     # This is ugly, but I've no time to take a look at "how it works in perl"
3009     if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
3010         system("gunzip -cd '$dest' > '$dest.in'");
3011         daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 5);
3012         unlink($dest);
3013         daemon_log("$session_id DEBUG: delete file '$dest'", 5); 
3014     } else {
3015         daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' into '$dest' failed!", 1);
3016     }
3017     return 0;
3021 sub parse_package {
3022     my ($path, $dist, $srv_path, $session_id)= @_;
3023     if (not defined $session_id) { $session_id = 0;}
3024     my ($package, $version, $section, $description);
3025     my $PACKAGES;
3026     my $timestamp = &get_time();
3028     if(not stat("$path.in")) {
3029         daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
3030         return;
3031     }
3033     open($PACKAGES, "<$path.in");
3034     if(not defined($PACKAGES)) {
3035         daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1); 
3036         return;
3037     }
3039     # Read lines
3040     while (<$PACKAGES>){
3041         my $line = $_;
3042         # Unify
3043         chop($line);
3045         # Use empty lines as a trigger
3046         if ($line =~ /^\s*$/){
3047             my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
3048             push(@packages_list_statements, $sql);
3049             $package = "none";
3050             $version = "none";
3051             $section = "none";
3052             $description = "none"; 
3053             next;
3054         }
3056         # Trigger for package name
3057         if ($line =~ /^Package:\s/){
3058             ($package)= ($line =~ /^Package: (.*)$/);
3059             next;
3060         }
3062         # Trigger for version
3063         if ($line =~ /^Version:\s/){
3064             ($version)= ($line =~ /^Version: (.*)$/);
3065             next;
3066         }
3068         # Trigger for description
3069         if ($line =~ /^Description:\s/){
3070             ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
3071             next;
3072         }
3074         # Trigger for section
3075         if ($line =~ /^Section:\s/){
3076             ($section)= ($line =~ /^Section: (.*)$/);
3077             next;
3078         }
3080         # Trigger for filename
3081         if ($line =~ /^Filename:\s/){
3082             my ($filename) = ($line =~ /^Filename: (.*)$/);
3083             store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
3084             next;
3085         }
3086     }
3088     close( $PACKAGES );
3089     unlink( "$path.in" );
3093 sub store_fileinfo {
3094     my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
3096     my %fileinfo = (
3097         'package' => $package,
3098         'dist' => $dist,
3099         'version' => $vers,
3100     );
3102     $repo_files{ "${srvdir}/$file" } = \%fileinfo;
3106 sub cleanup_and_extract {
3107         my $fileinfo = $repo_files{ $File::Find::name };
3109         if( defined $fileinfo ) {
3110                 my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
3111                 my $sql;
3112                 my $package = $fileinfo->{ 'package' };
3113                 my $newver = $fileinfo->{ 'version' };
3115                 mkpath($dir);
3116                 system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
3118                 if( -f "$dir/DEBIAN/templates" ) {
3120                         daemon_log("0 DEBUG: Found debconf templates in '$package' - $newver", 7);
3122                         my $tmpl= ""; {
3123                                 local $/=undef;
3124                                 open FILE, "$dir/DEBIAN/templates";
3125                                 $tmpl = &encode_base64(<FILE>);
3126                                 close FILE;
3127                         }
3128                         rmtree("$dir/DEBIAN/templates");
3130                         $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
3131                         push @packages_list_statements, $sql;
3132                 }
3133         }
3135         return;
3139 sub register_at_foreign_servers {   
3140     my ($kernel) = $_[KERNEL];
3142     # hole alle bekannten server aus known_server_db
3143     my $server_sql = "SELECT * FROM $known_server_tn";
3144     my $server_res = $known_server_db->exec_statement($server_sql);
3146     # no entries in known_server_db
3147     if (not ref(@$server_res[0]) eq "ARRAY") { 
3148         # TODO
3149     }
3151     # detect already connected clients
3152     my $client_sql = "SELECT * FROM $known_clients_tn"; 
3153     my $client_res = $known_clients_db->exec_statement($client_sql);
3155     # send my server details to all other gosa-si-server within the network
3156     foreach my $hit (@$server_res) {
3157         my $hostname = @$hit[0];
3158         my $hostkey = &create_passwd;
3160         # add already connected clients to registration message 
3161         my $myhash = &create_xml_hash('new_server', $server_address, $hostname);
3162         &add_content2xml_hash($myhash, 'key', $hostkey);
3163         map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res);
3165         # add locally loaded gosa-si modules to registration message
3166         my $loaded_modules = {};
3167         while (my ($package, $pck_info) = each %$known_modules) {
3168                                                 next if ((!defined(@$pck_info[2])) || (!(ref (@$pck_info[2]) eq 'HASH')));
3169                                                 foreach my $act_module (keys(%{@$pck_info[2]})) {
3170                                                         $loaded_modules->{$act_module} = ""; 
3171                                                 }
3172         }
3174         map(&add_content2xml_hash($myhash, "loaded_modules", $_), keys(%$loaded_modules));
3176         # add macaddress to registration message
3177         my ($host_ip, $host_port) = split(/:/, $hostname);
3178         my $local_ip = &get_local_ip_for_remote_ip($host_ip);
3179         my $network_interface= &get_interface_for_ip($local_ip);
3180         my $host_mac = &get_mac_for_interface($network_interface);
3181         &add_content2xml_hash($myhash, 'macaddress', $host_mac);
3182         
3183         # build registration message and send it
3184         my $foreign_server_msg = &create_xml_string($myhash);
3185         my $error = &send_msg_to_target($foreign_server_msg, $hostname, $ServerPackages_key, "new_server", 0); 
3186     }
3187     
3188     $kernel->delay_set("register_at_foreign_servers", $foreign_servers_register_delay); 
3189     return;
3193 #==== MAIN = main ==============================================================
3194 #  parse commandline options
3195 Getopt::Long::Configure( "bundling" );
3196 GetOptions("h|help" => \&usage,
3197         "c|config=s" => \$cfg_file,
3198         "f|foreground" => \$foreground,
3199         "v|verbose+" => \$verbose,
3200         "no-arp+" => \$no_arp,
3201            );
3203 #  read and set config parameters
3204 &check_cmdline_param ;
3205 &read_configfile($cfg_file, %cfg_defaults);
3206 &check_pid;
3208 $SIG{CHLD} = 'IGNORE';
3210 # forward error messages to logfile
3211 if( ! $foreground ) {
3212   open( STDIN,  '+>/dev/null' );
3213   open( STDOUT, '+>&STDIN'    );
3214   open( STDERR, '+>&STDIN'    );
3217 # Just fork, if we are not in foreground mode
3218 if( ! $foreground ) { 
3219     chdir '/'                 or die "Can't chdir to /: $!";
3220     $pid = fork;
3221     setsid                    or die "Can't start a new session: $!";
3222     umask 0;
3223 } else { 
3224     $pid = $$; 
3227 # Do something useful - put our PID into the pid_file
3228 if( 0 != $pid ) {
3229     open( LOCK_FILE, ">$pid_file" );
3230     print LOCK_FILE "$pid\n";
3231     close( LOCK_FILE );
3232     if( !$foreground ) { 
3233         exit( 0 ) 
3234     };
3237 # parse head url and revision from svn
3238 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
3239 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
3240 $server_headURL = defined $1 ? $1 : 'unknown' ;
3241 $server_revision = defined $2 ? $2 : 'unknown' ;
3242 if ($server_headURL =~ /\/tag\// || 
3243         $server_headURL =~ /\/branches\// ) {
3244     $server_status = "stable"; 
3245 } else {
3246     $server_status = "developmental" ;
3248 # Prepare log file and set permissions
3249 $root_uid = getpwnam('root');
3250 $adm_gid = getgrnam('adm');
3251 open(FH, ">>$log_file");
3252 close FH;
3253 chmod(0440, $log_file);
3254 chown($root_uid, $adm_gid, $log_file);
3255 chown($root_uid, $adm_gid, "/var/lib/gosa-si");
3257 daemon_log(" ", 1);
3258 daemon_log("$0 started!", 1);
3259 daemon_log("status: $server_status", 1);
3260 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1); 
3262 # Buildup data bases
3264     no strict "refs";
3266     if ($db_module eq "DBmysql") {
3267         # connect to incoming_db
3268         $incoming_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3270         # connect to gosa-si job queue
3271         $job_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3273         # connect to known_clients_db
3274         $known_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3276         # connect to foreign_clients_db
3277         $foreign_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3279         # connect to known_server_db
3280         $known_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3282         # connect to login_usr_db
3283         $login_users_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3285         # connect to fai_server_db 
3286         $fai_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3288         # connect to fai_release_db
3289         $fai_release_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3291         # connect to packages_list_db
3292         $packages_list_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3294         # connect to messaging_db
3295         $messaging_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3297     } elsif ($db_module eq "DBsqlite") {
3298         # connect to incoming_db
3299         unlink($incoming_file_name);
3300         $incoming_db = GOSA::DBsqlite->new($incoming_file_name);
3301         
3302         # connect to gosa-si job queue
3303         $job_db = GOSA::DBsqlite->new($job_queue_file_name);
3304         chmod(0640, $job_queue_file_name);
3305         chown($root_uid, $adm_gid, $job_queue_file_name);
3306         
3307         # connect to known_clients_db
3308         $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
3309         chmod(0640, $known_clients_file_name);
3310         chown($root_uid, $adm_gid, $known_clients_file_name);
3311         
3312         # connect to foreign_clients_db
3313         unlink($foreign_clients_file_name);
3314         $foreign_clients_db = GOSA::DBsqlite->new($foreign_clients_file_name);
3315         chmod(0640, $foreign_clients_file_name);
3316         chown($root_uid, $adm_gid, $foreign_clients_file_name);
3317         
3318         # connect to known_server_db
3319         unlink($known_server_file_name);
3320         $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
3321         chmod(0640, $known_server_file_name);
3322         chown($root_uid, $adm_gid, $known_server_file_name);
3323         
3324         # connect to login_usr_db
3325         unlink($login_users_file_name);
3326         $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
3327         chmod(0640, $login_users_file_name);
3328         chown($root_uid, $adm_gid, $login_users_file_name);
3329         
3330         # connect to fai_server_db
3331         unlink($fai_server_file_name);
3332         $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
3333         chmod(0640, $fai_server_file_name);
3334         chown($root_uid, $adm_gid, $fai_server_file_name);
3335         
3336         # connect to fai_release_db
3337         unlink($fai_release_file_name);
3338         $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
3339         chmod(0640, $fai_release_file_name);
3340         chown($root_uid, $adm_gid, $fai_release_file_name);
3341         
3342         # connect to packages_list_db
3343         #unlink($packages_list_file_name);
3344         unlink($packages_list_under_construction);
3345         $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
3346         chmod(0640, $packages_list_file_name);
3347         chown($root_uid, $adm_gid, $packages_list_file_name);
3348         
3349         # connect to messaging_db
3350         unlink($messaging_file_name);
3351         $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
3352         chmod(0640, $messaging_file_name);
3353         chown($root_uid, $adm_gid, $messaging_file_name);
3354     }
3358 # Creating tables
3359 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
3360 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
3361 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
3362 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
3363 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
3364 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
3365 $foreign_clients_db->create_table($foreign_clients_tn, \@foreign_clients_col_names);
3366 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
3367 $incoming_db->create_table($incoming_tn, \@incoming_col_names);
3368 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
3370 # create xml object used for en/decrypting
3371 $xml = new XML::Simple();
3374 # foreign servers 
3375 my @foreign_server_list;
3377 # add foreign server from cfg file
3378 if ($foreign_server_string ne "") {
3379     my @cfg_foreign_server_list = split(",", $foreign_server_string);
3380     foreach my $foreign_server (@cfg_foreign_server_list) {
3381         push(@foreign_server_list, $foreign_server);
3382     }
3384     daemon_log("0 INFO: found foreign server in config file: ".join(", ", @foreign_server_list), 5);
3387 # Perform a DNS lookup for server registration if flag is true
3388 if ($dns_lookup eq "true") {
3389     # Add foreign server from dns
3390     my @tmp_servers;
3391     if (not $server_domain) {
3392         # Try our DNS Searchlist
3393         for my $domain(get_dns_domains()) {
3394             chomp($domain);
3395             my ($tmp_domains, $error_string) = &get_server_addresses($domain);
3396             if(@$tmp_domains) {
3397                 for my $tmp_server(@$tmp_domains) {
3398                     push @tmp_servers, $tmp_server;
3399                 }
3400             }
3401         }
3402         if(@tmp_servers && length(@tmp_servers)==0) {
3403             daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3404         }
3405     } else {
3406         @tmp_servers = &get_server_addresses($server_domain);
3407         if( 0 == @tmp_servers ) {
3408             daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3409         }
3410     }
3412     daemon_log("0 INFO: found foreign server via DNS ".join(", ", @tmp_servers), 5);    
3414     foreach my $server (@tmp_servers) { 
3415         unshift(@foreign_server_list, $server); 
3416     }
3417 } else {
3418     daemon_log("0 INFO: DNS lookup for server registration is disabled", 5);
3422 # eliminate duplicate entries
3423 @foreign_server_list = &del_doubles(@foreign_server_list);
3424 my $all_foreign_server = join(", ", @foreign_server_list);
3425 daemon_log("0 INFO: found foreign server in config file and DNS: '$all_foreign_server'", 5);
3427 # add all found foreign servers to known_server
3428 my $cur_timestamp = &get_time();
3429 foreach my $foreign_server (@foreign_server_list) {
3431         # do not add myself to known_server_db
3432         if (&is_local($foreign_server)) { next; }
3433         ######################################
3435     my $res = $known_server_db->add_dbentry( {table=>$known_server_tn, 
3436             primkey=>['hostname'],
3437             hostname=>$foreign_server,
3438             macaddress=>"",
3439             status=>'not_yet_registered',
3440             hostkey=>"none",
3441             loaded_modules => "none", 
3442             timestamp=>$cur_timestamp,
3443             } );
3447 # Import all modules
3448 &import_modules;
3450 # Check wether all modules are gosa-si valid passwd check
3451 &password_check;
3453 # Prepare for using Opsi 
3454 if ($opsi_enabled eq "true") {
3455     use JSON::RPC::Client;
3456     use XML::Quote qw(:all);
3457     $opsi_url= "https://".$opsi_admin.":".$opsi_password."@".$opsi_server.":4447/rpc";
3458     $opsi_client = new JSON::RPC::Client;
3462 POE::Component::Server::TCP->new(
3463         Alias => "TCP_SERVER",
3464         Port => $server_port,
3465         ClientInput => sub {
3466                 my ($kernel, $input, $heap, $session) = @_[KERNEL, ARG0, HEAP, SESSION];
3467         my $session_id = $session->ID;
3468         my $remote_ip = $heap->{'remote_ip'};
3469                 push(@msgs_to_decrypt, $input);
3470         &daemon_log("$session_id DEBUG: incoming message from '$remote_ip'", 7);
3471                 $kernel->yield("msg_to_decrypt");
3472         },
3473         InlineStates => {
3474                 msg_to_decrypt => \&msg_to_decrypt,
3475                 next_task => \&next_task,
3476                 task_result => \&handle_task_result,
3477                 task_done   => \&handle_task_done,
3478                 task_debug  => \&handle_task_debug,
3479                 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3480         }
3481 );
3483 daemon_log("0 INFO: start socket for incoming xml messages at port '$server_port' ", 1);
3485 # create session for repeatedly checking the job queue for jobs
3486 POE::Session->create(
3487         inline_states => {
3488                 _start => \&session_start,
3489         register_at_foreign_servers => \&register_at_foreign_servers,
3490         sig_handler => \&sig_handler,
3491         next_task => \&next_task,
3492         task_result => \&handle_task_result,
3493         task_done   => \&handle_task_done,
3494         task_debug  => \&handle_task_debug,
3495         watch_for_next_tasks => \&watch_for_next_tasks,
3496         watch_for_new_messages => \&watch_for_new_messages,
3497         watch_for_delivery_messages => \&watch_for_delivery_messages,
3498         watch_for_done_messages => \&watch_for_done_messages,
3499                 watch_for_new_jobs => \&watch_for_new_jobs,
3500         watch_for_modified_jobs => \&watch_for_modified_jobs,
3501         watch_for_done_jobs => \&watch_for_done_jobs,
3502         watch_for_opsi_jobs => \&watch_for_opsi_jobs,
3503         watch_for_old_known_clients => \&watch_for_old_known_clients,
3504         create_packages_list_db => \&run_create_packages_list_db,
3505         create_fai_server_db => \&run_create_fai_server_db,
3506         create_fai_release_db => \&run_create_fai_release_db,
3507                 recreate_packages_db => \&run_recreate_packages_db,
3508         session_run_result => \&session_run_result,
3509         session_run_debug => \&session_run_debug,
3510         session_run_done => \&session_run_done,
3511         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3512         }
3513 );
3516 POE::Kernel->run();
3517 exit;