Code

- Added Benoit Mortier as uploaders
[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 $known_functions;
109 our $root_uid;
110 our $adm_gid;
112 # if foreground is not null, script will be not forked to background
113 $foreground = 0 ;
115 # specifies the timeout seconds while checking the online status of a registrating client
116 $ping_timeout = 5;
118 $no_arp = 0;
119 my $packages_list_under_construction = "/tmp/packages_list_creation_in_progress";
120 my @packages_list_statements;
121 my $watch_for_new_jobs_in_progress = 0;
123 # holds all incoming decrypted messages
124 our $incoming_db;
125 our $incoming_tn = 'incoming';
126 my $incoming_file_name;
127 my @incoming_col_names = ("id INTEGER PRIMARY KEY",
128         "timestamp VARCHAR(14) DEFAULT 'none'", 
129         "headertag VARCHAR(255) DEFAULT 'none'",
130         "targettag VARCHAR(255) DEFAULT 'none'",
131         "xmlmessage TEXT",
132         "module VARCHAR(255) DEFAULT 'none'",
133         "sessionid VARCHAR(255) DEFAULT '0'",
134 );
136 # holds all gosa jobs
137 our $job_db;
138 our $job_queue_tn = 'jobs';
139 my $job_queue_file_name;
140 my @job_queue_col_names = ("id INTEGER PRIMARY KEY",
141         "timestamp VARCHAR(14) DEFAULT 'none'", 
142         "status VARCHAR(255) DEFAULT 'none'", 
143         "result TEXT",
144         "progress VARCHAR(255) DEFAULT 'none'",
145         "headertag VARCHAR(255) DEFAULT 'none'",
146         "targettag VARCHAR(255) DEFAULT 'none'", 
147         "xmlmessage TEXT", 
148         "macaddress VARCHAR(17) DEFAULT 'none'",
149         "plainname VARCHAR(255) DEFAULT 'none'",
150         "siserver VARCHAR(255) DEFAULT 'none'",
151         "modified INTEGER DEFAULT '0'",
152 );
154 # holds all other gosa-si-server
155 our $known_server_db;
156 our $known_server_tn = "known_server";
157 my $known_server_file_name;
158 my @known_server_col_names = ("hostname VARCHAR(255)", "macaddress VARCHAR(17)", "status VARCHAR(255)", "hostkey VARCHAR(255)", "loaded_modules TEXT", "timestamp VARCHAR(14)", "update_time VARCHAR(14)");
160 # holds all registrated clients
161 our $known_clients_db;
162 our $known_clients_tn = "known_clients";
163 my $known_clients_file_name;
164 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)");
166 # holds all registered clients at a foreign server
167 our $foreign_clients_db;
168 our $foreign_clients_tn = "foreign_clients"; 
169 my $foreign_clients_file_name;
170 my @foreign_clients_col_names = ("hostname VARCHAR(255)", "macaddress VARCHAR(17)", "regserver VARCHAR(255)", "timestamp VARCHAR(14)");
172 # holds all logged in user at each client 
173 our $login_users_db;
174 our $login_users_tn = "login_users";
175 my $login_users_file_name;
176 my @login_users_col_names = ("client VARCHAR(255)", "user VARCHAR(255)", "timestamp VARCHAR(14)", "regserver VARCHAR(255) DEFAULT 'localhost'");
178 # holds all fai server, the debian release and tag
179 our $fai_server_db;
180 our $fai_server_tn = "fai_server"; 
181 my $fai_server_file_name;
182 our @fai_server_col_names = ("timestamp VARCHAR(14)", "server VARCHAR(255)", "fai_release VARCHAR(255)", "sections VARCHAR(255)", "tag VARCHAR(255)"); 
184 our $fai_release_db;
185 our $fai_release_tn = "fai_release"; 
186 my $fai_release_file_name;
187 our @fai_release_col_names = ("timestamp VARCHAR(14)", "fai_release VARCHAR(255)", "class VARCHAR(255)", "type VARCHAR(255)", "state VARCHAR(255)"); 
189 # holds all packages available from different repositories
190 our $packages_list_db;
191 our $packages_list_tn = "packages_list";
192 my $packages_list_file_name;
193 our @packages_list_col_names = ("distribution VARCHAR(255)", "package VARCHAR(255)", "version VARCHAR(255)", "section VARCHAR(255)", "description TEXT", "template LONGBLOB", "timestamp VARCHAR(14)");
194 my $outdir = "/tmp/packages_list_db";
195 my $arch = "i386"; 
197 # holds all messages which should be delivered to a user
198 our $messaging_db;
199 our $messaging_tn = "messaging"; 
200 our @messaging_col_names = ("id INTEGER", "subject TEXT", "message_from VARCHAR(255)", "message_to VARCHAR(255)", 
201         "flag VARCHAR(255)", "direction VARCHAR(255)", "delivery_time VARCHAR(255)", "message TEXT", "timestamp VARCHAR(14)" );
202 my $messaging_file_name;
204 # path to directory to store client install log files
205 our $client_fai_log_dir = "/var/log/fai"; 
207 # queue which stores taskes until one of the $max_children children are ready to process the task
208 #my @tasks = qw();
209 my @msgs_to_decrypt = qw();
210 my $max_children = 2;
213 # loop delay for job queue to look for opsi jobs
214 my $job_queue_opsi_delay = 10;
215 our $opsi_client;
216 our $opsi_url;
217  
218 # Lifetime of logged in user information. If no update information comes after n seconds, 
219 # the user is expeceted to be no longer logged in or the host is no longer running. Because
220 # of this, the user is deleted from login_users_db
221 our $logged_in_user_date_of_expiry = 600;
224 %cfg_defaults = (
225 "general" => {
226     "log-file" => [\$log_file, "/var/run/".$prg.".log"],
227     "pid-file" => [\$pid_file, "/var/run/".$prg.".pid"],
228     },
229 "server" => {
230     "ip"                    => [\$server_ip, "0.0.0.0"],
231     "port"                  => [\$server_port, "20081"],
232     "known-clients"         => [\$known_clients_file_name, '/var/lib/gosa-si/clients.db' ],
233     "known-servers"         => [\$known_server_file_name, '/var/lib/gosa-si/servers.db'],
234     "incoming"              => [\$incoming_file_name, '/var/lib/gosa-si/incoming.db'],
235     "login-users"           => [\$login_users_file_name, '/var/lib/gosa-si/users.db'],
236     "fai-server"            => [\$fai_server_file_name, '/var/lib/gosa-si/fai_server.db'],
237     "fai-release"           => [\$fai_release_file_name, '/var/lib/gosa-si/fai_release.db'],
238     "packages-list"         => [\$packages_list_file_name, '/var/lib/gosa-si/packages.db'],
239     "messaging"             => [\$messaging_file_name, '/var/lib/gosa-si/messaging.db'],
240     "foreign-clients"       => [\$foreign_clients_file_name, '/var/lib/gosa-si/foreign_clients.db'],
241     "source-list"           => [\$sources_list, '/etc/apt/sources.list'],
242     "repo-path"             => [\$repo_path, '/srv/www/repository'],
243     "ldap-uri"              => [\$ldap_uri, ""],
244     "ldap-base"             => [\$ldap_base, ""],
245     "ldap-admin-dn"         => [\$ldap_admin_dn, ""],
246     "ldap-admin-password"   => [\$ldap_admin_password, ""],
247     "ldap-version"          => [\$ldap_version, 3],
248     "gosa-unit-tag"         => [\$gosa_unit_tag, ""],
249     "max-clients"           => [\$max_clients, 10],
250     "wol-password"          => [\$wake_on_lan_passwd, ""],
251         "mysql-username"        => [\$mysql_username, "gosa_si"],
252         "mysql-password"        => [\$mysql_password, ""],
253         "mysql-database"        => [\$mysql_database, "gosa_si"],
254         "mysql-host"            => [\$mysql_host, "127.0.0.1"],
255     },
256 "GOsaPackages" => {
257     "job-queue" => [\$job_queue_file_name, '/var/lib/gosa-si/jobs.db'],
258     "job-queue-loop-delay" => [\$job_queue_loop_delay, 3],
259     "messaging-db-loop-delay" => [\$messaging_db_loop_delay, 3],
260     "key" => [\$GosaPackages_key, "none"],
261                 "new-systems-ou" => [\$new_systems_ou, 'ou=workstations,ou=systems'],
262     },
263 "ClientPackages" => {
264     "key" => [\$ClientPackages_key, "none"],
265     "user-date-of-expiry" => [\$logged_in_user_date_of_expiry, 600],
266     },
267 "ServerPackages"=> {
268     "address"      => [\$foreign_server_string, ""],
269     "dns-lookup"            => [\$dns_lookup, "true"],
270     "domain"  => [\$server_domain, ""],
271     "key"     => [\$ServerPackages_key, "none"],
272     "key-lifetime" => [\$foreign_servers_register_delay, 120],
273     "job-synchronization-enabled" => [\$job_synchronization, "true"],
274     "synchronization-loop" => [\$modified_jobs_loop_delay, 5],
275     },
276 "ArpHandler" => {
277     "enabled"   => [\$arp_enabled, "true"],
278     "interface" => [\$arp_interface, "all"],
279         },
280 "Opsi" => {
281     "enabled"  => [\$opsi_enabled, "false"], 
282     "server"   => [\$opsi_server, "localhost"],
283     "admin"    => [\$opsi_admin, "opsi-admin"],
284     "password" => [\$opsi_password, "secret"],
285    },
287 );
290 #===  FUNCTION  ================================================================
291 #         NAME:  usage
292 #   PARAMETERS:  nothing
293 #      RETURNS:  nothing
294 #  DESCRIPTION:  print out usage text to STDERR
295 #===============================================================================
296 sub usage {
297     print STDERR << "EOF" ;
298 usage: $prg [-hvf] [-c config]
300            -h        : this (help) message
301            -c <file> : config file
302            -f        : foreground, process will not be forked to background
303            -v        : be verbose (multiple to increase verbosity)
304            -no-arp   : starts $prg without connection to arp module
305  
306 EOF
307     print "\n" ;
311 #===  FUNCTION  ================================================================
312 #         NAME:  logging
313 #   PARAMETERS:  level - string - default 'info'
314 #                msg - string -
315 #                facility - string - default 'LOG_DAEMON'
316 #      RETURNS:  nothing
317 #  DESCRIPTION:  function for logging
318 #===============================================================================
319 sub daemon_log {
320     # log into log_file
321     my( $msg, $level ) = @_;
322     if(not defined $msg) { return }
323     if(not defined $level) { $level = 1 }
324                 if($level > $verbose) { return }
325     if(defined $log_file){
326         my $open_log_fh = sysopen(LOG_HANDLE, $log_file, O_WRONLY | O_CREAT | O_APPEND , 0440);
327         if(not $open_log_fh) {
328             print STDERR "cannot open $log_file: $!";
329             return;
330         }
331         # check owner and group of log_file and update settings if necessary
332         my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = stat($log_file);
333         if((not $uid eq $root_uid) || (not $gid eq $adm_gid)) {
334             chown($root_uid, $adm_gid, $log_file);
335                 }
337         chomp($msg);
338         #$msg =~s/\n//g;   # no newlines are allowed in log messages, this is important for later log parsing
339         if($level <= $verbose){
340             my ($seconds, $minutes, $hours, $monthday, $month,
341                     $year, $weekday, $yearday, $sommertime) = localtime(time);
342             $hours = $hours < 10 ? $hours = "0".$hours : $hours;
343             $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
344             $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
345             my @monthnames = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
346             $month = $monthnames[$month];
347             $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
348             $year+=1900;
349             my $name = $prg;
351             my $log_msg = "$month $monthday $hours:$minutes:$seconds $name $msg\n";
352                         flock(LOG_HANDLE, LOCK_EX);
353                         seek(LOG_HANDLE, 0, 2);
354             print LOG_HANDLE $log_msg;
355                         flock(LOG_HANDLE, LOCK_UN);
356             if( $foreground ) { 
357                 print STDERR $log_msg;
358             }
359         }
360         close( LOG_HANDLE );
361     }
365 #===  FUNCTION  ================================================================
366 #         NAME:  check_cmdline_param
367 #   PARAMETERS:  nothing
368 #      RETURNS:  nothing
369 #  DESCRIPTION:  validates commandline parameter
370 #===============================================================================
371 sub check_cmdline_param () {
372     my $err_config;
373     my $err_counter = 0;
374         if(not defined($cfg_file)) {
375                 $cfg_file = "/etc/gosa-si/server.conf";
376                 if(! -r $cfg_file) {
377                         $err_config = "please specify a config file";
378                         $err_counter += 1;
379                 }
380     }
381     if( $err_counter > 0 ) {
382         &usage( "", 1 );
383         if( defined( $err_config)) { print STDERR "$err_config\n"}
384         print STDERR "\n";
385         exit( -1 );
386     }
390 #===  FUNCTION  ================================================================
391 #         NAME:  check_pid
392 #   PARAMETERS:  nothing
393 #      RETURNS:  nothing
394 #  DESCRIPTION:  handels pid processing
395 #===============================================================================
396 sub check_pid {
397     $pid = -1;
398     # Check, if we are already running
399     if( open(LOCK_FILE, "<$pid_file") ) {
400         $pid = <LOCK_FILE>;
401         if( defined $pid ) {
402             chomp( $pid );
403             if( -f "/proc/$pid/stat" ) {
404                 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
405                 if( $stat ) {
406                                         print STDERR "\nERROR: Already running!\n";
407                     close( LOCK_FILE );
408                     exit -1;
409                 }
410             }
411         }
412         close( LOCK_FILE );
413         unlink( $pid_file );
414     }
416     # create a syslog msg if it is not to possible to open PID file
417     if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
418         my($msg) = "Couldn't obtain lockfile '$pid_file' ";
419         if (open(LOCK_FILE, '<', $pid_file)
420                 && ($pid = <LOCK_FILE>))
421         {
422             chomp($pid);
423             $msg .= "(PID $pid)\n";
424         } else {
425             $msg .= "(unable to read PID)\n";
426         }
427         if( ! ($foreground) ) {
428             openlog( $0, "cons,pid", "daemon" );
429             syslog( "warning", $msg );
430             closelog();
431         }
432         else {
433             print( STDERR " $msg " );
434         }
435         exit( -1 );
436     }
439 #===  FUNCTION  ================================================================
440 #         NAME:  import_modules
441 #   PARAMETERS:  module_path - string - abs. path to the directory the modules 
442 #                are stored
443 #      RETURNS:  nothing
444 #  DESCRIPTION:  each file in module_path which ends with '.pm' and activation 
445 #                state is on is imported by "require 'file';"
446 #===============================================================================
447 sub import_modules {
448     if (not -e $modules_path) {
449         daemon_log("0 ERROR: cannot find directory or directory is not readable: $modules_path", 1);   
450     }
452     opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
454     while (defined (my $file = readdir (DIR))) {
455         if (not $file =~ /(\S*?).pm$/) {
456             next;
457         }
458                 my $mod_name = $1;
460         # ArpHandler switch
461         if( $file =~ /ArpHandler.pm/ ) {
462             if( $arp_enabled eq "false" ) { next; }
463         }
464         
465         eval { require $file; };
466         if ($@) {
467             daemon_log("0 ERROR: gosa-si-server could not load module $file", 1);
468             daemon_log("$@", 1);
469             exit;
470                 } else {
471                         my $info = eval($mod_name.'::get_module_info()');
472                         # Only load module if get_module_info() returns a non-null object
473                         if( $info ) {
474                                 my ($input_address, $input_key, $event_hash) = @{$info};
475                                 $known_modules->{$mod_name} = $info;
476                                 daemon_log("0 INFO: module $mod_name loaded", 5);
477                         }
478                 }
479     }   
480     close (DIR);
483 #===  FUNCTION  ================================================================
484 #         NAME:  password_check
485 #   PARAMETERS:  nothing
486 #      RETURNS:  nothing
487 #  DESCRIPTION:  escalates an critical error if two modules exist which are avaialable by 
488 #                the same password
489 #===============================================================================
490 sub password_check {
491     my $passwd_hash = {};
492     while (my ($mod_name, $mod_info) = each %$known_modules) {
493         my $mod_passwd = @$mod_info[1];
494         if (not defined $mod_passwd) { next; }
495         if (not exists $passwd_hash->{$mod_passwd}) {
496             $passwd_hash->{$mod_passwd} = $mod_name;
498         # escalates critical error
499         } else {
500             &daemon_log("0 ERROR: two loaded modules do have the same password. Please modify the 'key'-parameter in config file");
501             &daemon_log("0 ERROR: module='$mod_name' and module='".$passwd_hash->{$mod_passwd}."'");
502             exit( -1 );
503         }
504     }
509 #===  FUNCTION  ================================================================
510 #         NAME:  sig_int_handler
511 #   PARAMETERS:  signal - string - signal arose from system
512 #      RETURNS:  nothing
513 #  DESCRIPTION:  handels tasks to be done befor signal becomes active
514 #===============================================================================
515 sub sig_int_handler {
516     my ($signal) = @_;
518 #       if (defined($ldap_handle)) {
519 #               $ldap_handle->disconnect;
520 #       }
521     # TODO alle verbliebenden ldap verbindungen aus allen heaps beenden
522     
524     daemon_log("shutting down gosa-si-server", 1);
525     system("kill `ps -C gosa-si-server -o pid=`");
527 $SIG{INT} = \&sig_int_handler;
530 sub check_key_and_xml_validity {
531     my ($crypted_msg, $module_key, $session_id) = @_;
532     my $msg;
533     my $msg_hash;
534     my $error_string;
535     eval{
536         $msg = &decrypt_msg($crypted_msg, $module_key);
538         if ($msg =~ /<xml>/i){
539             $msg =~ s/\s+/ /g;  # just for better daemon_log
540             daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 9);
541             $msg_hash = $xml->XMLin($msg, ForceArray=>1);
543             ##############
544             # check header
545             if( not exists $msg_hash->{'header'} ) { die "no header specified"; }
546             my $header_l = $msg_hash->{'header'};
547             if( (1 > @{$header_l}) || ( ( 'HASH' eq ref @{$header_l}[0]) && (1 > keys %{@{$header_l}[0]}) ) ) { die 'empty header tag'; }
548             if( 1 < @{$header_l} ) { die 'more than one header specified'; }
549             my $header = @{$header_l}[0];
550             if( 0 == length $header) { die 'empty string in header tag'; }
552             ##############
553             # check source
554             if( not exists $msg_hash->{'source'} ) { die "no source specified"; }
555             my $source_l = $msg_hash->{'source'};
556             if( (1 > @{$source_l}) || ( ( 'HASH' eq ref @{$source_l}[0]) && (1 > keys %{@{$source_l}[0]}) ) ) { die 'empty source tag'; }
557             if( 1 < @{$source_l} ) { die 'more than one source specified'; }
558             my $source = @{$source_l}[0];
559             if( 0 == length $source) { die 'source error'; }
561             ##############
562             # check target
563             if( not exists $msg_hash->{'target'} ) { die "no target specified"; }
564             my $target_l = $msg_hash->{'target'};
565             if( (1 > @{$target_l}) || ( ('HASH' eq ref @{$target_l}[0]) && (1 > keys %{@{$target_l}[0]}) ) ) { die 'empty target tag'; }
566         }
567     };
568     if($@) {
569         daemon_log("$session_id ERROR: do not understand the message: $@", 1);
570         $msg = undef;
571         $msg_hash = undef;
572     }
574     return ($msg, $msg_hash);
578 sub check_outgoing_xml_validity {
579     my ($msg, $session_id) = @_;
581     my $msg_hash;
582     eval{
583         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
585         ##############
586         # check header
587         my $header_l = $msg_hash->{'header'};
588         if( 1 != @{$header_l} ) {
589             die 'no or more than one headers specified';
590         }
591         my $header = @{$header_l}[0];
592         if( 0 == length $header) {
593             die 'header has length 0';
594         }
596         ##############
597         # check source
598         my $source_l = $msg_hash->{'source'};
599         if( 1 != @{$source_l} ) {
600             die 'no or more than 1 sources specified';
601         }
602         my $source = @{$source_l}[0];
603         if( 0 == length $source) {
604             die 'source has length 0';
605         }
607                                 # Check if source contains hostname instead of ip address
608                                 if($source =~ /^[a-z][a-z0-9\.]+:\d+$/i) {
609                                                 my ($hostname,$port) = split(/:/, $source);
610                                                 my $ip_address = inet_ntoa(scalar gethostbyname($hostname));
611                                                 if(defined($ip_address) && $ip_address =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/ && $port =~ /^\d+$/) {
612                                                         # Write ip address to $source variable
613                                                         $source = "$ip_address:$port";
614                                                 }
615                                 }
616         unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
617                 $source =~ /^GOSA$/i) {
618             die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
619         }
620         
621         ##############
622         # check target  
623         my $target_l = $msg_hash->{'target'};
624         if( 0 == @{$target_l} ) {
625             die "no targets specified";
626         }
627         foreach my $target (@$target_l) {
628             if( 0 == length $target) {
629                 die "target has length 0";
630             }
631             unless( $target =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
632                     $target =~ /^GOSA$/i ||
633                     $target =~ /^\*$/ ||
634                     $target =~ /KNOWN_SERVER/i ||
635                     $target =~ /JOBDB/i ||
636                     $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 ){
637                 die "target '$target' is not a complete ip-address with port or a valid target name or a mac-address";
638             }
639         }
640     };
641     if($@) {
642         daemon_log("$session_id ERROR: outgoing msg is not gosa-si envelope conform: $@", 1);
643         daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 1);
644         $msg_hash = undef;
645     }
647     return ($msg_hash);
651 sub input_from_known_server {
652     my ($input, $remote_ip, $session_id) = @_ ;  
653     my ($msg, $msg_hash, $module);
655     my $sql_statement= "SELECT * FROM known_server";
656     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
658     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
659         my $host_name = $hit->{hostname};
660         if( not $host_name =~ "^$remote_ip") {
661             next;
662         }
663         my $host_key = $hit->{hostkey};
664         #daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 7);
665         #daemon_log("$session_id DEBUG: input_from_known_server: host_key: $host_key", 7);
667         # check if module can open msg envelope with module key
668         my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
669         if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
670             daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 7);
671             daemon_log("$@", 8);
672             next;
673         }
674         else {
675             $msg = $tmp_msg;
676             $msg_hash = $tmp_msg_hash;
677             $module = "ServerPackages";
678             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 7);
679             last;
680         }
681     }
683     if( (!$msg) || (!$msg_hash) || (!$module) ) {
684         #daemon_log("$session_id DEBUG: Incoming message is not from a known server", 7);
685     }
686   
687     return ($msg, $msg_hash, $module);
691 sub input_from_known_client {
692     my ($input, $remote_ip, $session_id) = @_ ;  
693     my ($msg, $msg_hash, $module);
695     my $sql_statement= "SELECT * FROM known_clients";
696     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
697     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
698         my $host_name = $hit->{hostname};
699         if( not $host_name =~ /^$remote_ip/) {
700                 next;
701                 }
702         my $host_key = $hit->{hostkey};
703         #&daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 7);
704         #&daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 7);
706         # check if module can open msg envelope with module key
707         ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
709         if( (!$msg) || (!$msg_hash) ) {
710             &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 7);
711             next;
712         }
713         else {
714             $module = "ClientPackages";
715             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 7);
716             last;
717         }
718     }
720     if( (!$msg) || (!$msg_hash) || (!$module) ) {
721         #&daemon_log("$session_id DEBUG: Incoming message is not from a known client", 7);
722     }
724     return ($msg, $msg_hash, $module);
728 sub input_from_unknown_host {
729         no strict "refs";
730         my ($input, $session_id) = @_ ;
731         my ($msg, $msg_hash, $module);
732         my $error_string;
734         my %act_modules = %$known_modules;
736         while( my ($mod, $info) = each(%act_modules)) {
738                 # check a key exists for this module
739                 my $module_key = ${$mod."_key"};
740                 if( not defined $module_key ) {
741                         if( $mod eq 'ArpHandler' ) {
742                                 next;
743                         }
744                         daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
745                         next;
746                 }
747                 #daemon_log("$session_id DEBUG: $mod: $module_key", 7);
749                 # check if module can open msg envelope with module key
750                 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
751                 if( (not defined $msg) || (not defined $msg_hash) ) {
752                         next;
753                 } else {
754                         $module = $mod;
755             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 7);
756                         last;
757                 }
758         }
760         if( (!$msg) || (!$msg_hash) || (!$module)) {
761                 #daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 7);
762         }
764         return ($msg, $msg_hash, $module);
768 sub create_ciphering {
769     my ($passwd) = @_;
770         if((!defined($passwd)) || length($passwd)==0) {
771                 $passwd = "";
772         }
773     $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
774     my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
775     my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
776     $my_cipher->set_iv($iv);
777     return $my_cipher;
781 sub encrypt_msg {
782     my ($msg, $key) = @_;
783     my $my_cipher = &create_ciphering($key);
784     my $len;
785     {
786             use bytes;
787             $len= 16-length($msg)%16;
788     }
789     $msg = "\0"x($len).$msg;
790     $msg = $my_cipher->encrypt($msg);
791     chomp($msg = &encode_base64($msg));
792     # there are no newlines allowed inside msg
793     $msg=~ s/\n//g;
794     return $msg;
798 sub decrypt_msg {
800     my ($msg, $key) = @_ ;
801     $msg = &decode_base64($msg);
802     my $my_cipher = &create_ciphering($key);
803     $msg = $my_cipher->decrypt($msg); 
804     $msg =~ s/\0*//g;
805     return $msg;
809 sub get_encrypt_key {
810     my ($target) = @_ ;
811     my $encrypt_key;
812     my $error = 0;
814     # target can be in known_server
815     if( not defined $encrypt_key ) {
816         my $sql_statement= "SELECT * FROM known_server WHERE hostname='$target'";
817         my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
818         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
819             my $host_name = $hit->{hostname};
820             if( $host_name ne $target ) {
821                 next;
822             }
823             $encrypt_key = $hit->{hostkey};
824             last;
825         }
826     }
828     # target can be in known_client
829     if( not defined $encrypt_key ) {
830         my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$target'";
831         my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
832         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
833             my $host_name = $hit->{hostname};
834             if( $host_name ne $target ) {
835                 next;
836             }
837             $encrypt_key = $hit->{hostkey};
838             last;
839         }
840     }
842     return $encrypt_key;
846 #===  FUNCTION  ================================================================
847 #         NAME:  open_socket
848 #   PARAMETERS:  PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
849 #                [PeerPort] string necessary if port not appended by PeerAddr
850 #      RETURNS:  socket IO::Socket::INET
851 #  DESCRIPTION:  open a socket to PeerAddr
852 #===============================================================================
853 sub open_socket {
854     my ($PeerAddr, $PeerPort) = @_ ;
855     if(defined($PeerPort)){
856         $PeerAddr = $PeerAddr.":".$PeerPort;
857     }
858     my $socket;
859     $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
860             Porto => "tcp",
861             Type => SOCK_STREAM,
862             Timeout => 5,
863             );
864     if(not defined $socket) {
865         return;
866     }
867 #    &daemon_log("DEBUG: open_socket: $PeerAddr", 7);
868     return $socket;
872 sub send_msg_to_target {
873     my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
874     my $error = 0;
875     my $header;
876     my $timestamp = &get_time();
877     my $new_status;
878     my $act_status;
879     my ($sql_statement, $res);
880   
881     if( $msg_header ) {
882         $header = "'$msg_header'-";
883     } else {
884         $header = "";
885     }
887         # Memorize own source address
888         my $own_source_address = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
889         $own_source_address .= ":".$server_port;
891         # Patch 0.0.0.0 source to real address
892         $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$own_source_address<\/source>/s;
893         # Patch GOSA source to real address and add forward_to_gosa tag
894         $msg =~ s/<source>GOSA<\/source>/<source>$own_source_address<\/source> <forward_to_gosa>$own_source_address,$session_id<\/forward_to_gosa>/ ;
896     # encrypt xml msg
897     my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
899     # opensocket
900     my $socket = &open_socket($address);
901     if( !$socket ) {
902         daemon_log("$session_id ERROR: Cannot open socket to host '$address'. Message processing aborted!", 1);
903         $error++;
904     }
905     
906     if( $error == 0 ) {
907         # send xml msg
908         print $socket $crypted_msg.";$own_source_address\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.", 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                 if (not defined $msg_source) 
1075                 {
1076                         # Only needed, to be compatible with older gosa-si-server versions
1077                         ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $heap->{'remote_ip'}, $session_id);
1078                 }
1079                 else
1080                 {
1081                         ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $msg_source, $session_id);
1082                 }
1083         }
1084         # msg is from a gosa-si-client
1085         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1086                 ($msg, $msg_hash, $module) = &input_from_known_client($next_msg, $msg_source, $session_id);
1087         }
1088         # an error occurred
1089         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1090                 # If an incoming msg could not be decrypted (maybe a wrong key), decide if msg comes from a client 
1091                 # or a server.  In case of a client, send a ping. If the client could not understand a msg from its 
1092                 # server the client cause a re-registering process. In case of a server, decrease update_time in kown_server_db
1093                 # and trigger a re-registering process for servers
1094                 if (defined $msg_source && $msg_source =~ /:$server_port$/)
1095                 {
1096                         daemon_log("$session_id WARNING: Cannot understand incoming msg from server '$msg_source'. Cause re-registration process for servers.", 3);
1097                         my $update_statement = "UPDATE $known_server_tn SET update_time='19700101000000' WHERE hostname='$msg_source'"; 
1098                         daemon_log("$session_id DEBUG: $update_statement", 7);
1099                         my $upadte_res = $known_server_db->exec_statement($update_statement);
1100                         $kernel->yield("register_at_foreign_servers");
1101                 }
1102                 elsif (defined $msg_source)
1103                 {
1104                         daemon_log("$session_id WARNING: Cannot understand incoming msg from client '$msg_source'. Send ping-msg to cause a re-registering of the client if necessary", 3);
1105                         #my $remote_ip = $heap->{'remote_ip'};
1106                         #my $remote_port = $heap->{'remote_port'};
1107                         my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source><target>$msg_source</target></xml>";
1108                         my ($test_error, $test_error_string) = &send_msg_to_target($ping_msg, "$msg_source", "dummy-key", "gosa_ping", $session_id);
1109                         daemon_log("$session_id WARNING: sending msg to cause re-registering: $ping_msg", 3);
1110                 }
1111                 else
1112                 {
1113                         my $foreign_host = defined $msg_source ? $msg_source : $heap->{'remote_ip'};
1114                         daemon_log("$session_id ERROR: incoming message from host '$foreign_host' cannot be understood. Processing aborted: $tmp_next_msg", 1);
1115                 }
1117                 $error++;
1118         }
1121         my $header;
1122         my $target;
1123         my $source;
1124         my $done = 0;
1125         my $sql;
1126         my $res;
1128         # check whether this message should be processed here
1129         if ($error == 0) {
1130                 $header = @{$msg_hash->{'header'}}[0];
1131                 $target = @{$msg_hash->{'target'}}[0];
1132                 $source = @{$msg_hash->{'source'}}[0];
1133                 my $not_found_in_known_clients_db = 0;
1134                 my $not_found_in_known_server_db = 0;
1135                 my $not_found_in_foreign_clients_db = 0;
1136                 my $local_address;
1137                 my $local_mac;
1138                 my ($target_ip, $target_port) = split(':', $target);
1140                 # Determine the local ip address if target is an ip address
1141                 if ($target =~ /^\d+\.\d+\.\d+\.\d+:\d+$/) {
1142                         $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1143                 } else {
1144                         $local_address = $server_address;
1145                 }
1147                 # Determine the local mac address if target is a mac address
1148                 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) {
1149                         my $loc_ip = &get_local_ip_for_remote_ip($heap->{'remote_ip'});
1150                         my $network_interface= &get_interface_for_ip($loc_ip);
1151                         $local_mac = &get_mac_for_interface($network_interface);
1152                 } else {
1153                         $local_mac = $server_mac_address;
1154                 }
1156                 # target and source is equal to GOSA -> process here
1157                 if (not $done) {
1158                         if ($target eq "GOSA" && $source eq "GOSA") {
1159                                 $done = 1;                    
1160                                 &daemon_log("$session_id DEBUG: target and source is 'GOSA' -> process here", 7);
1161                         }
1162                 }
1164                 # target is own address without forward_to_gosa-tag -> process here
1165                 if (not $done) {
1166                         #if ((($target eq $local_address) || ($target eq $local_mac) ) && (not exists $msg_hash->{'forward_to_gosa'})) {
1167                         if (($target eq $local_address) && (not exists $msg_hash->{'forward_to_gosa'})) {
1168                                 $done = 1;
1169                                 if ($source eq "GOSA") {
1170                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1171                                 }
1172                                 &daemon_log("$session_id DEBUG: target is own address without forward_to_gosa-tag -> process here", 7);
1173                         }
1174                 }
1176                 # target is own address with forward_to_gosa-tag not pointing to myself -> process here
1177                 if (not $done) {
1178                         my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1179                         my $gosa_at;
1180                         my $gosa_session_id;
1181                         if (($target eq $local_address) && (defined $forward_to_gosa)){
1182                                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1183                                 if ($gosa_at ne $local_address) {
1184                                         $done = 1;
1185                                         &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag not pointing to myself -> process here", 7); 
1186                                 }
1187                         }
1188                 }
1190                 # Target is a client address and there is a processing function within a plugin -> process loaclly
1191                 if (not $done)
1192                 {
1193                         # Check if target is a client address
1194                         $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')"; 
1195                         $res = $known_clients_db->select_dbentry($sql);
1196                         if ((keys(%$res) > 0) ) 
1197                         {
1198                                 my $hostname = $res->{1}->{'hostname'};
1199                                 my $reduced_header = $header;
1200                                 $reduced_header =~ s/gosa_//;
1201                                 # Check if there is a processing function within a plugin
1202                                 if (exists $known_functions->{$reduced_header}) 
1203                                 {
1204                                         $msg =~ s/<target>\S*<\/target>/<target>$hostname<\/target>/;
1205                                         $done = 1;
1206                                         &daemon_log("$session_id DEBUG: Target is client address with processing function within a plugin -> process here", 7);
1207                                 }
1208                         }
1209                 }
1211                 # If header has a 'job_' prefix, do always process message locally
1212                 # which means put it into job queue
1213                 if ((not $done) && ($header =~ /job_/))
1214                 {
1215                         $done = 1;
1216                         &daemon_log("$session_id DEBUG: Header has a 'job_' prefix. Put it into job queue. -> process here", 7);
1217                 }
1219                 # if message should be processed here -> add message to incoming_db
1220                 if ($done) {
1221                         # if a 'job_' or a 'gosa_' message comes from a foreign server, fake module from
1222                         # ServerPackages to GosaPackages so gosa-si-server knows how to process this kind of messages
1223                         if ($header =~ /^gosa_/ || $header =~ /^job_/) {
1224                                 $module = "GosaPackages";
1225                         }
1227                         my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1228                                         primkey=>[],
1229                                         headertag=>$header,
1230                                         targettag=>$target,
1231                                         xmlmessage=>&encode_base64($msg),
1232                                         timestamp=>&get_time,
1233                                         module=>$module,
1234                                         sessionid=>$session_id,
1235                                 } );
1237                 }
1239                 # target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa
1240                 if (not $done) {
1241                         my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1242                         my $gosa_at;
1243                         my $gosa_session_id;
1244                         if (($target eq $local_address) && (defined $forward_to_gosa)){
1245                                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1246                                 if ($gosa_at eq $local_address) {
1247                                         my $session_reference = $kernel->ID_id_to_session($gosa_session_id);
1248                                         if( defined $session_reference ) {
1249                                                 $heap = $session_reference->get_heap();
1250                                         }
1251                                         if(exists $heap->{'client'}) {
1252                                                 $msg = &encrypt_msg($msg, $GosaPackages_key);
1253                                                 $heap->{'client'}->put($msg);
1254                                                 &daemon_log("$session_id INFO: incoming '$header' message forwarded to GOsa", 5); 
1255                                         }
1256                                         $done = 1;
1257                                         &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa", 7);
1258                                 }
1259                         }
1261                 }
1263                 # target is a client address in known_clients -> forward to client
1264                 if (not $done) {
1265                         $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')"; 
1266                         $res = $known_clients_db->select_dbentry($sql);
1267                         if (keys(%$res) > 0) 
1268                         {
1269                                 $done = 1; 
1270                                 &daemon_log("$session_id DEBUG: target is a client address in known_clients -> forward to client", 7);
1271                                 my $hostkey = $res->{1}->{'hostkey'};
1272                                 my $hostname = $res->{1}->{'hostname'};
1273                                 $msg =~ s/<target>\S*<\/target>/<target>$hostname<\/target>/;
1274                                 $msg =~ s/<header>gosa_/<header>/;
1275                                 my $error= &send_msg_to_target($msg, $hostname, $hostkey, $header, $session_id);
1276                                 if ($error) {
1277                                         &daemon_log("$session_id ERROR: Some problems occurred while trying to send msg to client '$hostkey': $msg", 1);
1278                                 }
1279                         } 
1280                         else 
1281                         {
1282                                 $not_found_in_known_clients_db = 1;
1283                         }
1284                 }
1286                 # target is a client address in foreign_clients -> forward to registration server
1287                 if (not $done) {
1288                         $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1289                         $res = $foreign_clients_db->select_dbentry($sql);
1290                         if (keys(%$res) > 0) {
1291                                 my $hostname = $res->{1}->{'hostname'};
1292                                 my ($host_ip, $host_port) = split(/:/, $hostname);
1293                                 my $local_address =  &get_local_ip_for_remote_ip($host_ip).":$server_port";
1294                                 my $regserver = $res->{1}->{'regserver'};
1295                                 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$regserver'"; 
1296                                 my $res = $known_server_db->select_dbentry($sql);
1297                                 if (keys(%$res) > 0) {
1298                                         my $regserver_key = $res->{1}->{'hostkey'};
1299                                         $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1300                                         $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1301                                         if ($source eq "GOSA") {
1302                                                 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1303                                         }
1304                                         my $error= &send_msg_to_target($msg, $regserver, $regserver_key, $header, $session_id);
1305                                         if ($error) {
1306                                                 &daemon_log("$session_id ERROR: some problems occurred while trying to send msg to registration server: $msg", 1); 
1307                                         }
1308                                 }
1309                                 $done = 1;
1310                                 &daemon_log("$session_id DEBUG: target is a client address in foreign_clients -> forward to registration server", 7);
1311                         } else {
1312                                 $not_found_in_foreign_clients_db = 1;
1313                         }
1314                 }
1316                 # target is a server address -> forward to server
1317                 if (not $done) {
1318                         $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1319                         $res = $known_server_db->select_dbentry($sql);
1320                         if (keys(%$res) > 0) {
1321                                 my $hostkey = $res->{1}->{'hostkey'};
1323                                 if ($source eq "GOSA") {
1324                                         $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1325                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1327                                 }
1329                                 &send_msg_to_target($msg, $target, $hostkey, $header, $session_id);
1330                                 $done = 1;
1331                                 &daemon_log("$session_id DEBUG: target is a server address -> forward to server", 7);
1332                         } else {
1333                                 $not_found_in_known_server_db = 1;
1334                         }
1335                 }
1338                 # target is not in foreign_clients_db, known_server_db or known_clients_db, maybe it is a complete new one -> process here
1339                 if ( $not_found_in_foreign_clients_db 
1340                         && $not_found_in_known_server_db
1341                         && $not_found_in_known_clients_db) {
1342                         &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);
1343             if ($header =~ /^gosa_/ || $header =~ /^job_/) { 
1344                 $module = "GosaPackages"; 
1345             }
1346                         my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1347                                         primkey=>[],
1348                                         headertag=>$header,
1349                                         targettag=>$target,
1350                                         xmlmessage=>&encode_base64($msg),
1351                                         timestamp=>&get_time,
1352                                         module=>$module,
1353                                         sessionid=>$session_id,
1354                                 } );
1355                         $done = 1;
1356                 }
1359                 if (not $done) {
1360                         daemon_log("$session_id ERROR: do not know what to do with this message: $msg", 1);
1361                         if ($source eq "GOSA") {
1362                                 my %data = ('error_msg' => &encode_base64($msg), 'error_string' => "Do not know what to do with this message!");
1363                                 my $error_msg = &build_msg("error", $local_address, "GOSA", \%data ); 
1365                                 my $session_reference = $kernel->ID_id_to_session($session_id);
1366                                 if( defined $session_reference ) {
1367                                         $heap = $session_reference->get_heap();
1368                                 }
1369                                 if(exists $heap->{'client'}) {
1370                                         $error_msg = &encrypt_msg($error_msg, $GosaPackages_key);
1371                                         $heap->{'client'}->put($error_msg);
1372                                 }
1373                         }
1374                 }
1376         }
1378         return;
1382 sub next_task {
1383     my ($session, $heap, $task) = @_[SESSION, HEAP, ARG0, ARG1];
1384     my $running_task = POE::Wheel::Run->new(
1385             Program => sub { process_task($session, $heap, $task) },
1386             StdioFilter => POE::Filter::Reference->new(),
1387             StdoutEvent  => "task_result",
1388             StderrEvent  => "task_debug",
1389             CloseEvent   => "task_done",
1390             );
1391     $heap->{task}->{ $running_task->ID } = $running_task;
1394 sub handle_task_result {
1395     my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
1396     my $client_answer = $result->{'answer'};
1397     if( $client_answer =~ s/session_id=(\d+)$// ) {
1398         my $session_id = $1;
1399         if( defined $session_id ) {
1400             my $session_reference = $kernel->ID_id_to_session($session_id);
1401             if( defined $session_reference ) {
1402                 $heap = $session_reference->get_heap();
1403             }
1404         }
1406         if(exists $heap->{'client'}) {
1407             $heap->{'client'}->put($client_answer);
1408         }
1409     }
1410     $kernel->sig(CHLD => "child_reap");
1413 sub handle_task_debug {
1414     my $result = $_[ARG0];
1415     print STDERR "$result\n";
1418 sub handle_task_done {
1419     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1420     delete $heap->{task}->{$task_id};
1421         if (exists $heap->{ldap_handle}->{$task_id}) {
1422                 &release_ldap_handle($heap->{ldap_handle}->{$task_id});
1423         }
1426 sub process_task {
1427     no strict "refs";
1428     #CHECK: Not @_[...]?
1429     my ($session, $heap, $task) = @_;
1430     my $error = 0;
1431     my $answer_l;
1432     my ($answer_header, @answer_target_l, $answer_source);
1433     my $client_answer = "";
1435     # prepare all variables needed to process message
1436     #my $msg = $task->{'xmlmessage'};
1437     my $msg = &decode_base64($task->{'xmlmessage'});
1438     my $incoming_id = $task->{'id'};
1439     my $module = $task->{'module'};
1440     my $header =  $task->{'headertag'};
1441     my $session_id = $task->{'sessionid'};
1442                 my $msg_hash;
1443                 eval {
1444         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
1445                 }; 
1446                 daemon_log("ERROR: XML failure '$@'") if ($@);
1447     my $source = @{$msg_hash->{'source'}}[0];
1448     
1449     # set timestamp of incoming client uptodate, so client will not 
1450     # be deleted from known_clients because of expiration
1451     my $cur_time = &get_time();
1452     my $sql = "UPDATE $known_clients_tn SET timestamp='$cur_time' WHERE hostname='$source'"; 
1453     my $res = $known_clients_db->exec_statement($sql);
1455     ######################
1456     # process incoming msg
1457     if( $error == 0) {
1458         daemon_log("$session_id INFO: Incoming msg (session_id=$session_id) with header '".@{$msg_hash->{'header'}}[0]."'", 5); 
1459         daemon_log("$session_id DEBUG: Processing module ".$module, 7);
1460         $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1462         if ( 0 < @{$answer_l} ) {
1463             my $answer_str = join("\n", @{$answer_l});
1464             while ($answer_str =~ /<header>(\w+)<\/header>/g) {
1465                 daemon_log("$session_id INFO: got answer message with header '$1'", 5);
1466             }
1467             daemon_log("$session_id DEBUG: $module: got answer from module: \n".$answer_str,9);
1468         } else {
1469             daemon_log("$session_id DEBUG: $module: got no answer from module!" ,7);
1470         }
1472     }
1473     if( !$answer_l ) { $error++ };
1475     ########
1476     # answer
1477     if( $error == 0 ) {
1479         foreach my $answer ( @{$answer_l} ) {
1480             # check outgoing msg to xml validity
1481             my $answer_hash = &check_outgoing_xml_validity($answer, $session_id);
1482             if( not defined $answer_hash ) { next; }
1483             
1484             $answer_header = @{$answer_hash->{'header'}}[0];
1485             @answer_target_l = @{$answer_hash->{'target'}};
1486             $answer_source = @{$answer_hash->{'source'}}[0];
1488             # deliver msg to all targets 
1489             foreach my $answer_target ( @answer_target_l ) {
1491                 # targets of msg are all gosa-si-clients in known_clients_db
1492                 if( $answer_target eq "*" ) {
1493                     # answer is for all clients
1494                     my $sql_statement= "SELECT * FROM known_clients";
1495                     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
1496                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1497                         my $host_name = $hit->{hostname};
1498                         my $host_key = $hit->{hostkey};
1499                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1500                         &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1501                     }
1502                 }
1504                 # targets of msg are all gosa-si-server in known_server_db
1505                 elsif( $answer_target eq "KNOWN_SERVER" ) {
1506                     # answer is for all server in known_server
1507                     my $sql_statement= "SELECT * FROM $known_server_tn";
1508                     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
1509                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1510                         my $host_name = $hit->{hostname};
1511                         my $host_key = $hit->{hostkey};
1512                         $answer =~ s/<target>\S+<\/target>/<target>$host_name<\/target>/g;
1513                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1514                         &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1515                     }
1516                 }
1518                 # target of msg is GOsa
1519                                 elsif( $answer_target eq "GOSA" ) {
1520                                         my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1521                                         my $add_on = "";
1522                     if( defined $session_id ) {
1523                         $add_on = ".session_id=$session_id";
1524                     }
1525                     # answer is for GOSA and has to returned to connected client
1526                     my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1527                     $client_answer = $gosa_answer.$add_on;
1528                 }
1530                 # target of msg is job queue at this host
1531                 elsif( $answer_target eq "JOBDB") {
1532                     $answer =~ /<header>(\S+)<\/header>/;   
1533                     my $header;
1534                     if( defined $1 ) { $header = $1; }
1535                     my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1536                     &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1537                 }
1539                 # Target of msg is a mac address
1540                 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 ) {
1541                     daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients and foreign_clients", 5);
1543                     # Looking for macaddress in known_clients
1544                     my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1545                     my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1546                     my $found_ip_flag = 0;
1547                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1548                         my $host_name = $hit->{hostname};
1549                         my $host_key = $hit->{hostkey};
1550                         $answer =~ s/$answer_target/$host_name/g;
1551                         daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1552                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1553                         &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1554                         $found_ip_flag++ ;
1555                     }   
1557                     # Looking for macaddress in foreign_clients
1558                     if ($found_ip_flag == 0) {
1559                         my $sql = "SELECT * FROM $foreign_clients_tn WHERE macaddress LIKE '$answer_target'";
1560                         my $res = $foreign_clients_db->select_dbentry($sql);
1561                         while( my ($hit_num, $hit) = each %{ $res } ) {
1562                             my $host_name = $hit->{hostname};
1563                             my $reg_server = $hit->{regserver};
1564                             daemon_log("$session_id INFO: found host '$host_name' with mac '$answer_target', registered at '$reg_server'", 5);
1565                             
1566                             # Fetch key for reg_server
1567                             my $reg_server_key;
1568                             my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$reg_server'";
1569                             my $res = $known_server_db->select_dbentry($sql);
1570                             if (exists $res->{1}) {
1571                                 $reg_server_key = $res->{1}->{'hostkey'}; 
1572                             } else {
1573                                 daemon_log("$session_id ERROR: cannot find hostkey for '$host_name' in '$known_server_tn'", 1); 
1574                                 daemon_log("$session_id ERROR: unable to forward answer to correct registration server, processing is aborted!", 1); 
1575                                 $reg_server_key = undef;
1576                             }
1578                             # Send answer to server where client is registered
1579                             if (defined $reg_server_key) {
1580                                 $answer =~ s/$answer_target/$host_name/g;
1581                                 my $error = &send_msg_to_target($answer, $reg_server, $reg_server_key, $answer_header, $session_id);
1582                                 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1583                                 $found_ip_flag++ ;
1584                             }
1585                         }
1586                     }
1588                     # No mac to ip matching found
1589                     if( $found_ip_flag == 0) {
1590                         daemon_log("$session_id WARNING: no host found in known_clients or foreign_clients with mac address '$answer_target'", 3);
1591                         &reactivate_job_with_delay($session_id, $answer_target, $answer_header, 30);
1592                     }
1594                 # Answer is for one specific host   
1595                 } else {
1596                     # get encrypt_key
1597                     my $encrypt_key = &get_encrypt_key($answer_target);
1598                     if( not defined $encrypt_key ) {
1599                         # unknown target
1600                         daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1601                         next;
1602                     }
1603                     my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1604                     &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1605                 }
1606             }
1607         }
1608     }
1610     my $filter = POE::Filter::Reference->new();
1611     my %result = ( 
1612             status => "seems ok to me",
1613             answer => $client_answer,
1614             );
1616     my $output = $filter->put( [ \%result ] );
1617     print @$output;
1622 sub session_start {
1623     my ($kernel) = $_[KERNEL];
1624     $global_kernel = $kernel;
1625     $kernel->yield('register_at_foreign_servers');
1626         $kernel->yield('create_fai_server_db', $fai_server_tn );
1627         $kernel->yield('create_fai_release_db', $fai_release_tn );
1628     $kernel->yield('watch_for_next_tasks');
1629         $kernel->sig(USR1 => "sig_handler");
1630         $kernel->sig(USR2 => "recreate_packages_db");
1631         $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1632         $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay); 
1633     $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay); 
1634         $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1635     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1636         $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1637     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1639     # Start opsi check
1640     if ($opsi_enabled eq "true") {
1641         $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay); 
1642     }
1647 sub watch_for_done_jobs {
1648         #CHECK: $heap for what?
1649         my ($kernel,$heap) = @_[KERNEL, HEAP];
1651         my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((status='done') AND (modified='0'))";
1652         my $res = $job_db->select_dbentry( $sql_statement );
1654         while( my ($id, $hit) = each %{$res} ) {
1655                 my $jobdb_id = $hit->{id};
1656                 my $sql_statement = "DELETE FROM $job_queue_tn WHERE id=$jobdb_id"; 
1657                 my $res = $job_db->del_dbentry($sql_statement); 
1658         }
1660         $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1664 sub watch_for_opsi_jobs {
1665     my ($kernel) = $_[KERNEL];
1667     # 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 
1668     # opsi install job is to parse the xml message. There is still the correct header.
1669     my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((xmlmessage LIKE '%opsi_install_client</header>%') AND (status='processing') AND (siserver='localhost'))";
1670         my $res = $job_db->select_dbentry( $sql_statement );
1672     # Ask OPSI for an update of the running jobs
1673     while (my ($id, $hit) = each %$res ) {
1674         # Determine current parameters of the job
1675         my $hostId = $hit->{'plainname'};
1676         my $macaddress = $hit->{'macaddress'};
1677         my $progress = $hit->{'progress'};
1679         my $result= {};
1680         
1681         # For hosts, only return the products that are or get installed
1682         my $callobj;
1683         $callobj = {
1684             method  => 'getProductStates_hash',
1685             params  => [ $hostId ],
1686             id  => 1,
1687         };
1688         
1689         my $hres = $opsi_client->call($opsi_url, $callobj);
1690         #my ($hres_err, $hres_err_string) = &check_opsi_res($hres);
1691         if (not &check_opsi_res($hres)) {
1692             my $htmp= $hres->result->{$hostId};
1693         
1694             # Check state != not_installed or action == setup -> load and add
1695             my $products= 0;
1696             my $installed= 0;
1697             my $installing = 0;
1698             my $error= 0;  
1699             my @installed_list;
1700             my @error_list;
1701             my $act_status = "none";
1702             foreach my $product (@{$htmp}){
1704                 if ($product->{'installationStatus'} ne "not_installed" or
1705                         $product->{'actionRequest'} eq "setup"){
1707                     # Increase number of products for this host
1708                     $products++;
1709         
1710                     if ($product->{'installationStatus'} eq "failed"){
1711                         $result->{$product->{'productId'}}= "error";
1712                         unshift(@error_list, $product->{'productId'});
1713                         $error++;
1714                     }
1715                     if ($product->{'installationStatus'} eq "installed" && $product->{'actionRequest'}  eq "none"){
1716                         $result->{$product->{'productId'}}= "installed";
1717                         unshift(@installed_list, $product->{'productId'});
1718                         $installed++;
1719                     }
1720                     if ($product->{'installationStatus'} eq "installing"){
1721                         $result->{$product->{'productId'}}= "installing";
1722                         $installing++;
1723                         $act_status = "installing - ".$product->{'productId'};
1724                     }
1725                 }
1726             }
1727         
1728             # Estimate "rough" progress, avoid division by zero
1729             if ($products == 0) {
1730                 $result->{'progress'}= 0;
1731             } else {
1732                 $result->{'progress'}= int($installed * 100 / $products);
1733             }
1735             # Set updates in job queue
1736             if ((not $error) && (not $installing) && ($installed)) {
1737                 $act_status = "installed - ".join(", ", @installed_list);
1738             }
1739             if ($error) {
1740                 $act_status = "error - ".join(", ", @error_list);
1741             }
1742             if ($progress ne $result->{'progress'} ) {
1743                 # Updating progress and result 
1744                 my $update_statement = "UPDATE $job_queue_tn SET modified='1', progress='".$result->{'progress'}."', result='$act_status' WHERE macaddress LIKE '$macaddress' AND siserver='localhost'";
1745                 my $update_res = $job_db->update_dbentry($update_statement);
1746             }
1747             if ($progress eq 100) { 
1748                 # Updateing status
1749                 my $done_statement = "UPDATE $job_queue_tn SET modified='1', ";
1750                 if ($error) {
1751                     $done_statement .= "status='error'";
1752                 } else {
1753                     $done_statement .= "status='done'";
1754                 }
1755                 $done_statement .= " WHERE macaddress LIKE '$macaddress' AND siserver='localhost'";
1756                 my $done_res = $job_db->update_dbentry($done_statement);
1757             }
1760         }
1761     }
1763     $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay);
1767 # If a job got an update or was modified anyway, send to all other si-server an update message of this jobs.
1768 sub watch_for_modified_jobs {
1769     my ($kernel,$heap) = @_[KERNEL, HEAP];
1771     my $sql_statement = "SELECT * FROM $job_queue_tn WHERE (modified='1')"; 
1772     my $res = $job_db->select_dbentry( $sql_statement );
1773     
1774     # if db contains no jobs which should be update, do nothing
1775     if (keys %$res != 0) {
1777         if ($job_synchronization  eq "true") {
1778             # make out of the db result a gosa-si message   
1779             my $update_msg = &db_res2si_msg ($res, "foreign_job_updates", "KNOWN_SERVER", "MY_LOCAL_ADDRESS");
1780  
1781             # update all other SI-server
1782             &inform_all_other_si_server($update_msg);
1783         }
1785         # set jobs all jobs to modified = 0, wait until the next modification for updates of other si-server
1786         $sql_statement = "UPDATE $job_queue_tn SET modified='0' ";
1787         $res = $job_db->update_dbentry($sql_statement);
1788     }
1790     $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay);
1794 sub watch_for_new_jobs {
1795         if($watch_for_new_jobs_in_progress == 0) {
1796                 $watch_for_new_jobs_in_progress = 1;
1797                 my ($kernel,$heap) = @_[KERNEL, HEAP];
1799                 # check gosa job queue for jobs with executable timestamp
1800                 my $timestamp = &get_time();
1801                 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE siserver='localhost' AND status='waiting' AND (CAST(timestamp AS UNSIGNED)) < $timestamp ORDER BY timestamp";
1802                 my $res = $job_db->exec_statement( $sql_statement );
1804                 # Merge all new jobs that would do the same actions
1805                 my @drops;
1806                 my $hits;
1807                 foreach my $hit (reverse @{$res} ) {
1808                         my $macaddress= lc @{$hit}[8];
1809                         my $headertag= @{$hit}[5];
1810                         if(
1811                                 defined($hits->{$macaddress}) &&
1812                                 defined($hits->{$macaddress}->{$headertag}) &&
1813                                 defined($hits->{$macaddress}->{$headertag}[0])
1814                         ) {
1815                                 push @drops, "DELETE FROM $job_queue_tn WHERE id = $hits->{$macaddress}->{$headertag}[0]";
1816                         }
1817                         $hits->{$macaddress}->{$headertag}= $hit;
1818                 }
1820                 # Delete new jobs with a matching job in state 'processing'
1821                 foreach my $macaddress (keys %{$hits}) {
1822                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1823                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1824                                 if(defined($jobdb_id)) {
1825                                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND headertag='$jobdb_headertag' AND status='processing'";
1826                                         my $res = $job_db->exec_statement( $sql_statement );
1827                                         foreach my $hit (@{$res}) {
1828                                                 push @drops, "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1829                                         }
1830                                 } else {
1831                                         daemon_log("J ERROR: Job without id exists for macaddress $macaddress!", 1);
1832                                 }
1833                         }
1834                 }
1836                 # Commit deletion
1837                 $job_db->exec_statementlist(\@drops);
1839                 # Look for new jobs that could be executed
1840                 foreach my $macaddress (keys %{$hits}) {
1842                         # Look if there is an executing job
1843                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='processing'";
1844                         my $res = $job_db->exec_statement( $sql_statement );
1846                         # Skip new jobs for host if there is a processing job
1847                         if(defined($res) and defined @{$res}[0]) {
1848                                 # Prevent race condition if there is a trigger_activate job waiting and a goto-activation job processing
1849                                 my $row = @{$res}[0] if (ref $res eq 'ARRAY');
1850                                 if(@{$row}[5] eq 'trigger_action_reinstall') {
1851                                         my $sql_statement_2 =  "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='waiting' AND headertag = 'trigger_activate_new'"; 
1852                                         my $res_2 = $job_db->exec_statement( $sql_statement_2 );
1853                                         if(defined($res_2) and defined @{$res_2}[0]) {
1854                                                 # Set status from goto-activation to 'waiting' and update timestamp
1855                                                 $job_db->exec_statement("UPDATE $job_queue_tn SET status='waiting', timestamp='".&calc_timestamp(&get_time(), 'plus', 30)."' WHERE macaddress LIKE '$macaddress' AND headertag = 'trigger_action_reinstall'");
1856                                         }
1857                                 }
1858                                 next;
1859                         }
1861                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1862                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1863                                 if(defined($jobdb_id)) {
1864                                         my $job_msg = @{$hits->{$macaddress}->{$jobdb_headertag}}[7];
1866                                         daemon_log("J DEBUG: its time to execute $job_msg", 7);
1867                                         my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1868                                         my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1870                                         # expect macaddress is unique!!!!!!
1871                                         my $target = $res_hash->{1}->{hostname};
1873                                         # change header
1874                                         $job_msg =~ s/<header>job_/<header>gosa_/;
1876                                         # add sqlite_id
1877                                         $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1879                                         $job_msg =~ /<header>(\S+)<\/header>/;
1880                                         my $header = $1 ;
1881                                         my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");                    
1883                                         # update status in job queue to ...
1884                     # ... 'processing', for jobs: 'reinstall', 'update'
1885                     if (($header =~ /gosa_trigger_action_reinstall/) 
1886                             || ($header =~ /gosa_trigger_activate_new/)
1887                             || ($header =~ /gosa_trigger_action_update/)) {
1888                         my $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
1889                         my $dbres = $job_db->update_dbentry($sql_statement);
1890                     }
1892                     # ... 'done', for all other jobs, they are no longer needed in the jobqueue
1893                     else {
1894                         my $sql_statement = "UPDATE $job_queue_tn SET status='done' WHERE id=$jobdb_id";
1895                         my $dbres = $job_db->update_dbentry($sql_statement);
1896                     }
1897                 
1899                                         # We don't want parallel processing
1900                                         last;
1901                                 }
1902                         }
1903                 }
1905                 $watch_for_new_jobs_in_progress = 0;
1906                 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1907         }
1911 sub watch_for_new_messages {
1912     my ($kernel,$heap) = @_[KERNEL, HEAP];
1913     my @coll_user_msg;   # collection list of outgoing messages
1914     
1915     # check messaging_db for new incoming messages with executable timestamp
1916     my $timestamp = &get_time();
1917     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS UNSIGNED))<$timestamp AND flag='n' AND direction='in' )";
1918     my $res = $messaging_db->exec_statement( $sql_statement );
1919         foreach my $hit (@{$res}) {
1921         # create outgoing messages
1922         my $message_to = @{$hit}[3];
1923         # translate message_to to plain login name
1924         my @message_to_l = split(/,/, $message_to);  
1925                 my %receiver_h; 
1926                 foreach my $receiver (@message_to_l) {
1927                         if ($receiver =~ /^u_([\s\S]*)$/) {
1928                                 $receiver_h{$1} = 0;
1929                         } elsif ($receiver =~ /^g_([\s\S]*)$/) {
1930                                 my $group_name = $1;
1931                                 # fetch all group members from ldap and add them to receiver hash
1932                                 my $ldap_handle = &get_ldap_handle();
1933                                 if (defined $ldap_handle) {
1934                                                 my $mesg = $ldap_handle->search(
1935                                                                                 base => $ldap_base,
1936                                                                                 scope => 'sub',
1937                                                                                 attrs => ['memberUid'],
1938                                                                                 filter => "cn=$group_name",
1939                                                                                 );
1940                                                 if ($mesg->count) {
1941                                                                 my @entries = $mesg->entries;
1942                                                                 foreach my $entry (@entries) {
1943                                                                                 my @receivers= $entry->get_value("memberUid");
1944                                                                                 foreach my $receiver (@receivers) { 
1945                                                                                                 $receiver_h{$receiver} = 0;
1946                                                                                 }
1947                                                                 }
1948                                                 } 
1949                                                 # translating errors ?
1950                                                 if ($mesg->code) {
1951                                                                 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: $mesg->error", 1);
1952                                                 }
1953                                                 &release_ldap_handle($ldap_handle);
1954                                 # ldap handle error ?           
1955                                 } else {
1956                                         daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: no ldap handle available", 1);
1957                                 }
1958                         } else {
1959                                 my $sbjct = &encode_base64(@{$hit}[1]);
1960                                 my $msg = &encode_base64(@{$hit}[7]);
1961                                 &daemon_log("M WARNING: unknown receiver '$receiver' for a user-message '$sbjct - $msg'", 3); 
1962                         }
1963                 }
1964                 my @receiver_l = keys(%receiver_h);
1966         my $message_id = @{$hit}[0];
1968         #add each outgoing msg to messaging_db
1969         my $receiver;
1970         foreach $receiver (@receiver_l) {
1971             my $sql_statement = "INSERT INTO $messaging_tn (id, subject, message_from, message_to, flag, direction, delivery_time, message, timestamp) ".
1972                 "VALUES ('".
1973                 $message_id."', '".    # id
1974                 @{$hit}[1]."', '".     # subject
1975                 @{$hit}[2]."', '".     # message_from
1976                 $receiver."', '".      # message_to
1977                 "none"."', '".         # flag
1978                 "out"."', '".          # direction
1979                 @{$hit}[6]."', '".     # delivery_time
1980                 @{$hit}[7]."', '".     # message
1981                 $timestamp."'".     # timestamp
1982                 ")";
1983             &daemon_log("M DEBUG: $sql_statement", 1);
1984             my $res = $messaging_db->exec_statement($sql_statement);
1985             &daemon_log("M INFO: message '".@{$hit}[0]."' is prepared for delivery to receiver '$receiver'", 5);
1986         }
1988         # set incoming message to flag d=deliverd
1989         $sql_statement = "UPDATE $messaging_tn SET flag='p' WHERE id='$message_id'"; 
1990         &daemon_log("M DEBUG: $sql_statement", 7);
1991         $res = $messaging_db->update_dbentry($sql_statement);
1992         &daemon_log("M INFO: message '$message_id' is set to flag 'p' (processed)", 5);
1993     }
1995     $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay); 
1996     return;
1999 sub watch_for_delivery_messages {
2000     my ($kernel, $heap) = @_[KERNEL, HEAP];
2002     # select outgoing messages
2003     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( flag='p' AND direction='out' )";
2004     #&daemon_log("0 DEBUG: $sql", 7);
2005     my $res = $messaging_db->exec_statement( $sql_statement );
2006     
2007     # build out msg for each    usr
2008     foreach my $hit (@{$res}) {
2009         my $receiver = @{$hit}[3];
2010         my $msg_id = @{$hit}[0];
2011         my $subject = @{$hit}[1];
2012         my $message = @{$hit}[7];
2014         # resolve usr -> host where usr is logged in
2015         my $sql = "SELECT * FROM $login_users_tn WHERE (user='$receiver')"; 
2016         #&daemon_log("0 DEBUG: $sql", 7);
2017         my $res = $login_users_db->exec_statement($sql);
2019         # receiver is logged in nowhere
2020         if (not ref(@$res[0]) eq "ARRAY") { next; }    
2022         # receiver ist logged in at a client registered at local server
2023                 my $send_succeed = 0;
2024                 foreach my $hit (@$res) {
2025                                 my $receiver_host = @$hit[0];
2026                 my $delivered2host = 0;
2027                                 &daemon_log("M DEBUG: user '$receiver' is logged in at host '$receiver_host'", 7);
2029                                 # Looking for host in know_clients_db 
2030                                 my $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$receiver_host')";
2031                                 my $res = $known_clients_db->exec_statement($sql);
2033                 # Host is known in known_clients_db
2034                 if (ref(@$res[0]) eq "ARRAY") {
2035                     my $receiver_key = @{@{$res}[0]}[2];
2036                     my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
2037                     my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
2038                     my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0); 
2039                     if ($error == 0 ) {
2040                         $send_succeed++ ;
2041                         $delivered2host++ ;
2042                         &daemon_log("M DEBUG: send message for user '$receiver' to host '$receiver_host'", 7); 
2043                     } else {
2044                         &daemon_log("M DEBUG: cannot send message for user '$receiver' to host '$receiver_host'", 7); 
2045                     }
2046                 }
2047                 
2048                 # Message already send, do not need to do anything more, otherwise ...
2049                 if ($delivered2host) { next;}
2050     
2051                 # ...looking for host in foreign_clients_db
2052                 $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$receiver_host')";
2053                 $res = $foreign_clients_db->exec_statement($sql);
2054   
2055                                 # Host is known in foreign_clients_db 
2056                                 if (ref(@$res[0]) eq "ARRAY") { 
2057                     my $registration_server = @{@{$res}[0]}[2];
2058                     
2059                     # Fetch encryption key for registration server
2060                     my $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$registration_server')";
2061                     my $res = $known_server_db->exec_statement($sql);
2062                     if (ref(@$res[0]) eq "ARRAY") { 
2063                         my $registration_server_key = @{@{$res}[0]}[3];
2064                         my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
2065                         my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
2066                         my $error = &send_msg_to_target($out_msg, $registration_server, $registration_server_key, "usr_msg", 0); 
2067                         if ($error == 0 ) {
2068                             $send_succeed++ ;
2069                             $delivered2host++ ;
2070                             &daemon_log("M DEBUG: send message for user '$receiver' to server '$registration_server'", 7); 
2071                         } else {
2072                             &daemon_log("M ERROR: cannot send message for user '$receiver' to server '$registration_server'", 1); 
2073                         }
2075                     } else {
2076                         &daemon_log("M ERROR: host '$receiver_host' is reported to be ".
2077                                 "registrated at server '$registration_server', ".
2078                                 "but no data available in known_server_db ", 1); 
2079                     }
2080                 }
2081                 
2082                 if (not $delivered2host) {
2083                     &daemon_log("M ERROR: unable to send user message to host '$receiver_host'", 1);
2084                 }
2085                 }
2087                 if ($send_succeed) {
2088                                 # set outgoing msg at db to deliverd
2089                                 my $sql = "UPDATE $messaging_tn SET flag='d' WHERE (id='$msg_id' AND direction='out' AND message_to='$receiver')"; 
2090                                 my $res = $messaging_db->exec_statement($sql); 
2091                 &daemon_log("M INFO: send message for user '$receiver' to logged in hosts", 5);
2092                 } else {
2093             &daemon_log("M WARNING: failed to deliver message for user '$receiver'", 3); 
2094         }
2095         }
2097     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay); 
2098     return;
2102 sub watch_for_done_messages {
2103     my ($kernel,$heap) = @_[KERNEL, HEAP];
2105     my $sql = "SELECT * FROM $messaging_tn WHERE (flag='p' AND direction='in')"; 
2106     #&daemon_log("0 DEBUG: $sql", 7);
2107     my $res = $messaging_db->exec_statement($sql); 
2109     foreach my $hit (@{$res}) {
2110         my $msg_id = @{$hit}[0];
2112         my $sql = "SELECT * FROM $messaging_tn WHERE (id='$msg_id' AND direction='out' AND (NOT flag='s'))"; 
2113         #&daemon_log("0 DEBUG: $sql", 7); 
2114         my $res = $messaging_db->exec_statement($sql);
2116         # not all usr msgs have been seen till now
2117         if ( ref(@$res[0]) eq "ARRAY") { next; }
2118         
2119         $sql = "DELETE FROM $messaging_tn WHERE (id='$msg_id')"; 
2120         #&daemon_log("0 DEBUG: $sql", 7);
2121         $res = $messaging_db->exec_statement($sql);
2122     
2123     }
2125     $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay); 
2126     return;
2130 sub watch_for_old_known_clients {
2131     my ($kernel,$heap) = @_[KERNEL, HEAP];
2133     my $sql_statement = "SELECT * FROM $known_clients_tn";
2134     my $res = $known_clients_db->select_dbentry( $sql_statement );
2136     my $cur_time = int(&get_time());
2138     while ( my ($hit_num, $hit) = each %$res) {
2139         my $expired_timestamp = int($hit->{'timestamp'});
2140         $expired_timestamp =~ /(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/;
2141         my $dt = DateTime->new( year   => $1,
2142                 month  => $2,
2143                 day    => $3,
2144                 hour   => $4,
2145                 minute => $5,
2146                 second => $6,
2147                 );
2149         $dt->add( seconds => 2 * int($hit->{'keylifetime'}) );
2150         $expired_timestamp = $dt->ymd('').$dt->hms('');
2151         if ($cur_time > $expired_timestamp) {
2152             my $hostname = $hit->{'hostname'};
2153             my $del_sql = "DELETE FROM $known_clients_tn WHERE hostname='$hostname'"; 
2154             my $del_res = $known_clients_db->exec_statement($del_sql);
2156             &main::daemon_log("0 INFO: timestamp '".$hit->{'timestamp'}."' of client '$hostname' is expired('$expired_timestamp'), client will be deleted from known_clients_db", 5);
2157         }
2159     }
2161     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
2165 sub watch_for_next_tasks {
2166     my ($kernel,$heap) = @_[KERNEL, HEAP];
2168     my $sql = "SELECT * FROM $incoming_tn";
2169     my $res = $incoming_db->select_dbentry($sql);
2170     
2171     while ( my ($hit_num, $hit) = each %$res) {
2172         my $headertag = $hit->{'headertag'};
2173         if ($headertag =~ /^answer_(\d+)/) {
2174             # do not start processing, this message is for a still running POE::Wheel
2175             next;
2176         }
2177         my $message_id = $hit->{'id'};
2178         my $session_id = $hit->{'sessionid'};
2179         &daemon_log("$session_id DEBUG: start processing for message with incoming id: '$message_id'", 7);
2181         $kernel->yield('next_task', $hit);
2183         my $sql = "DELETE FROM $incoming_tn WHERE id=$message_id";
2184         my $res = $incoming_db->exec_statement($sql);
2185     }
2187     $kernel->delay_set('watch_for_next_tasks', 1); 
2191 sub get_ldap_handle {
2192         my ($session_id) = @_;
2193         my $heap;
2195         if (not defined $session_id ) { $session_id = 0 };
2196         if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
2198         my ($package, $file, $row, $subroutine, $hasArgs, $wantArray, $evalText, $isRequire) = caller(1);
2199         my $caller_text = "subroutine $subroutine";
2200         if ($subroutine eq "(eval)") {
2201                 $caller_text = "eval block within file '$file' for '$evalText'"; 
2202         }
2203         daemon_log("$session_id INFO: new ldap handle for '$caller_text' required!", 7);
2205 get_handle:
2206         my $ldap_handle = Net::LDAP->new( $ldap_uri );
2207         if (not ref $ldap_handle) {
2208                 daemon_log("$session_id ERROR: Connection to LDAP URI '$ldap_uri' failed! Retrying!", 1);
2209                 usleep(100000);
2210                 goto get_handle;
2211         } else {
2212                 daemon_log("$session_id DEBUG: Connection to LDAP URI '$ldap_uri' established.", 6);
2213         }
2215         $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);
2216         return $ldap_handle;
2220 sub release_ldap_handle {
2221         my ($ldap_handle) = @_ ;
2222         if(ref $ldap_handle) {
2223           $ldap_handle->disconnect();
2224   }
2225         &main::daemon_log("0 DEBUG: Released a ldap handle!", 6);
2226         return;
2230 sub change_fai_state {
2231         my ($st, $targets, $session_id) = @_;
2232         $session_id = 0 if not defined $session_id;
2233         # Set FAI state to localboot
2234         my %mapActions= (
2235                 reboot    => '',
2236                 update    => 'softupdate',
2237                 localboot => 'localboot',
2238                 reinstall => 'install',
2239                 rescan    => '',
2240                 wake      => '',
2241                 memcheck  => 'memcheck',
2242                 sysinfo   => 'sysinfo',
2243                 install   => 'install',
2244         );
2246         # Return if this is unknown
2247         if (!exists $mapActions{ $st }){
2248                 daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1); 
2249                 return;
2250         }
2252         my $state= $mapActions{ $st };
2254         # Build search filter for hosts
2255         my $search= "(&(objectClass=GOhard)";
2256         foreach (@{$targets}){
2257                 $search.= "(macAddress=$_)";
2258         }
2259         $search.= ")";
2261         # If there's any host inside of the search string, procress them
2262         if (!($search =~ /macAddress/)){
2263                 daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);    
2264                 return;
2265         }
2267         my $ldap_handle = &get_ldap_handle($session_id);
2268         # Perform search for Unit Tag
2269         my $mesg = $ldap_handle->search(
2270                 base   => $ldap_base,
2271                 scope  => 'sub',
2272                 attrs  => ['dn', 'FAIstate', 'objectClass'],
2273                 filter => "$search"
2274         );
2276         if ($mesg->count) {
2277                 my @entries = $mesg->entries;
2278                 if (0 == @entries) {
2279                         daemon_log("$session_id ERROR: ldap search failed: ldap_base=$ldap_base, filter=$search", 1); 
2280                 }
2282                 foreach my $entry (@entries) {
2283                         # Only modify entry if it is not set to '$state'
2284                         if ($entry->get_value("FAIstate") ne "$state"){
2285                                 daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
2286                                 my $result;
2287                                 my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
2288                                 if (exists $tmp{'FAIobject'}){
2289                                         if ($state eq ''){
2290                                                 $result= $ldap_handle->modify($entry->dn, changes => [ delete => [ FAIstate => [] ] ]);
2291                                         } else {
2292                                                 $result= $ldap_handle->modify($entry->dn, changes => [ replace => [ FAIstate => $state ] ]);
2293                                         }
2294                                 } elsif ($state ne ''){
2295                                         $result= $ldap_handle->modify($entry->dn, changes => [ add => [ objectClass => 'FAIobject' ], add => [ FAIstate => $state ] ]);
2296                                 }
2298                                 # Errors?
2299                                 if ($result->code){
2300                                         daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2301                                 }
2302                         } else {
2303                                 daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 7); 
2304                         }  
2305                 }
2306         } else {
2307                 daemon_log("$session_id ERROR: LDAP search failed: ldap_base=$ldap_base, filter=$search", 1);
2308         }
2309         &release_ldap_handle($ldap_handle);               
2311         return;
2315 sub change_goto_state {
2316     my ($st, $targets, $session_id) = @_;
2317     $session_id = 0  if not defined $session_id;
2319     # Switch on or off?
2320     my $state= $st eq 'active' ? 'active': 'locked';
2322     my $ldap_handle = &get_ldap_handle($session_id);
2323     if( defined($ldap_handle) ) {
2325       # Build search filter for hosts
2326       my $search= "(&(objectClass=GOhard)";
2327       foreach (@{$targets}){
2328         $search.= "(macAddress=$_)";
2329       }
2330       $search.= ")";
2332       # If there's any host inside of the search string, procress them
2333       if (!($search =~ /macAddress/)){
2334               &release_ldap_handle($ldap_handle);
2335         return;
2336       }
2338       # Perform search for Unit Tag
2339       my $mesg = $ldap_handle->search(
2340           base   => $ldap_base,
2341           scope  => 'sub',
2342           attrs  => ['dn', 'gotoMode'],
2343           filter => "$search"
2344           );
2346       if ($mesg->count) {
2347         my @entries = $mesg->entries;
2348         foreach my $entry (@entries) {
2350           # Only modify entry if it is not set to '$state'
2351           if ($entry->get_value("gotoMode") ne $state){
2353             daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
2354             my $result;
2355             $result= $ldap_handle->modify($entry->dn, changes => [replace => [ gotoMode => $state ] ]);
2357             # Errors?
2358             if ($result->code){
2359               &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2360             }
2362           }
2363         }
2364       } else {
2365                 daemon_log("$session_id ERROR: LDAP search failed in function change_goto_state: ldap_base=$ldap_base, filter=$search", 1);
2366           }
2368     }
2369         &release_ldap_handle($ldap_handle);
2370         return;
2374 sub run_recreate_packages_db {
2375     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2376     my $session_id = $session->ID;
2377         &main::daemon_log("$session_id INFO: Recreating FAI Packages DB ('$fai_release_tn', '$fai_server_tn', '$packages_list_tn')", 5);
2378         $kernel->yield('create_fai_release_db', $fai_release_tn);
2379         $kernel->yield('create_fai_server_db', $fai_server_tn);
2380         return;
2384 sub run_create_fai_server_db {
2385     my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2386     my $session_id = $session->ID;
2387     my $task = POE::Wheel::Run->new(
2388             Program => sub { &create_fai_server_db($table_name,$kernel, undef, $session_id) },
2389             StdoutEvent  => "session_run_result",
2390             StderrEvent  => "session_run_debug",
2391             CloseEvent   => "session_run_done",
2392             );
2394     $heap->{task}->{ $task->ID } = $task;
2395     return;
2399 sub create_fai_server_db {
2400         my ($table_name, $kernel, $dont_create_packages_list, $session_id) = @_;
2401         my $result;
2403         if (not defined $session_id) { $session_id = 0; }
2404         my $ldap_handle = &get_ldap_handle($session_id);
2405         if(defined($ldap_handle)) {
2406                 daemon_log("$session_id INFO: create_fai_server_db: start", 5);
2407                 my $mesg= $ldap_handle->search(
2408                         base   => $ldap_base,
2409                         scope  => 'sub',
2410                         attrs  => ['FAIrepository', 'gosaUnitTag'],
2411                         filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
2412                 );
2413                 if($mesg->{'resultCode'} == 0 &&
2414                         $mesg->count != 0) {
2415                         foreach my $entry (@{$mesg->{entries}}) {
2416                                 if($entry->exists('FAIrepository')) {
2417                                         # Add an entry for each Repository configured for server
2418                                         foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
2419                                                 my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
2420                                                 my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
2421                                                 $result= $fai_server_db->add_dbentry( { 
2422                                                                 table => $table_name,
2423                                                                 primkey => ['server', 'fai_release', 'tag'],
2424                                                                 server => $tmp_url,
2425                                                                 fai_release => $tmp_release,
2426                                                                 sections => $tmp_sections,
2427                                                                 tag => (length($tmp_tag)>0)?$tmp_tag:"",
2428                                                         } );
2429                                         }
2430                                 }
2431                         }
2432                 }
2433                 daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
2434                 &release_ldap_handle($ldap_handle);
2436                 # TODO: Find a way to post the 'create_packages_list_db' event
2437                 if(not defined($dont_create_packages_list)) {
2438                         &create_packages_list_db(undef, $session_id);
2439                 }
2440         }       
2442         return $result;
2446 sub run_create_fai_release_db {
2447         my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2448         my $session_id = $session->ID;
2449         my $task = POE::Wheel::Run->new(
2450                 Program => sub { &create_fai_release_db($table_name, $session_id) },
2451                 StdoutEvent  => "session_run_result",
2452                 StderrEvent  => "session_run_debug",
2453                 CloseEvent   => "session_run_done",
2454         );
2456         $heap->{task}->{ $task->ID } = $task;
2457         return;
2461 sub create_fai_release_db {
2462         my ($table_name, $session_id) = @_;
2463         my $result;
2465         # used for logging
2466         if (not defined $session_id) { $session_id = 0; }
2468         my $ldap_handle = &get_ldap_handle($session_id);
2469         if(defined($ldap_handle)) {
2470                 daemon_log("$session_id INFO: create_fai_release_db: start",5);
2471                 my $mesg= $ldap_handle->search(
2472                         base   => $ldap_base,
2473                         scope  => 'sub',
2474                         attrs  => [],
2475                         filter => "(&(objectClass=organizationalUnit)(ou=fai))",
2476                 );
2477                 if(($mesg->code == 0) && ($mesg->count != 0))
2478                 {
2479                         daemon_log("$session_id DEBUG: create_fai_release_db: count " . $mesg->count,8);
2481                         # Walk through all possible FAI container ou's
2482                         my @sql_list;
2483                         my $timestamp= &get_time();
2484                         foreach my $ou (@{$mesg->{entries}}) {
2485                                 my $tmp_classes= resolve_fai_classes($ou->dn, $ldap_handle, $session_id);
2486                                 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
2487                                         my @tmp_array=get_fai_release_entries($tmp_classes);
2488                                         if(@tmp_array) {
2489                                                 foreach my $entry (@tmp_array) {
2490                                                         if(defined($entry) && ref($entry) eq 'HASH') {
2491                                                                 my $sql= 
2492                                                                 "INSERT INTO $table_name "
2493                                                                 ."(timestamp, fai_release, class, type, state) VALUES ("
2494                                                                 .$timestamp.","
2495                                                                 ."'".$entry->{'release'}."',"
2496                                                                 ."'".$entry->{'class'}."',"
2497                                                                 ."'".$entry->{'type'}."',"
2498                                                                 ."'".$entry->{'state'}."')";
2499                                                                 push @sql_list, $sql;
2500                                                         }
2501                                                 }
2502                                         }
2503                                 }
2504                         }
2506                         daemon_log("$session_id DEBUG: create_fai_release_db: Inserting ".scalar @sql_list." entries to DB",8);
2507             &release_ldap_handle($ldap_handle);
2508                         if(@sql_list) {
2509                                 unshift @sql_list, "VACUUM";
2510                                 unshift @sql_list, "DELETE FROM $table_name";
2511                                 $fai_release_db->exec_statementlist(\@sql_list);
2512                         }
2513                         daemon_log("$session_id DEBUG: create_fai_release_db: Done with inserting",7);
2514                 } else {
2515                         daemon_log("$session_id INFO: create_fai_release_db: error: " . $mesg->code, 5);
2516                 }
2517                 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
2518         }
2519         return $result;
2522 sub get_fai_types {
2523         my $tmp_classes = shift || return undef;
2524         my @result;
2526         foreach my $type(keys %{$tmp_classes}) {
2527                 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
2528                         my $entry = {
2529                                 type => $type,
2530                                 state => $tmp_classes->{$type}[0],
2531                         };
2532                         push @result, $entry;
2533                 }
2534         }
2536         return @result;
2539 sub get_fai_state {
2540         my $result = "";
2541         my $tmp_classes = shift || return $result;
2543         foreach my $type(keys %{$tmp_classes}) {
2544                 if(defined($tmp_classes->{$type}[0])) {
2545                         $result = $tmp_classes->{$type}[0];
2546                         
2547                 # State is equal for all types in class
2548                         last;
2549                 }
2550         }
2552         return $result;
2555 sub resolve_fai_classes {
2556         my ($fai_base, $ldap_handle, $session_id) = @_;
2557         if (not defined $session_id) { $session_id = 0; }
2558         my $result;
2559         my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
2560         my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
2561         my $fai_classes;
2563         daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base",7);
2564         my $mesg= $ldap_handle->search(
2565                 base   => $fai_base,
2566                 scope  => 'sub',
2567                 attrs  => ['cn','objectClass','FAIstate'],
2568                 filter => $fai_filter,
2569         );
2570         daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries",7);
2572         if($mesg->{'resultCode'} == 0 &&
2573                 $mesg->count != 0) {
2574                 foreach my $entry (@{$mesg->{entries}}) {
2575                         if($entry->exists('cn')) {
2576                                 my $tmp_dn= $entry->dn();
2577                                 $tmp_dn= substr( $tmp_dn, 0, length($tmp_dn)
2578                                         - length($fai_base) - 1 );
2580                                 # Skip classname and ou dn parts for class
2581                                 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?)$/;
2583                                 # Skip classes without releases
2584                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2585                                         next;
2586                                 }
2588                                 my $tmp_cn= $entry->get_value('cn');
2589                                 my $tmp_state= $entry->get_value('FAIstate');
2591                                 my $tmp_type;
2592                                 # Get FAI type
2593                                 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
2594                                         if(grep $_ eq $oclass, @possible_fai_classes) {
2595                                                 $tmp_type= $oclass;
2596                                                 last;
2597                                         }
2598                                 }
2600                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2601                                         # A Subrelease
2602                                         my @sub_releases = split(/,/, $tmp_release);
2604                                         # Walk through subreleases and build hash tree
2605                                         my $hash;
2606                                         while(my $tmp_sub_release = pop @sub_releases) {
2607                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2608                                         }
2609                                         eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
2610                                 } else {
2611                                         # A branch, no subrelease
2612                                         push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
2613                                 }
2614                         } elsif (!$entry->exists('cn')) {
2615                                 my $tmp_dn= $entry->dn();
2616                                 $tmp_dn= substr( $tmp_dn, 0, length($tmp_dn)
2617                                         - length($fai_base) - 1 );
2618                                 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?)$/;
2620                                 # Skip classes without releases
2621                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2622                                         next;
2623                                 }
2625                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2626                                         # A Subrelease
2627                                         my @sub_releases= split(/,/, $tmp_release);
2629                                         # Walk through subreleases and build hash tree
2630                                         my $hash;
2631                                         while(my $tmp_sub_release = pop @sub_releases) {
2632                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2633                                         }
2634                                         # Remove the last two characters
2635                                         chop($hash);
2636                                         chop($hash);
2638                                         eval('$fai_classes->'.$hash.'= {}');
2639                                 } else {
2640                                         # A branch, no subrelease
2641                                         if(!exists($fai_classes->{$tmp_release})) {
2642                                                 $fai_classes->{$tmp_release} = {};
2643                                         }
2644                                 }
2645                         }
2646                 }
2648                 # The hash is complete, now we can honor the copy-on-write based missing entries
2649                 foreach my $release (keys %$fai_classes) {
2650                         $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
2651                 }
2652         }
2653         return $result;
2656 sub apply_fai_inheritance {
2657        my $fai_classes = shift || return {};
2658        my $tmp_classes;
2660        # Get the classes from the branch
2661        foreach my $class (keys %{$fai_classes}) {
2662                # Skip subreleases
2663                if($class =~ /^ou=.*$/) {
2664                        next;
2665                } else {
2666                        $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
2667                }
2668        }
2670        # Apply to each subrelease
2671        foreach my $subrelease (keys %{$fai_classes}) {
2672                if($subrelease =~ /ou=/) {
2673                        foreach my $tmp_class (keys %{$tmp_classes}) {
2674                                if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
2675                                        $fai_classes->{$subrelease}->{$tmp_class} =
2676                                        deep_copy($tmp_classes->{$tmp_class});
2677                                } else {
2678                                        foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
2679                                                if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
2680                                                        $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
2681                                                        deep_copy($tmp_classes->{$tmp_class}->{$type});
2682                                                }
2683                                        }
2684                                }
2685                        }
2686                }
2687        }
2689        # Find subreleases in deeper levels
2690        foreach my $subrelease (keys %{$fai_classes}) {
2691                if($subrelease =~ /ou=/) {
2692                        foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
2693                                if($subsubrelease =~ /ou=/) {
2694                                        apply_fai_inheritance($fai_classes->{$subrelease});
2695                                }
2696                        }
2697                }
2698        }
2700        return $fai_classes;
2703 sub get_fai_release_entries {
2704         my $tmp_classes = shift || return;
2705         my $parent = shift || "";
2706         my @result = shift || ();
2708         foreach my $entry (keys %{$tmp_classes}) {
2709                 if(defined($entry)) {
2710                         if($entry =~ /^ou=.*$/) {
2711                                 my $release_name = $entry;
2712                                 $release_name =~ s/ou=//g;
2713                                 if(length($parent)>0) {
2714                                         $release_name = $parent."/".$release_name;
2715                                 }
2716                                 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2717                                 foreach my $bufentry(@bufentries) {
2718                                         push @result, $bufentry;
2719                                 }
2720                         } else {
2721                                 my @types = get_fai_types($tmp_classes->{$entry});
2722                                 foreach my $type (@types) {
2723                                         push @result, 
2724                                         {
2725                                                 'class' => $entry,
2726                                                 'type' => $type->{'type'},
2727                                                 'release' => $parent,
2728                                                 'state' => $type->{'state'},
2729                                         };
2730                                 }
2731                         }
2732                 }
2733         }
2735         return @result;
2738 sub deep_copy {
2739         my $this = shift;
2740         if (not ref $this) {
2741                 $this;
2742         } elsif (ref $this eq "ARRAY") {
2743                 [map deep_copy($_), @$this];
2744         } elsif (ref $this eq "HASH") {
2745                 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2746         } else { die "what type is $_?" }
2750 sub session_run_result {
2751     my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];    
2752     $kernel->sig(CHLD => "child_reap");
2755 sub session_run_debug {
2756     my $result = $_[ARG0];
2757     print STDERR "$result\n";
2760 sub session_run_done {
2761     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2762     delete $heap->{task}->{$task_id};
2763         if (exists $heap->{ldap_handle}->{$task_id}) {
2764                 &release_ldap_handle($heap->{ldap_handle}->{$task_id});
2765         }
2766         delete $heap->{ldap_handle}->{$task_id};
2770 sub create_sources_list {
2771         my $session_id = shift || 0;
2772         my $result="/tmp/gosa_si_tmp_sources_list";
2774         # Remove old file
2775         if(stat($result)) {
2776                 unlink($result);
2777                 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7); 
2778         }
2780         my $fh;
2781         open($fh, ">$result");
2782         if (not defined $fh) {
2783                 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7); 
2784                 return undef;
2785         }
2786         if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2787                 my $ldap_handle = &get_ldap_handle($session_id);
2788                 my $mesg=$ldap_handle->search(
2789                         base    => $main::ldap_server_dn,
2790                         scope   => 'base',
2791                         attrs   => 'FAIrepository',
2792                         filter  => 'objectClass=FAIrepositoryServer'
2793                 );
2794                 if($mesg->count) {
2795                         foreach my $entry(@{$mesg->{'entries'}}) {
2796                                 foreach my $value(@{$entry->get_value('FAIrepository', asref => 1)}) {
2797                                         my ($server, $tag, $release, $sections)= split /\|/, $value;
2798                                         my $line = "deb $server $release";
2799                                         $sections =~ s/,/ /g;
2800                                         $line.= " $sections";
2801                                         print $fh $line."\n";
2802                                 }
2803                         }
2804                 }
2805                 &release_ldap_handle($ldap_handle);
2806         } else {
2807                 if (defined $main::ldap_server_dn){
2808                         &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1); 
2809                 } else {
2810                         &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2811                 }
2812         }
2813         close($fh);
2815         return $result;
2819 sub run_create_packages_list_db {
2820     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2821         my $session_id = $session->ID;
2822         my $task = POE::Wheel::Run->new(
2823                                         Priority => +20,
2824                                         Program => sub {&create_packages_list_db(undef, $session_id)},
2825                                         StdoutEvent  => "session_run_result",
2826                                         StderrEvent  => "session_run_debug",
2827                                         CloseEvent   => "session_run_done",
2828                                         );
2829         $heap->{task}->{ $task->ID } = $task;
2833 sub create_packages_list_db {
2834         my ($sources_file, $session_id) = @_;
2835         
2836         # it should not be possible to trigger a recreation of packages_list_db
2837         # while packages_list_db is under construction, so set flag packages_list_under_construction
2838         # which is tested befor recreation can be started
2839         if (-r $packages_list_under_construction) {
2840                 daemon_log("$session_id WARNING: packages_list_db is right now under construction, please wait until this process is finished", 3);
2841                 return;
2842         } else {
2843                 daemon_log("$session_id INFO: create_packages_list_db: start", 5); 
2844                 # set packages_list_under_construction to true
2845                 system("touch $packages_list_under_construction");
2846                 @packages_list_statements=();
2847         }
2849         if (not defined $session_id) { $session_id = 0; }
2851         if (not defined $sources_file) { 
2852                 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5); 
2853                 $sources_file = &create_sources_list($session_id);
2854         }
2856         if (not defined $sources_file) {
2857                 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1); 
2858                 unlink($packages_list_under_construction);
2859                 return;
2860         }
2862         my $line;
2864         open(CONFIG, "<$sources_file") or do {
2865                 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2866                 unlink($packages_list_under_construction);
2867                 return;
2868         };
2870         # Read lines
2871         while ($line = <CONFIG>){
2872                 # Unify
2873                 chop($line);
2874                 $line =~ s/^\s+//;
2875                 $line =~ s/^\s+/ /;
2877                 # Strip comments
2878                 $line =~ s/#.*$//g;
2880                 # Skip empty lines
2881                 if ($line =~ /^\s*$/){
2882                         next;
2883                 }
2885                 # Interpret deb line
2886                 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2887                         my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2888                         my $section;
2889                         foreach $section (split(' ', $sections)){
2890                                 &parse_package_info( $baseurl, $dist, $section, $session_id );
2891                         }
2892                 }
2893         }
2895         close (CONFIG);
2897         if(keys(%repo_dirs)) {
2898                 find(\&cleanup_and_extract, keys( %repo_dirs ));
2899                 &main::strip_packages_list_statements();
2900                 $packages_list_db->exec_statementlist(\@packages_list_statements);
2901         }
2902         unlink($packages_list_under_construction);
2903         daemon_log("$session_id INFO: create_packages_list_db: finished", 5); 
2904         return;
2907 # This function should do some intensive task to minimize the db-traffic
2908 sub strip_packages_list_statements {
2909         my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2910         my @new_statement_list=();
2911         my $hash;
2912         my $insert_hash;
2913         my $update_hash;
2914         my $delete_hash;
2915         my $known_packages_hash;
2916         my $local_timestamp=get_time();
2918         foreach my $existing_entry (@existing_entries) {
2919                 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
2920         }
2922         foreach my $statement (@packages_list_statements) {
2923                 if($statement =~ /^INSERT/i) {
2924                         # Assign the values from the insert statement
2925                         my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~ 
2926                         /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
2927                         if(exists($hash->{$distribution}->{$package}->{$version})) {
2928                                 # If section or description has changed, update the DB
2929                                 if( 
2930                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or 
2931                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
2932                                 ) {
2933                                         @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
2934                                 } else {
2935                                         # package is already present in database. cache this knowledge for later use
2936                                         @{$known_packages_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2937                                 }
2938                         } else {
2939                                 # Insert a non-existing entry to db
2940                                 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2941                         }
2942                 } elsif ($statement =~ /^UPDATE/i) {
2943                         my ($template,$package,$version) = ($1,$2,$3) if $statement =~
2944                         /^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;
2945                         foreach my $distribution (keys %{$hash}) {
2946                                 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
2947                                         # update the insertion hash to execute only one query per package (insert instead insert+update)
2948                                         @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
2949                                 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
2950                                         if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
2951                                                 my $section;
2952                                                 my $description;
2953                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
2954                                                         length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
2955                                                         $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
2956                                                 }
2957                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2958                                                         $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
2959                                                 }
2960                                                 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2961                                         }
2962                                 }
2963                         }
2964                 }
2965         }
2967         # Check for orphaned entries
2968         foreach my $existing_entry (@existing_entries) {
2969                 my $distribution= @{$existing_entry}[0];
2970                 my $package= @{$existing_entry}[1];
2971                 my $version= @{$existing_entry}[2];
2972                 my $section= @{$existing_entry}[3];
2974                 if(
2975                         exists($insert_hash->{$distribution}->{$package}->{$version}) ||
2976                         exists($update_hash->{$distribution}->{$package}->{$version}) ||
2977                         exists($known_packages_hash->{$distribution}->{$package}->{$version})
2978                 ) {
2979                         next;
2980                 } else {
2981                         # Insert entry to delete hash
2982                         @{$delete_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section);
2983                 }
2984         }
2986         # unroll the insert hash
2987         foreach my $distribution (keys %{$insert_hash}) {
2988                 foreach my $package (keys %{$insert_hash->{$distribution}}) {
2989                         foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
2990                                 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
2991                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
2992                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
2993                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
2994                                 ."'$local_timestamp')";
2995                         }
2996                 }
2997         }
2999         # unroll the update hash
3000         foreach my $distribution (keys %{$update_hash}) {
3001                 foreach my $package (keys %{$update_hash->{$distribution}}) {
3002                         foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
3003                                 my $set = "";
3004                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
3005                                         $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
3006                                 }
3007                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
3008                                         $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
3009                                 }
3010                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
3011                                         $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
3012                                 }
3013                                 if(defined($set) and length($set) > 0) {
3014                                         $set .= "timestamp = '$local_timestamp'";
3015                                 } else {
3016                                         next;
3017                                 }
3018                                 push @new_statement_list, 
3019                                 "UPDATE $main::packages_list_tn SET $set WHERE"
3020                                 ." distribution = '$distribution'"
3021                                 ." AND package = '$package'"
3022                                 ." AND version = '$version'";
3023                         }
3024                 }
3025         }
3026         
3027         # unroll the delete hash
3028         foreach my $distribution (keys %{$delete_hash}) {
3029                 foreach my $package (keys %{$delete_hash->{$distribution}}) {
3030                         foreach my $version (keys %{$delete_hash->{$distribution}->{$package}}) {
3031                                 my $section = @{$delete_hash->{$distribution}->{$package}->{$version}}[3];
3032                                 push @new_statement_list, "DELETE FROM $main::packages_list_tn WHERE distribution='$distribution' AND package='$package' AND version='$version' AND section='$section'";
3033                         }
3034                 }
3035         }
3037         unshift(@new_statement_list, "VACUUM");
3039         @packages_list_statements = @new_statement_list;
3043 sub parse_package_info {
3044     my ($baseurl, $dist, $section, $session_id)= @_;
3045     my ($package);
3046     if (not defined $session_id) { $session_id = 0; }
3047     my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
3048     $repo_dirs{ "${repo_path}/pool" } = 1;
3050     foreach $package ("Packages.gz"){
3051         daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
3052         get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
3053         parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
3054     }
3055     
3059 sub get_package {
3060     my ($url, $dest, $session_id)= @_;
3061     if (not defined $session_id) { $session_id = 0; }
3063     my $tpath = dirname($dest);
3064     -d "$tpath" || mkpath "$tpath";
3066     # This is ugly, but I've no time to take a look at "how it works in perl"
3067     if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
3068         system("gunzip -cd '$dest' > '$dest.in'");
3069         daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 7);
3070         unlink($dest);
3071         daemon_log("$session_id DEBUG: delete file '$dest'", 7); 
3072     } else {
3073         daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' into '$dest' failed!", 1);
3074     }
3075     return 0;
3079 sub parse_package {
3080     my ($path, $dist, $srv_path, $session_id)= @_;
3081     if (not defined $session_id) { $session_id = 0;}
3082     my ($package, $version, $section, $description);
3083     my $PACKAGES;
3084     my $timestamp = &get_time();
3086     if(not stat("$path.in")) {
3087         daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
3088         return;
3089     }
3091     open($PACKAGES, "<$path.in");
3092     if(not defined($PACKAGES)) {
3093         daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1); 
3094         return;
3095     }
3097     # Read lines
3098     while (<$PACKAGES>){
3099         my $line = $_;
3100         # Unify
3101         chop($line);
3103         # Use empty lines as a trigger
3104         if ($line =~ /^\s*$/){
3105             my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
3106             push(@packages_list_statements, $sql);
3107             $package = "none";
3108             $version = "none";
3109             $section = "none";
3110             $description = "none"; 
3111             next;
3112         }
3114         # Trigger for package name
3115         if ($line =~ /^Package:\s/){
3116             ($package)= ($line =~ /^Package: (.*)$/);
3117             next;
3118         }
3120         # Trigger for version
3121         if ($line =~ /^Version:\s/){
3122             ($version)= ($line =~ /^Version: (.*)$/);
3123             next;
3124         }
3126         # Trigger for description
3127         if ($line =~ /^Description:\s/){
3128             ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
3129             next;
3130         }
3132         # Trigger for section
3133         if ($line =~ /^Section:\s/){
3134             ($section)= ($line =~ /^Section: (.*)$/);
3135             next;
3136         }
3138         # Trigger for filename
3139         if ($line =~ /^Filename:\s/){
3140             my ($filename) = ($line =~ /^Filename: (.*)$/);
3141             store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
3142             next;
3143         }
3144     }
3146     close( $PACKAGES );
3147     unlink( "$path.in" );
3151 sub store_fileinfo {
3152     my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
3154     my %fileinfo = (
3155         'package' => $package,
3156         'dist' => $dist,
3157         'version' => $vers,
3158     );
3160     $repo_files{ "${srvdir}/$file" } = \%fileinfo;
3164 sub cleanup_and_extract {
3165         my $fileinfo = $repo_files{ $File::Find::name };
3167         if( defined $fileinfo ) {
3168                 my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
3169                 my $sql;
3170                 my $package = $fileinfo->{ 'package' };
3171                 my $newver = $fileinfo->{ 'version' };
3173                 mkpath($dir);
3174                 system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
3176                 if( -f "$dir/DEBIAN/templates" ) {
3178                         daemon_log("0 DEBUG: Found debconf templates in '$package' - $newver", 7);
3180                         my $tmpl= ""; {
3181                                 local $/=undef;
3182                                 open FILE, "$dir/DEBIAN/templates";
3183                                 $tmpl = &encode_base64(<FILE>);
3184                                 close FILE;
3185                         }
3186                         rmtree("$dir/DEBIAN/templates");
3188                         $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
3189                         push @packages_list_statements, $sql;
3190                 }
3191         }
3193         return;
3197 sub register_at_foreign_servers {   
3198     my ($kernel) = $_[KERNEL];
3200         # Update status and update-time of all si-server with expired update_time and 
3201         # block them for race conditional registration processes of other si-servers.
3202         my $act_time = &get_time();
3203         my $block_statement = "UPDATE $known_server_tn SET status='new_server',update_time='19700101000000' WHERE (CAST(update_time AS UNSIGNED))<$act_time ";
3204         &daemon_log("0 DEBUG: $block_statement", 7);
3205         my $block_res = $known_server_db->exec_statement($block_statement);
3207         # Fetch all si-server from db where update_time is younger than act_time
3208         my $fetch_statement = "SELECT * FROM $known_server_tn WHERE update_time='19700101000000'"; 
3209         &daemon_log("0 DEBUG: $fetch_statement", 7);
3210         my $fetch_res = $known_server_db->exec_statement($fetch_statement);
3212     # Detect already connected clients. Will be added to registration msg later. 
3213     my $client_sql = "SELECT * FROM $known_clients_tn"; 
3214     my $client_res = $known_clients_db->exec_statement($client_sql);
3216         # Send registration messag to all fetched si-server
3217     foreach my $hit (@$fetch_res) {
3218         my $hostname = @$hit[0];
3219         my $hostkey = &create_passwd;
3221         # Add already connected clients to registration message 
3222         my $myhash = &create_xml_hash('new_server', $server_address, $hostname);
3223         &add_content2xml_hash($myhash, 'key', $hostkey);
3224         map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res);
3226         # Add locally loaded gosa-si modules to registration message
3227         my $loaded_modules = {};
3228         while (my ($package, $pck_info) = each %$known_modules) {
3229                         next if ((!defined(@$pck_info[2])) || (!(ref (@$pck_info[2]) eq 'HASH')));
3230                         foreach my $act_module (keys(%{@$pck_info[2]})) {
3231                                 $loaded_modules->{$act_module} = ""; 
3232                         }
3233                 }
3234         map(&add_content2xml_hash($myhash, "loaded_modules", $_), keys(%$loaded_modules));
3236         # Add macaddress to registration message
3237         my ($host_ip, $host_port) = split(/:/, $hostname);
3238         my $local_ip = &get_local_ip_for_remote_ip($host_ip);
3239         my $network_interface= &get_interface_for_ip($local_ip);
3240         my $host_mac = &get_mac_for_interface($network_interface);
3241         &add_content2xml_hash($myhash, 'macaddress', $host_mac);
3242         
3243         # Build registration message and send it
3244         my $foreign_server_msg = &create_xml_string($myhash);
3245         my $error = &send_msg_to_target($foreign_server_msg, $hostname, $ServerPackages_key, "new_server", 0); 
3246     }
3249         # After n sec perform a check of all server registration processes
3250     $kernel->delay_set("control_server_registration", 2); 
3252         return;
3256 sub control_server_registration {
3257         my ($kernel) = $_[KERNEL];
3258         
3259         # Check if all registration processes succeed or not
3260         my $select_statement = "SELECT * FROM $known_server_tn WHERE status='new_server'"; 
3261         &daemon_log("0 DEBUG $select_statement", 7);
3262         my $select_res = $known_server_db->exec_statement($select_statement);
3264         # If at least one registration process failed, maybe in case of a race condition
3265         # with a foreign registration process
3266         if (@$select_res > 0) 
3267         {
3268                 # Release block statement 'new_server' to make the server accessible
3269                 # for foreign registration processes
3270                 my $update_statement = "UPDATE $known_server_tn SET status='waiting' WHERE status='new_server'";        
3271                 &daemon_log("0 DEBUG: $update_statement", 7);
3272                 my $update_res = $known_server_db->exec_statement($update_statement);
3274                 # Set a random delay to avoid the registration race condition
3275                 my $new_foreign_servers_register_delay = int(rand(4))+1;
3276                 $kernel->delay_set("register_at_foreign_servers", $new_foreign_servers_register_delay);
3277         }
3278         # If all registration processes succeed
3279         else
3280         {
3281                 $kernel->delay_set("register_at_foreign_servers", $foreign_servers_register_delay);
3282         }
3284         return;
3288 #==== MAIN = main ==============================================================
3289 #  parse commandline options
3290 Getopt::Long::Configure( "bundling" );
3291 GetOptions("h|help" => \&usage,
3292         "c|config=s" => \$cfg_file,
3293         "f|foreground" => \$foreground,
3294         "v|verbose+" => \$verbose,
3295         "no-arp+" => \$no_arp,
3296            );
3298 #  read and set config parameters
3299 &check_cmdline_param ;
3300 &read_configfile($cfg_file, %cfg_defaults);
3301 &check_pid;
3303 $SIG{CHLD} = 'IGNORE';
3305 # forward error messages to logfile
3306 if( ! $foreground ) {
3307   open( STDIN,  '+>/dev/null' );
3308   open( STDOUT, '+>&STDIN'    );
3309   open( STDERR, '+>&STDIN'    );
3312 # Just fork, if we are not in foreground mode
3313 if( ! $foreground ) { 
3314     chdir '/'                 or die "Can't chdir to /: $!";
3315     $pid = fork;
3316     setsid                    or die "Can't start a new session: $!";
3317     umask 0;
3318 } else { 
3319     $pid = $$; 
3322 # Do something useful - put our PID into the pid_file
3323 if( 0 != $pid ) {
3324     open( LOCK_FILE, ">$pid_file" );
3325     print LOCK_FILE "$pid\n";
3326     close( LOCK_FILE );
3327     if( !$foreground ) { 
3328         exit( 0 ) 
3329     };
3332 # parse head url and revision from svn
3333 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
3334 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
3335 $server_headURL = defined $1 ? $1 : 'unknown' ;
3336 $server_revision = defined $2 ? $2 : 'unknown' ;
3337 if ($server_headURL =~ /\/tag\// || 
3338         $server_headURL =~ /\/branches\// ) {
3339     $server_status = "stable"; 
3340 } else {
3341     $server_status = "developmental" ;
3343 # Prepare log file and set permissions
3344 $root_uid = getpwnam('root');
3345 $adm_gid = getgrnam('adm');
3346 open(FH, ">>$log_file");
3347 close FH;
3348 chmod(0440, $log_file);
3349 chown($root_uid, $adm_gid, $log_file);
3350 chown($root_uid, $adm_gid, "/var/lib/gosa-si");
3352 daemon_log(" ", 1);
3353 daemon_log("$0 started!", 1);
3354 daemon_log("status: $server_status", 1);
3355 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1); 
3357 # Buildup data bases
3359     no strict "refs";
3361     if ($db_module eq "DBmysql") {
3362         # connect to incoming_db
3363         $incoming_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3365         # connect to gosa-si job queue
3366         $job_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3368         # connect to known_clients_db
3369         $known_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3371         # connect to foreign_clients_db
3372         $foreign_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3374         # connect to known_server_db
3375         $known_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3377         # connect to login_usr_db
3378         $login_users_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3380         # connect to fai_server_db 
3381         $fai_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3383         # connect to fai_release_db
3384         $fai_release_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3386         # connect to packages_list_db
3387         $packages_list_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3389         # connect to messaging_db
3390         $messaging_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3392     } elsif ($db_module eq "DBsqlite") {
3393         # connect to incoming_db
3394         unlink($incoming_file_name);
3395         $incoming_db = GOSA::DBsqlite->new($incoming_file_name);
3396         chmod(0640, $incoming_file_name);
3397         chown($root_uid, $adm_gid, $incoming_file_name);
3398         
3399         # connect to gosa-si job queue
3400         $job_db = GOSA::DBsqlite->new($job_queue_file_name);
3401         chmod(0640, $job_queue_file_name);
3402         chown($root_uid, $adm_gid, $job_queue_file_name);
3403         
3404         # connect to known_clients_db
3405         #unlink($known_clients_file_name);
3406         $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
3407         chmod(0640, $known_clients_file_name);
3408         chown($root_uid, $adm_gid, $known_clients_file_name);
3409         
3410         # connect to foreign_clients_db
3411         #unlink($foreign_clients_file_name);
3412         $foreign_clients_db = GOSA::DBsqlite->new($foreign_clients_file_name);
3413         chmod(0640, $foreign_clients_file_name);
3414         chown($root_uid, $adm_gid, $foreign_clients_file_name);
3415         
3416         # connect to known_server_db
3417         #unlink($known_server_file_name);
3418         $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
3419         chmod(0640, $known_server_file_name);
3420         chown($root_uid, $adm_gid, $known_server_file_name);
3421         
3422         # connect to login_usr_db
3423         #unlink($login_users_file_name);
3424         $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
3425         chmod(0640, $login_users_file_name);
3426         chown($root_uid, $adm_gid, $login_users_file_name);
3427         
3428         # connect to fai_server_db
3429         unlink($fai_server_file_name);
3430         $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
3431         chmod(0640, $fai_server_file_name);
3432         chown($root_uid, $adm_gid, $fai_server_file_name);
3433         
3434         # connect to fai_release_db
3435         unlink($fai_release_file_name);
3436         $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
3437         chmod(0640, $fai_release_file_name);
3438         chown($root_uid, $adm_gid, $fai_release_file_name);
3439         
3440         # connect to packages_list_db
3441         unlink($packages_list_under_construction);
3442         $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
3443         chmod(0640, $packages_list_file_name);
3444         chown($root_uid, $adm_gid, $packages_list_file_name);
3445         
3446         # connect to messaging_db
3447         #unlink($messaging_file_name);
3448         $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
3449         chmod(0640, $messaging_file_name);
3450         chown($root_uid, $adm_gid, $messaging_file_name);
3451     }
3455 # Creating tables
3456 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
3457 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
3458 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
3459 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
3460 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
3461 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
3462 $foreign_clients_db->create_table($foreign_clients_tn, \@foreign_clients_col_names);
3463 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
3464 $incoming_db->create_table($incoming_tn, \@incoming_col_names);
3465 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
3467 # create xml object used for en/decrypting
3468 $xml = new XML::Simple();
3471 # foreign servers 
3472 my @foreign_server_list;
3474 # add foreign server from cfg file
3475 if ($foreign_server_string ne "") {
3476     my @cfg_foreign_server_list = split(",", $foreign_server_string);
3477     foreach my $foreign_server (@cfg_foreign_server_list) {
3478         push(@foreign_server_list, $foreign_server);
3479     }
3481     daemon_log("0 INFO: found foreign server in config file: ".join(", ", @foreign_server_list), 5);
3484 # Perform a DNS lookup for server registration if flag is true
3485 if ($dns_lookup eq "true") {
3486     # Add foreign server from dns
3487     my @tmp_servers;
3488     if (not $server_domain) {
3489         # Try our DNS Searchlist
3490         for my $domain(get_dns_domains()) {
3491             chomp($domain);
3492             my ($tmp_domains, $error_string) = &get_server_addresses($domain);
3493             if(@$tmp_domains) {
3494                 for my $tmp_server(@$tmp_domains) {
3495                     push @tmp_servers, $tmp_server;
3496                 }
3497             }
3498         }
3499         if(@tmp_servers && length(@tmp_servers)==0) {
3500             daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3501         }
3502     } else {
3503         @tmp_servers = &get_server_addresses($server_domain);
3504         if( 0 == @tmp_servers ) {
3505             daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3506         }
3507     }
3509     daemon_log("0 INFO: found foreign server via DNS ".join(", ", @tmp_servers), 5);    
3511     foreach my $server (@tmp_servers) { 
3512         unshift(@foreign_server_list, $server); 
3513     }
3514 } else {
3515     daemon_log("0 INFO: DNS lookup for server registration is disabled", 5);
3519 # eliminate duplicate entries
3520 @foreign_server_list = &del_doubles(@foreign_server_list);
3521 my $all_foreign_server = join(", ", @foreign_server_list);
3522 daemon_log("0 INFO: found foreign server in config file and DNS: '$all_foreign_server'", 5);
3524 # add all found foreign servers to known_server
3525 my $cur_timestamp = &get_time();
3526 foreach my $foreign_server (@foreign_server_list) {
3528         # do not add myself to known_server_db
3529         if (&is_local($foreign_server)) { next; }
3530         ######################################
3532     my $res = $known_server_db->add_dbentry( {table=>$known_server_tn, 
3533             primkey=>['hostname'],
3534             hostname=>$foreign_server,
3535             macaddress=>"",
3536             status=>'not_yet_registered',
3537             hostkey=>"none",
3538             loaded_modules => "none", 
3539             timestamp=>$cur_timestamp,
3540                         update_time=>'19700101000000',
3541             } );
3545 # Import all modules
3546 &import_modules;
3548 # Check wether all modules are gosa-si valid passwd check
3549 &password_check;
3551 # Create functions hash
3552 #print STDERR Dumper $known_modules;
3553 while (my ($module, @mod_info) = each %$known_modules) 
3555 #print STDERR Dumper $module;
3556         while (my ($plugin, $functions) = each %{$mod_info[0][2]})
3557         {
3558 #print STDERR Dumper $functions;
3559                 while (my ($function, $nothing) = each %$functions )
3560                 {
3561                         $known_functions->{$function} = $nothing;
3562                 }
3563         }
3566 # Prepare for using Opsi 
3567 if ($opsi_enabled eq "true") {
3568     use JSON::RPC::Client;
3569     use XML::Quote qw(:all);
3570     $opsi_url= "https://".$opsi_admin.":".$opsi_password."@".$opsi_server.":4447/rpc";
3571     $opsi_client = new JSON::RPC::Client;
3575 POE::Component::Server::TCP->new(
3576         Alias => "TCP_SERVER",
3577         Port => $server_port,
3578         ClientInput => sub {
3579                 my ($kernel, $input, $heap, $session) = @_[KERNEL, ARG0, HEAP, SESSION];
3580         my $session_id = $session->ID;
3581                 if ($input =~ /;([\d\.]+:[\d]+)$/) 
3582                 {
3583                         &daemon_log("$session_id DEBUG: incoming message from '$1'", 7);
3584                 }
3585                 else
3586                 {
3587                         my $remote_ip = $heap->{'remote_ip'};
3588                         &daemon_log("$session_id DEBUG: incoming message from '$remote_ip'", 7);
3589                 }
3590                 push(@msgs_to_decrypt, $input);
3591                 $kernel->yield("msg_to_decrypt");
3592         },
3593         InlineStates => {
3594                 msg_to_decrypt => \&msg_to_decrypt,
3595                 next_task => \&next_task,
3596                 task_result => \&handle_task_result,
3597                 task_done   => \&handle_task_done,
3598                 task_debug  => \&handle_task_debug,
3599                 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3600         }
3601 );
3603 daemon_log("0 INFO: start socket for incoming xml messages at port '$server_port' ", 1);
3605 # create session for repeatedly checking the job queue for jobs
3606 POE::Session->create(
3607         inline_states => {
3608                 _start => \&session_start,
3609         register_at_foreign_servers => \&register_at_foreign_servers,
3610                 control_server_registration => \&control_server_registration,
3611         sig_handler => \&sig_handler,
3612         next_task => \&next_task,
3613         task_result => \&handle_task_result,
3614         task_done   => \&handle_task_done,
3615         task_debug  => \&handle_task_debug,
3616         watch_for_next_tasks => \&watch_for_next_tasks,
3617         watch_for_new_messages => \&watch_for_new_messages,
3618         watch_for_delivery_messages => \&watch_for_delivery_messages,
3619         watch_for_done_messages => \&watch_for_done_messages,
3620                 watch_for_new_jobs => \&watch_for_new_jobs,
3621         watch_for_modified_jobs => \&watch_for_modified_jobs,
3622         watch_for_done_jobs => \&watch_for_done_jobs,
3623         watch_for_opsi_jobs => \&watch_for_opsi_jobs,
3624         watch_for_old_known_clients => \&watch_for_old_known_clients,
3625         create_packages_list_db => \&run_create_packages_list_db,
3626         create_fai_server_db => \&run_create_fai_server_db,
3627         create_fai_release_db => \&run_create_fai_release_db,
3628                 recreate_packages_db => \&run_recreate_packages_db,
3629         session_run_result => \&session_run_result,
3630         session_run_debug => \&session_run_debug,
3631         session_run_done => \&session_run_done,
3632         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3633         }
3634 );
3637 POE::Kernel->run();
3638 exit;