Code

update to config option ServerPackages - enabled = false
[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 $debug_parts = 0;
80 my $debug_parts_bitstring;
81 my ($xml);
82 my $sources_list;
83 my $max_clients;
84 my %repo_files=();
85 my $repo_path;
86 my %repo_dirs=();
88 # Variables declared in config file are always set to 'our'
89 our (%cfg_defaults, $log_file, $pid_file, 
90     $server_ip, $server_port, $ClientPackages_key, $dns_lookup,
91     $arp_activ, $gosa_unit_tag,
92     $GosaPackages_key, $gosa_timeout,
93     $serverPackages_enabled, $foreign_server_string, $server_domain, $ServerPackages_key, $foreign_servers_register_delay,
94     $wake_on_lan_passwd, $job_synchronization, $modified_jobs_loop_delay,
95     $arp_enabled, $arp_interface,
96     $opsi_enabled, $opsi_server, $opsi_admin, $opsi_password,
97                 $new_systems_ou,
98 );
100 # additional variable which should be globaly accessable
101 our $server_address;
102 our $server_mac_address;
103 our $gosa_address;
104 our $no_arp;
105 our $forground;
106 our $cfg_file;
107 our ($ldap_uri, $ldap_base, $ldap_admin_dn, $ldap_admin_password, $ldap_server_dn, $ldap_version);
108 our ($mysql_username, $mysql_password, $mysql_database, $mysql_host);
109 our $known_modules;
110 our $known_functions;
111 our $root_uid;
112 our $adm_gid;
114 # if foreground is not null, script will be not forked to background
115 $foreground = 0 ;
117 # specifies the timeout seconds while checking the online status of a registrating client
118 $ping_timeout = 5;
120 $no_arp = 0;
121 my $packages_list_under_construction = "/tmp/packages_list_creation_in_progress";
122 my @packages_list_statements;
123 my $watch_for_new_jobs_in_progress = 0;
125 # holds all incoming decrypted messages
126 our $incoming_db;
127 our $incoming_tn = 'incoming';
128 my $incoming_file_name;
129 my @incoming_col_names = ("id INTEGER PRIMARY KEY",
130         "timestamp VARCHAR(14) DEFAULT 'none'", 
131         "headertag VARCHAR(255) DEFAULT 'none'",
132         "targettag VARCHAR(255) DEFAULT 'none'",
133         "xmlmessage TEXT",
134         "module VARCHAR(255) DEFAULT 'none'",
135         "sessionid VARCHAR(255) DEFAULT '0'",
136 );
138 # holds all gosa jobs
139 our $job_db;
140 our $job_queue_tn = 'jobs';
141 my $job_queue_file_name;
142 my @job_queue_col_names = ("id INTEGER PRIMARY KEY",
143         "timestamp VARCHAR(14) DEFAULT 'none'", 
144         "status VARCHAR(255) DEFAULT 'none'", 
145         "result TEXT",
146         "progress VARCHAR(255) DEFAULT 'none'",
147         "headertag VARCHAR(255) DEFAULT 'none'",
148         "targettag VARCHAR(255) DEFAULT 'none'", 
149         "xmlmessage TEXT", 
150         "macaddress VARCHAR(17) DEFAULT 'none'",
151         "plainname VARCHAR(255) DEFAULT 'none'",
152         "siserver VARCHAR(255) DEFAULT 'none'",
153         "modified INTEGER DEFAULT '0'",
154 );
156 # holds all other gosa-si-server
157 our $known_server_db;
158 our $known_server_tn = "known_server";
159 my $known_server_file_name;
160 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)");
162 # holds all registrated clients
163 our $known_clients_db;
164 our $known_clients_tn = "known_clients";
165 my $known_clients_file_name;
166 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)");
168 # holds all registered clients at a foreign server
169 our $foreign_clients_db;
170 our $foreign_clients_tn = "foreign_clients"; 
171 my $foreign_clients_file_name;
172 my @foreign_clients_col_names = ("hostname VARCHAR(255)", "macaddress VARCHAR(17)", "regserver VARCHAR(255)", "timestamp VARCHAR(14)");
174 # holds all logged in user at each client 
175 our $login_users_db;
176 our $login_users_tn = "login_users";
177 my $login_users_file_name;
178 my @login_users_col_names = ("client VARCHAR(255)", "user VARCHAR(255)", "timestamp VARCHAR(14)", "regserver VARCHAR(255) DEFAULT 'localhost'");
180 # holds all fai server, the debian release and tag
181 our $fai_server_db;
182 our $fai_server_tn = "fai_server"; 
183 my $fai_server_file_name;
184 our @fai_server_col_names = ("timestamp VARCHAR(14)", "server VARCHAR(255)", "fai_release VARCHAR(255)", "sections VARCHAR(255)", "tag VARCHAR(255)"); 
186 our $fai_release_db;
187 our $fai_release_tn = "fai_release"; 
188 my $fai_release_file_name;
189 our @fai_release_col_names = ("timestamp VARCHAR(14)", "fai_release VARCHAR(255)", "class VARCHAR(255)", "type VARCHAR(255)", "state VARCHAR(255)"); 
191 # holds all packages available from different repositories
192 our $packages_list_db;
193 our $packages_list_tn = "packages_list";
194 my $packages_list_file_name;
195 our @packages_list_col_names = ("distribution VARCHAR(255)", "package VARCHAR(255)", "version VARCHAR(255)", "section VARCHAR(255)", "description TEXT", "template LONGBLOB", "timestamp VARCHAR(14)");
196 my $outdir = "/tmp/packages_list_db";
197 my $arch = "i386"; 
199 # holds all messages which should be delivered to a user
200 our $messaging_db;
201 our $messaging_tn = "messaging"; 
202 our @messaging_col_names = ("id INTEGER", "subject TEXT", "message_from VARCHAR(255)", "message_to VARCHAR(255)", 
203         "flag VARCHAR(255)", "direction VARCHAR(255)", "delivery_time VARCHAR(255)", "message TEXT", "timestamp VARCHAR(14)" );
204 my $messaging_file_name;
206 # path to directory to store client install log files
207 our $client_fai_log_dir = "/var/log/fai"; 
209 # queue which stores taskes until one of the $max_children children are ready to process the task
210 #my @tasks = qw();
211 my @msgs_to_decrypt = qw();
212 my $max_children = 2;
215 # loop delay for job queue to look for opsi jobs
216 my $job_queue_opsi_delay = 10;
217 our $opsi_client;
218 our $opsi_url;
219  
220 # Lifetime of logged in user information. If no update information comes after n seconds, 
221 # the user is expeceted to be no longer logged in or the host is no longer running. Because
222 # of this, the user is deleted from login_users_db
223 our $logged_in_user_date_of_expiry = 600;
225 # List of month names, used in function daemon_log
226 my @monthnames = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
228 %cfg_defaults = (
229 "general" => {
230     "log-file" => [\$log_file, "/var/run/".$prg.".log"],
231     "pid-file" => [\$pid_file, "/var/run/".$prg.".pid"],
232     },
233 "server" => {
234     "ip"                    => [\$server_ip, "0.0.0.0"],
235     "port"                  => [\$server_port, "20081"],
236     "known-clients"         => [\$known_clients_file_name, '/var/lib/gosa-si/clients.db' ],
237     "known-servers"         => [\$known_server_file_name, '/var/lib/gosa-si/servers.db'],
238     "incoming"              => [\$incoming_file_name, '/var/lib/gosa-si/incoming.db'],
239     "login-users"           => [\$login_users_file_name, '/var/lib/gosa-si/users.db'],
240     "fai-server"            => [\$fai_server_file_name, '/var/lib/gosa-si/fai_server.db'],
241     "fai-release"           => [\$fai_release_file_name, '/var/lib/gosa-si/fai_release.db'],
242     "packages-list"         => [\$packages_list_file_name, '/var/lib/gosa-si/packages.db'],
243     "messaging"             => [\$messaging_file_name, '/var/lib/gosa-si/messaging.db'],
244     "foreign-clients"       => [\$foreign_clients_file_name, '/var/lib/gosa-si/foreign_clients.db'],
245     "source-list"           => [\$sources_list, '/etc/apt/sources.list'],
246     "repo-path"             => [\$repo_path, '/srv/www/repository'],
247     "ldap-uri"              => [\$ldap_uri, ""],
248     "ldap-base"             => [\$ldap_base, ""],
249     "ldap-admin-dn"         => [\$ldap_admin_dn, ""],
250     "ldap-admin-password"   => [\$ldap_admin_password, ""],
251     "ldap-version"          => [\$ldap_version, 3],
252     "gosa-unit-tag"         => [\$gosa_unit_tag, ""],
253     "max-clients"           => [\$max_clients, 10],
254     "wol-password"          => [\$wake_on_lan_passwd, ""],
255         "mysql-username"        => [\$mysql_username, "gosa_si"],
256         "mysql-password"        => [\$mysql_password, ""],
257         "mysql-database"        => [\$mysql_database, "gosa_si"],
258         "mysql-host"            => [\$mysql_host, "127.0.0.1"],
259     },
260 "GOsaPackages" => {
261     "job-queue" => [\$job_queue_file_name, '/var/lib/gosa-si/jobs.db'],
262     "job-queue-loop-delay" => [\$job_queue_loop_delay, 3],
263     "messaging-db-loop-delay" => [\$messaging_db_loop_delay, 3],
264     "key" => [\$GosaPackages_key, "none"],
265                 "new-systems-ou" => [\$new_systems_ou, 'ou=workstations,ou=systems'],
266     },
267 "ClientPackages" => {
268     "key" => [\$ClientPackages_key, "none"],
269     "user-date-of-expiry" => [\$logged_in_user_date_of_expiry, 600],
270     },
271 "ServerPackages"=> {
272         "enabled" => [\$serverPackages_enabled, "true"],
273     "address"      => [\$foreign_server_string, ""],
274     "dns-lookup"            => [\$dns_lookup, "true"],
275     "domain"  => [\$server_domain, ""],
276     "key"     => [\$ServerPackages_key, "none"],
277     "key-lifetime" => [\$foreign_servers_register_delay, 120],
278     "job-synchronization-enabled" => [\$job_synchronization, "true"],
279     "synchronization-loop" => [\$modified_jobs_loop_delay, 5],
280     },
281 "ArpHandler" => {
282     "enabled"   => [\$arp_enabled, "true"],
283     "interface" => [\$arp_interface, "all"],
284         },
285 "Opsi" => {
286     "enabled"  => [\$opsi_enabled, "false"], 
287     "server"   => [\$opsi_server, "localhost"],
288     "admin"    => [\$opsi_admin, "opsi-admin"],
289     "password" => [\$opsi_password, "secret"],
290    },
292 );
295 #===  FUNCTION  ================================================================
296 #         NAME:  usage
297 #   PARAMETERS:  nothing
298 #      RETURNS:  nothing
299 #  DESCRIPTION:  print out usage text to STDERR
300 #===============================================================================
301 sub usage {
302     print STDERR << "EOF" ;
303 usage: $prg [-hvf] [-c config] [-d number]
305            -h        : this (help) message
306            -c <file> : config file
307            -f        : foreground, process will not be forked to background
308            -v        : be verbose (multiple to increase verbosity)
309                               'v': error logs
310                             'vvv': warning plus error logs                                              
311                           'vvvvv': info plus warning logs
312                         'vvvvvvv': debug plus info logs
313            -no-arp   : starts $prg without connection to arp module
314            -d <int>  : if verbose level is higher than 7x 'v' specified parts can be debugged
315                            1 : receiving messages
316                            2 : sending messages
317                            4 : encrypting/decrypting messages
318                            8 : verification if a message complies gosa-si requirements
319                           16 : message processing
320                           32 : ldap connectivity
321                           64 : database status and connectivity
322                          128 : main process 
323  
324 EOF
325     print "\n" ;
329 #===  FUNCTION  ================================================================
330 #         NAME:  logging
331 #   PARAMETERS:  level - string - default 'info'
332 #                msg - string -
333 #                facility - string - default 'LOG_DAEMON'
334 #      RETURNS:  nothing
335 #  DESCRIPTION:  function for logging
336 #===============================================================================
337 sub daemon_log {
338     my( $msg, $level ) = @_;
339     if (not defined $msg) { return }
340     if (not defined $level) { $level = 1 }
341         my $to_be_logged = 0;
343         # Write log line if line level is lower than verbosity given in commandline
344         if ($level <= $verbose) 
345         { 
346                 $to_be_logged = 1 ;
347         }
349         # Write if debug flag is set and bitstring matches
350         if ($debug_parts > 0)
351         {
352                 my $tmp_level = ($level - 10 >= 0) ? $level - 10 : 0 ;
353                 my $tmp_level_bitstring = unpack("B32", pack("N", $tmp_level));
354                 if (int($debug_parts_bitstring & $tmp_level_bitstring)) 
355                 {
356                         $to_be_logged = 1;
357                 }
358         }
360         if ($to_be_logged) 
361         {
362                 if(defined $log_file){
363                         my $open_log_fh = sysopen(LOG_HANDLE, $log_file, O_WRONLY | O_CREAT | O_APPEND , 0440);
364                         if(not $open_log_fh) {
365                                 print STDERR "cannot open $log_file: $!";
366                                 return;
367                         }
368                         # Check owner and group of log_file and update settings if necessary
369                         my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = stat($log_file);
370                         if((not $uid eq $root_uid) || (not $gid eq $adm_gid)) {
371                                 chown($root_uid, $adm_gid, $log_file);
372                         }
374                         # Prepare time string for log message
375                         my ($seconds,$minutes,$hours,$monthday,$month,$year,$weekday,$yearday,$sommertime) = localtime(time);
376                         $hours = $hours < 10 ? $hours = "0".$hours : $hours;
377                         $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
378                         $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
379                         $month = $monthnames[$month];
380                         $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
381                         $year+=1900;
383                         
384                         # Build log message and write it to log file and commandline
385                         chomp($msg);
386                         my $log_msg = "$month $monthday $hours:$minutes:$seconds $prg $msg\n";
387                         flock(LOG_HANDLE, LOCK_EX);
388                         seek(LOG_HANDLE, 0, 2);
389                         print LOG_HANDLE $log_msg;
390                         flock(LOG_HANDLE, LOCK_UN);
391                         if( $foreground ) 
392                         { 
393                                 print STDERR $log_msg;
394                         }
395                         close( LOG_HANDLE );
396                 }
397         }
401 #===  FUNCTION  ================================================================
402 #         NAME:  check_cmdline_param
403 #   PARAMETERS:  nothing
404 #      RETURNS:  nothing
405 #  DESCRIPTION:  validates commandline parameter
406 #===============================================================================
407 sub check_cmdline_param () {
408     my $err_counter = 0;
410         # Check configuration file
411         if(not defined($cfg_file)) {
412                 $cfg_file = "/etc/gosa-si/server.conf";
413                 if(! -r $cfg_file) {
414                         print STDERR "Please specify a config file.\n";
415                         $err_counter++;
416                 }
417     }
419         # Prepare identification which gosa-si parts should be debugged and which not
420         if (defined $debug_parts) 
421         {
422                 if ($debug_parts =~ /^\d+$/)
423                 {
424                         $debug_parts_bitstring = unpack("B32", pack("N", $debug_parts));
425                 }
426                 else
427                 {
428                         print STDERR "Value '$debug_parts' invalid for option d (number expected)\n";
429                         $err_counter++;
430                 }
431         }
433         # Exit if an error occour
434     if( $err_counter > 0 ) 
435         {
436         &usage( "", 1 );
437         exit( -1 );
438     }
442 #===  FUNCTION  ================================================================
443 #         NAME:  check_pid
444 #   PARAMETERS:  nothing
445 #      RETURNS:  nothing
446 #  DESCRIPTION:  handels pid processing
447 #===============================================================================
448 sub check_pid {
449     $pid = -1;
450     # Check, if we are already running
451     if( open(LOCK_FILE, "<$pid_file") ) {
452         $pid = <LOCK_FILE>;
453         if( defined $pid ) {
454             chomp( $pid );
455             if( -f "/proc/$pid/stat" ) {
456                 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
457                 if( $stat ) {
458                                         print STDERR "\nERROR: Already running!\n";
459                     close( LOCK_FILE );
460                     exit -1;
461                 }
462             }
463         }
464         close( LOCK_FILE );
465         unlink( $pid_file );
466     }
468     # create a syslog msg if it is not to possible to open PID file
469     if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
470         my($msg) = "Couldn't obtain lockfile '$pid_file' ";
471         if (open(LOCK_FILE, '<', $pid_file)
472                 && ($pid = <LOCK_FILE>))
473         {
474             chomp($pid);
475             $msg .= "(PID $pid)\n";
476         } else {
477             $msg .= "(unable to read PID)\n";
478         }
479         if( ! ($foreground) ) {
480             openlog( $0, "cons,pid", "daemon" );
481             syslog( "warning", $msg );
482             closelog();
483         }
484         else {
485             print( STDERR " $msg " );
486         }
487         exit( -1 );
488     }
491 #===  FUNCTION  ================================================================
492 #         NAME:  import_modules
493 #   PARAMETERS:  module_path - string - abs. path to the directory the modules 
494 #                are stored
495 #      RETURNS:  nothing
496 #  DESCRIPTION:  each file in module_path which ends with '.pm' and activation 
497 #                state is on is imported by "require 'file';"
498 #===============================================================================
499 sub import_modules {
500     if (not -e $modules_path) {
501         daemon_log("0 ERROR: cannot find directory or directory is not readable: $modules_path", 1);   
502     }
504     opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
506     while (defined (my $file = readdir (DIR))) {
507         if (not $file =~ /(\S*?).pm$/) {
508             next;
509         }
510                 my $mod_name = $1;
512         # ArpHandler switch
513         if( $file =~ /ArpHandler.pm/ ) {
514             if( $arp_enabled eq "false" ) { next; }
515         }
517                 # ServerPackages switch
518                 if ($file eq "ServerPackages.pm" && $serverPackages_enabled eq "false") 
519                 {
520                         $dns_lookup = "false";
521                         next; 
522                 }
523         
524         eval { require $file; };
525         if ($@) {
526             daemon_log("0 ERROR: gosa-si-server could not load module $file", 1);
527             daemon_log("$@", 1);
528             exit;
529                 } else {
530                         my $info = eval($mod_name.'::get_module_info()');
531                         # Only load module if get_module_info() returns a non-null object
532                         if( $info ) {
533                                 my ($input_address, $input_key, $event_hash) = @{$info};
534                                 $known_modules->{$mod_name} = $info;
535                                 daemon_log("0 INFO: module $mod_name loaded", 5);
536                         }
537                 }
538     }   
539     close (DIR);
542 #===  FUNCTION  ================================================================
543 #         NAME:  password_check
544 #   PARAMETERS:  nothing
545 #      RETURNS:  nothing
546 #  DESCRIPTION:  escalates an critical error if two modules exist which are avaialable by 
547 #                the same password
548 #===============================================================================
549 sub password_check {
550     my $passwd_hash = {};
551     while (my ($mod_name, $mod_info) = each %$known_modules) {
552         my $mod_passwd = @$mod_info[1];
553         if (not defined $mod_passwd) { next; }
554         if (not exists $passwd_hash->{$mod_passwd}) {
555             $passwd_hash->{$mod_passwd} = $mod_name;
557         # escalates critical error
558         } else {
559             &daemon_log("0 ERROR: two loaded modules do have the same password. Please modify the 'key'-parameter in config file");
560             &daemon_log("0 ERROR: module='$mod_name' and module='".$passwd_hash->{$mod_passwd}."'");
561             exit( -1 );
562         }
563     }
568 #===  FUNCTION  ================================================================
569 #         NAME:  sig_int_handler
570 #   PARAMETERS:  signal - string - signal arose from system
571 #      RETURNS:  nothing
572 #  DESCRIPTION:  handels tasks to be done befor signal becomes active
573 #===============================================================================
574 sub sig_int_handler {
575     my ($signal) = @_;
577 #       if (defined($ldap_handle)) {
578 #               $ldap_handle->disconnect;
579 #       }
580     # TODO alle verbliebenden ldap verbindungen aus allen heaps beenden
581     
583     daemon_log("shutting down gosa-si-server", 1);
584     system("kill `ps -C gosa-si-server -o pid=`");
586 $SIG{INT} = \&sig_int_handler;
589 sub check_key_and_xml_validity {
590     my ($crypted_msg, $module_key, $session_id) = @_;
591     my $msg;
592     my $msg_hash;
593     my $error_string;
594     eval{
595         $msg = &decrypt_msg($crypted_msg, $module_key);
597         if ($msg =~ /<xml>/i){
598             $msg =~ s/\s+/ /g;  # just for better daemon_log
599             daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 18);
600             $msg_hash = $xml->XMLin($msg, ForceArray=>1);
602             ##############
603             # check header
604             if( not exists $msg_hash->{'header'} ) { die "no header specified"; }
605             my $header_l = $msg_hash->{'header'};
606             if( (1 > @{$header_l}) || ( ( 'HASH' eq ref @{$header_l}[0]) && (1 > keys %{@{$header_l}[0]}) ) ) { die 'empty header tag'; }
607             if( 1 < @{$header_l} ) { die 'more than one header specified'; }
608             my $header = @{$header_l}[0];
609             if( 0 == length $header) { die 'empty string in header tag'; }
611             ##############
612             # check source
613             if( not exists $msg_hash->{'source'} ) { die "no source specified"; }
614             my $source_l = $msg_hash->{'source'};
615             if( (1 > @{$source_l}) || ( ( 'HASH' eq ref @{$source_l}[0]) && (1 > keys %{@{$source_l}[0]}) ) ) { die 'empty source tag'; }
616             if( 1 < @{$source_l} ) { die 'more than one source specified'; }
617             my $source = @{$source_l}[0];
618             if( 0 == length $source) { die 'source error'; }
620             ##############
621             # check target
622             if( not exists $msg_hash->{'target'} ) { die "no target specified"; }
623             my $target_l = $msg_hash->{'target'};
624             if( (1 > @{$target_l}) || ( ('HASH' eq ref @{$target_l}[0]) && (1 > keys %{@{$target_l}[0]}) ) ) { die 'empty target tag'; }
625         }
626     };
627     if($@) {
628         daemon_log("$session_id ERROR: do not understand the message: $@", 1);
629         $msg = undef;
630         $msg_hash = undef;
631     }
633     return ($msg, $msg_hash);
637 sub check_outgoing_xml_validity {
638     my ($msg, $session_id) = @_;
640     my $msg_hash;
641     eval{
642         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
644         ##############
645         # check header
646         my $header_l = $msg_hash->{'header'};
647         if( 1 != @{$header_l} ) {
648             die 'no or more than one headers specified';
649         }
650         my $header = @{$header_l}[0];
651         if( 0 == length $header) {
652             die 'header has length 0';
653         }
655         ##############
656         # check source
657         my $source_l = $msg_hash->{'source'};
658         if( 1 != @{$source_l} ) {
659             die 'no or more than 1 sources specified';
660         }
661         my $source = @{$source_l}[0];
662         if( 0 == length $source) {
663             die 'source has length 0';
664         }
666                 # Check if source contains hostname instead of ip address
667                 if($source =~ /^[a-z][\w\-\.]+:\d+$/i) {
668                         my ($hostname,$port) = split(/:/, $source);
669                         my $ip_address = inet_ntoa(scalar gethostbyname($hostname));
670                         if(defined($ip_address) && $ip_address =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/ && $port =~ /^\d+$/) {
671                                 # Write ip address to $source variable
672                                 $source = "$ip_address:$port";
673                                 $msg_hash->{source}[0] = $source ;
674                                 $msg =~ s/<source>.*<\/source>/<source>$source<\/source>/; 
675                         }
676                 }
677         unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
678                 $source =~ /^GOSA$/i) {
679             die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
680         }
681         
682         ##############
683         # check target  
684         my $target_l = $msg_hash->{'target'};
685         if( 0 == @{$target_l} ) {
686             die "no targets specified";
687         }
688         foreach my $target (@$target_l) {
689             if( 0 == length $target) {
690                 die "target has length 0";
691             }
692             unless( $target =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
693                     $target =~ /^GOSA$/i ||
694                     $target =~ /^\*$/ ||
695                     $target =~ /KNOWN_SERVER/i ||
696                     $target =~ /JOBDB/i ||
697                     $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 ){
698                 die "target '$target' is not a complete ip-address with port or a valid target name or a mac-address";
699             }
700         }
701     };
702     if($@) {
703         daemon_log("$session_id ERROR: outgoing msg is not gosa-si envelope conform: $@", 1);
704         daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 1);
705         $msg_hash = undef;
706     }
708     return ($msg, $msg_hash);
712 sub input_from_known_server {
713     my ($input, $remote_ip, $session_id) = @_ ;  
714     my ($msg, $msg_hash, $module);
716     my $sql_statement= "SELECT * FROM known_server";
717     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
719     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
720         my $host_name = $hit->{hostname};
721         if( not $host_name =~ "^$remote_ip") {
722             next;
723         }
724         my $host_key = $hit->{hostkey};
725         daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 14);
726         daemon_log("$session_id DEBUG: input_from_known_server: host_key: $host_key", 14);
728         # check if module can open msg envelope with module key
729         my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
730         if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
731             daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 14);
732             daemon_log("$@", 14);
733             next;
734         }
735         else {
736             $msg = $tmp_msg;
737             $msg_hash = $tmp_msg_hash;
738             $module = "ServerPackages";
739             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 14);
740             last;
741         }
742     }
744     if( (!$msg) || (!$msg_hash) || (!$module) ) {
745         daemon_log("$session_id DEBUG: Incoming message is not from a known server", 14);
746     }
747   
748     return ($msg, $msg_hash, $module);
752 sub input_from_known_client {
753     my ($input, $remote_ip, $session_id) = @_ ;  
754     my ($msg, $msg_hash, $module);
756     my $sql_statement= "SELECT * FROM known_clients";
757     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
758     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
759         my $host_name = $hit->{hostname};
760         if( not $host_name =~ /^$remote_ip/) {
761                 next;
762                 }
763         my $host_key = $hit->{hostkey};
764         &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 14);
765         &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 14);
767         # check if module can open msg envelope with module key
768         ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
770         if( (!$msg) || (!$msg_hash) ) {
771             &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 14);
772             next;
773         }
774         else {
775             $module = "ClientPackages";
776             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 14);
777             last;
778         }
779     }
781     if( (!$msg) || (!$msg_hash) || (!$module) ) {
782         &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 14);
783     }
785     return ($msg, $msg_hash, $module);
789 sub input_from_unknown_host {
790         no strict "refs";
791         my ($input, $session_id) = @_ ;
792         my ($msg, $msg_hash, $module);
793         my $error_string;
795         my %act_modules = %$known_modules;
797         while( my ($mod, $info) = each(%act_modules)) {
799                 # check a key exists for this module
800                 my $module_key = ${$mod."_key"};
801                 if( not defined $module_key ) {
802                         if( $mod eq 'ArpHandler' ) {
803                                 next;
804                         }
805                         daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
806                         next;
807                 }
808                 daemon_log("$session_id DEBUG: $mod: $module_key", 14);
810                 # check if module can open msg envelope with module key
811                 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
812                 if( (not defined $msg) || (not defined $msg_hash) ) {
813                         next;
814                 } else {
815                         $module = $mod;
816             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 18);
817                         last;
818                 }
819         }
821         if( (!$msg) || (!$msg_hash) || (!$module)) {
822                 daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 14);
823         }
825         return ($msg, $msg_hash, $module);
829 sub create_ciphering {
830     my ($passwd) = @_;
831         if((!defined($passwd)) || length($passwd)==0) {
832                 $passwd = "";
833         }
834     $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
835     my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
836     my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
837     $my_cipher->set_iv($iv);
838     return $my_cipher;
842 sub encrypt_msg {
843     my ($msg, $key) = @_;
844     my $my_cipher = &create_ciphering($key);
845     my $len;
846     {
847             use bytes;
848             $len= 16-length($msg)%16;
849     }
850     $msg = "\0"x($len).$msg;
851     $msg = $my_cipher->encrypt($msg);
852     chomp($msg = &encode_base64($msg));
853     # there are no newlines allowed inside msg
854     $msg=~ s/\n//g;
855     return $msg;
859 sub decrypt_msg {
861     my ($msg, $key) = @_ ;
862     $msg = &decode_base64($msg);
863     my $my_cipher = &create_ciphering($key);
864     $msg = $my_cipher->decrypt($msg); 
865     $msg =~ s/\0*//g;
866     return $msg;
870 sub get_encrypt_key {
871     my ($target) = @_ ;
872     my $encrypt_key;
873     my $error = 0;
875     # target can be in known_server
876     if( not defined $encrypt_key ) {
877         my $sql_statement= "SELECT * FROM known_server WHERE hostname='$target'";
878         my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
879         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
880             my $host_name = $hit->{hostname};
881             if( $host_name ne $target ) {
882                 next;
883             }
884             $encrypt_key = $hit->{hostkey};
885             last;
886         }
887     }
889     # target can be in known_client
890     if( not defined $encrypt_key ) {
891         my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$target'";
892         my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
893         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
894             my $host_name = $hit->{hostname};
895             if( $host_name ne $target ) {
896                 next;
897             }
898             $encrypt_key = $hit->{hostkey};
899             last;
900         }
901     }
903     return $encrypt_key;
907 #===  FUNCTION  ================================================================
908 #         NAME:  open_socket
909 #   PARAMETERS:  PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
910 #                [PeerPort] string necessary if port not appended by PeerAddr
911 #      RETURNS:  socket IO::Socket::INET
912 #  DESCRIPTION:  open a socket to PeerAddr
913 #===============================================================================
914 sub open_socket {
915     my ($PeerAddr, $PeerPort) = @_ ;
916     if(defined($PeerPort)){
917         $PeerAddr = $PeerAddr.":".$PeerPort;
918     }
919     my $socket;
920     $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
921             Porto => "tcp",
922             Type => SOCK_STREAM,
923             Timeout => 5,
924             );
925     if(not defined $socket) {
926         return;
927     }
928 #    &daemon_log("DEBUG: open_socket: $PeerAddr", 7);
929     return $socket;
933 sub send_msg_to_target {
934     my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
935     my $error = 0;
936     my $header;
937     my $timestamp = &get_time();
938     my $new_status;
939     my $act_status;
940     my ($sql_statement, $res);
941   
942     if( $msg_header ) {
943         $header = "'$msg_header'-";
944     } else {
945         $header = "";
946     }
948         # Memorize own source address
949         my $own_source_address = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
950         $own_source_address .= ":".$server_port;
952         # Patch 0.0.0.0 source to real address
953         $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$own_source_address<\/source>/s;
954         # Patch GOSA source to real address and add forward_to_gosa tag
955         $msg =~ s/<source>GOSA<\/source>/<source>$own_source_address<\/source> <forward_to_gosa>$own_source_address,$session_id<\/forward_to_gosa>/ ;
957     # encrypt xml msg
958     my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
960     # opensocket
961     my $socket = &open_socket($address);
962     if( !$socket ) {
963         daemon_log("$session_id ERROR: Cannot open socket to host '$address'. Message processing aborted!", 1);
964         $error++;
965     }
966     
967     if( $error == 0 ) {
968         # send xml msg
969         print $socket $crypted_msg.";$own_source_address\n";
970         daemon_log("$session_id INFO: send ".$header."msg to $address", 5);
971         daemon_log("$session_id DEBUG: message:\n$msg", 12);
972         
973     }
975     # close socket in any case
976     if( $socket ) {
977         close $socket;
978     }
980     if( $error > 0 ) { $new_status = "down"; }
981     else { $new_status = $msg_header; }
984     # known_clients
985     $sql_statement = "SELECT * FROM $known_clients_tn WHERE hostname='$address'";
986     $res = $known_clients_db->select_dbentry($sql_statement);
987     if( keys(%$res) == 1) {
988         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
989         if ($act_status eq "down" && $new_status eq "down") {
990             $sql_statement = "DELETE FROM known_clients WHERE hostname='$address'";
991             $res = $known_clients_db->del_dbentry($sql_statement);
992             daemon_log("$session_id WARNING: failed 2x to send msg to host '$address', delete host from known_clients", 3);
993         } else { 
994             $sql_statement = "UPDATE known_clients SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
995             $res = $known_clients_db->update_dbentry($sql_statement);
996             if($new_status eq "down"){
997                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
998             } else {
999                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
1000             }
1001         }
1002     }
1004     # known_server
1005     $sql_statement = "SELECT * FROM $known_server_tn WHERE hostname='$address'";
1006     $res = $known_server_db->select_dbentry($sql_statement);
1007     if( keys(%$res) == 1) {
1008         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
1009         if ($act_status eq "down" && $new_status eq "down") {
1010             $sql_statement = "DELETE FROM known_server WHERE hostname='$address'";
1011             $res = $known_server_db->del_dbentry($sql_statement);
1012             daemon_log("$session_id WARNING: failed 2x to send a message to host '$address', delete host from known_server", 3);
1013         } 
1014         else { 
1015             $sql_statement = "UPDATE known_server SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
1016             $res = $known_server_db->update_dbentry($sql_statement);
1017             if($new_status eq "down"){
1018                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
1019             } else {
1020                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
1021             }
1022         }
1023     }
1024     return $error; 
1028 sub update_jobdb_status_for_send_msgs {
1029     my ($session_id, $answer, $error) = @_;
1030     if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
1031                 &daemon_log("$session_id DEBUG: try to update job status", 138); 
1032         my $jobdb_id = $1;
1033     
1034         $answer =~ /<header>(.*)<\/header>/;
1035         my $job_header = $1;
1037         $answer =~ /<target>(.*)<\/target>/;
1038         my $job_target = $1;
1039             
1040         # Sending msg failed
1041         if( $error ) {
1043             # Set jobs to done, jobs do not need to deliver their message in any case
1044             if (($job_header eq "trigger_action_localboot")
1045                     ||($job_header eq "trigger_action_lock")
1046                     ||($job_header eq "trigger_action_halt") 
1047                     ) {
1048                 my $sql_statement = "UPDATE $job_queue_tn SET status='done' WHERE id=$jobdb_id";
1049                 my $res = $job_db->update_dbentry($sql_statement);
1050                 
1051             # Reactivate jobs, jobs need to deliver their message
1052             } elsif (($job_header eq "trigger_action_activate")
1053                     ||($job_header eq "trigger_action_update")
1054                     ||($job_header eq "trigger_action_reinstall") 
1055                     ||($job_header eq "trigger_activate_new")
1056                     ) {
1057                 &reactivate_job_with_delay($session_id, $job_target, $job_header, 30 );
1059             # For all other messages
1060             } else {
1061                 my $sql_statement = "UPDATE $job_queue_tn ".
1062                     "SET status='error', result='can not deliver msg, please consult log file' ".
1063                     "WHERE id=$jobdb_id";
1064                 my $res = $job_db->update_dbentry($sql_statement);
1065             }
1067         # Sending msg was successful
1068         } else {
1069             # Set jobs localboot, lock, activate, halt, reboot and wake to done
1070             # jobs reinstall, update, inst_update do themself setting to done
1071             if (($job_header eq "trigger_action_localboot")
1072                     ||($job_header eq "trigger_action_lock")
1073                     ||($job_header eq "trigger_action_activate")
1074                     ||($job_header eq "trigger_action_halt") 
1075                     ||($job_header eq "trigger_action_reboot")
1076                     ||($job_header eq "trigger_action_wake")
1077                     ||($job_header eq "trigger_wake")
1078                     ) {
1080                 my $sql_statement = "UPDATE $job_queue_tn ".
1081                     "SET status='done' ".
1082                     "WHERE id=$jobdb_id AND status='processed'";
1083                 my $res = $job_db->update_dbentry($sql_statement);
1084             } else { 
1085                 &daemon_log("$session_id DEBUG: sending message succeed but cannot update job status.", 138); 
1086             } 
1087         } 
1088     } else { 
1089         &daemon_log("$session_id DEBUG: cannot update job status, msg has no jobdb_id-tag.", 138); 
1090     }
1093 sub reactivate_job_with_delay {
1094     my ($session_id, $target, $header, $delay) = @_ ;
1095     # 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
1096     
1097     if (not defined $delay) { $delay = 30 } ;
1098     my $delay_timestamp = &calc_timestamp(&get_time(), "plus", $delay);
1100     my $sql = "UPDATE $job_queue_tn Set timestamp='$delay_timestamp', status='waiting' WHERE (macaddress LIKE 'target' AND headertag='$header')"; 
1101     my $res = $job_db->update_dbentry($sql);
1102     daemon_log("$session_id INFO: '$header'-job will be reactivated at '$delay_timestamp' ".
1103             "cause client '$target' is currently not available", 5);
1104     return;
1108 sub sig_handler {
1109         my ($kernel, $signal) = @_[KERNEL, ARG0] ;
1110         daemon_log("0 INFO got signal '$signal'", 1); 
1111         $kernel->sig_handled();
1112         return;
1116 sub msg_to_decrypt {
1117         my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
1118         my $session_id = $session->ID;
1119         my ($msg, $msg_hash, $module);
1120         my $error = 0;
1122         # fetch new msg out of @msgs_to_decrypt
1123         my $tmp_next_msg = shift @msgs_to_decrypt;
1124     my ($next_msg, $msg_source) = split(/;/, $tmp_next_msg);
1126         # msg is from a new client or gosa
1127         ($msg, $msg_hash, $module) = &input_from_unknown_host($next_msg, $session_id);
1129         # msg is from a gosa-si-server
1130         if(((!$msg) || (!$msg_hash) || (!$module)) && ($serverPackages_enabled eq "true")){
1131                 if (not defined $msg_source) 
1132                 {
1133                         # Only needed, to be compatible with older gosa-si-server versions
1134                         ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $heap->{'remote_ip'}, $session_id);
1135                 }
1136                 else
1137                 {
1138                         ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $msg_source, $session_id);
1139                 }
1140         }
1141         # msg is from a gosa-si-client
1142         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1143                 ($msg, $msg_hash, $module) = &input_from_known_client($next_msg, $msg_source, $session_id);
1144         }
1145         # an error occurred
1146         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1147                 # If an incoming msg could not be decrypted (maybe a wrong key), decide if msg comes from a client 
1148                 # or a server.  In case of a client, send a ping. If the client could not understand a msg from its 
1149                 # server the client cause a re-registering process. In case of a server, decrease update_time in kown_server_db
1150                 # and trigger a re-registering process for servers
1151                 if (defined $msg_source && $msg_source =~ /:$server_port$/ && $serverPackages_enabled eq "true")
1152                 {
1153                         daemon_log("$session_id WARNING: Cannot understand incoming msg from server '$msg_source'. Cause re-registration process for servers.", 3);
1154                         my $update_statement = "UPDATE $known_server_tn SET update_time='19700101000000' WHERE hostname='$msg_source'"; 
1155                         daemon_log("$session_id DEBUG: $update_statement", 7);
1156                         my $upadte_res = $known_server_db->exec_statement($update_statement);
1157                         $kernel->yield("register_at_foreign_servers");
1158                 }
1159                 elsif ((defined $msg_source) && (not $msg_source =~ /:$server_port$/))
1160                 {
1161                         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);
1162                         #my $remote_ip = $heap->{'remote_ip'};
1163                         #my $remote_port = $heap->{'remote_port'};
1164                         my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source><target>$msg_source</target></xml>";
1165                         my ($test_error, $test_error_string) = &send_msg_to_target($ping_msg, "$msg_source", "dummy-key", "gosa_ping", $session_id);
1166                         daemon_log("$session_id WARNING: Sending msg to cause re-registering: $ping_msg", 3);
1167                 }
1168                 else
1169                 {
1170                         my $foreign_host = defined $msg_source ? $msg_source : $heap->{'remote_ip'};
1171                         daemon_log("$session_id ERROR: Incoming message from host '$foreign_host' cannot be understood. Processing aborted!", 1);
1172                         daemon_log("$session_id DEBUG: Aborted message: $tmp_next_msg", 11);
1173                 }
1175                 $error++
1176         }
1179         my $header;
1180         my $target;
1181         my $source;
1182         my $done = 0;
1183         my $sql;
1184         my $res;
1186         # check whether this message should be processed here
1187         if ($error == 0) {
1188                 $header = @{$msg_hash->{'header'}}[0];
1189                 $target = @{$msg_hash->{'target'}}[0];
1190                 $source = @{$msg_hash->{'source'}}[0];
1191                 my $not_found_in_known_clients_db = 0;
1192                 my $not_found_in_known_server_db = 0;
1193                 my $not_found_in_foreign_clients_db = 0;
1194                 my $local_address;
1195                 my $local_mac;
1196                 my ($target_ip, $target_port) = split(':', $target);
1198                 # Determine the local ip address if target is an ip address
1199                 if ($target =~ /^\d+\.\d+\.\d+\.\d+:\d+$/) {
1200                         $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1201                 } else {
1202                         $local_address = $server_address;
1203                 }
1205                 # Determine the local mac address if target is a mac address
1206                 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) {
1207                         my $loc_ip = &get_local_ip_for_remote_ip($heap->{'remote_ip'});
1208                         my $network_interface= &get_interface_for_ip($loc_ip);
1209                         $local_mac = &get_mac_for_interface($network_interface);
1210                 } else {
1211                         $local_mac = $server_mac_address;
1212                 }
1214                 # target and source is equal to GOSA -> process here
1215                 if (not $done) {
1216                         if ($target eq "GOSA" && $source eq "GOSA") {
1217                                 $done = 1;                    
1218                                 &daemon_log("$session_id DEBUG: target and source is 'GOSA' -> process '$header' here", 11);
1219                         }
1220                 }
1222                 # target is own address without forward_to_gosa-tag -> process here
1223                 if (not $done) {
1224                         #if ((($target eq $local_address) || ($target eq $local_mac) ) && (not exists $msg_hash->{'forward_to_gosa'})) {
1225                         if (($target eq $local_address) && (not exists $msg_hash->{'forward_to_gosa'})) {
1226                                 $done = 1;
1227                                 if ($source eq "GOSA") {
1228                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1229                                 }
1230                                 &daemon_log("$session_id DEBUG: target is own address without forward_to_gosa-tag -> process '$header' here", 11);
1231                         }
1232                 }
1234                 # target is own address with forward_to_gosa-tag not pointing to myself -> process here
1235                 if (not $done) {
1236                         my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1237                         my $gosa_at;
1238                         my $gosa_session_id;
1239                         if (($target eq $local_address) && (defined $forward_to_gosa)){
1240                                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1241                                 if ($gosa_at ne $local_address) {
1242                                         $done = 1;
1243                                         &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag not pointing to myself -> process '$header' here", 11); 
1244                                 }
1245                         }
1246                 }
1248                 # Target is a client address and there is a processing function within a plugin -> process loaclly
1249                 if (not $done)
1250                 {
1251                         # Check if target is a client address
1252                         $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')"; 
1253                         $res = $known_clients_db->select_dbentry($sql);
1254                         if ((keys(%$res) > 0) ) 
1255                         {
1256                                 my $hostname = $res->{1}->{'hostname'};
1257                                 my $reduced_header = $header;
1258                                 $reduced_header =~ s/gosa_//;
1259                                 # Check if there is a processing function within a plugin
1260                                 if (exists $known_functions->{$reduced_header}) 
1261                                 {
1262                                         $msg =~ s/<target>\S*<\/target>/<target>$hostname<\/target>/;
1263                                         $done = 1;
1264                                         &daemon_log("$session_id DEBUG: Target is client address with processing function within a plugin -> process '$header' here", 11);
1265                                 }
1266                         }
1267                 }
1269                 # If header has a 'job_' prefix, do always process message locally
1270                 # which means put it into job queue
1271                 if ((not $done) && ($header =~ /job_/))
1272                 {
1273                         $done = 1;
1274                         &daemon_log("$session_id DEBUG: Header has a 'job_' prefix. Put it into job queue. -> process '$header' here", 11);
1275                 }
1277                 # if message should be processed here -> add message to incoming_db
1278                 if ($done) {
1279                         # if a 'job_' or a 'gosa_' message comes from a foreign server, fake module from
1280                         # ServerPackages to GosaPackages so gosa-si-server knows how to process this kind of messages
1281                         if ($header =~ /^gosa_/ || $header =~ /^job_/) {
1282                                 $module = "GosaPackages";
1283                         }
1285                         my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1286                                         primkey=>[],
1287                                         headertag=>$header,
1288                                         targettag=>$target,
1289                                         xmlmessage=>&encode_base64($msg),
1290                                         timestamp=>&get_time,
1291                                         module=>$module,
1292                                         sessionid=>$session_id,
1293                                 } );
1295                 }
1297                 # target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa
1298                 if (not $done) {
1299                         my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1300                         my $gosa_at;
1301                         my $gosa_session_id;
1302                         if (($target eq $local_address) && (defined $forward_to_gosa)){
1303                                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1304                                 if ($gosa_at eq $local_address) {
1305                                         my $session_reference = $kernel->ID_id_to_session($gosa_session_id);
1306                                         if( defined $session_reference ) {
1307                                                 $heap = $session_reference->get_heap();
1308                                         }
1309                                         if(exists $heap->{'client'}) {
1310                                                 $msg = &encrypt_msg($msg, $GosaPackages_key);
1311                                                 $heap->{'client'}->put($msg);
1312                                                 &daemon_log("$session_id DEBUG: incoming '$header' message forwarded to GOsa", 11); 
1313                                         }
1314                                         $done = 1;
1315                                         &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag pointing at myself -> forward '$header' to gosa", 11);
1316                                 }
1317                         }
1319                 }
1321                 # target is a client address in known_clients -> forward to client
1322                 if (not $done) {
1323                         $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')"; 
1324                         $res = $known_clients_db->select_dbentry($sql);
1325                         if (keys(%$res) > 0) 
1326                         {
1327                                 $done = 1; 
1328                                 &daemon_log("$session_id DEBUG: target is a client address in known_clients -> forward '$header' to client", 11);
1329                                 my $hostkey = $res->{1}->{'hostkey'};
1330                                 my $hostname = $res->{1}->{'hostname'};
1331                                 $msg =~ s/<target>\S*<\/target>/<target>$hostname<\/target>/;
1332                                 $msg =~ s/<header>gosa_/<header>/;
1333                                 my $error= &send_msg_to_target($msg, $hostname, $hostkey, $header, $session_id);
1334                                 if ($error) {
1335                                         &daemon_log("$session_id ERROR: Some problems occurred while trying to send msg to client '$hostkey': $msg", 1);
1336                                 }
1337                         } 
1338                         else 
1339                         {
1340                                 $not_found_in_known_clients_db = 1;
1341                         }
1342                 }
1344                 # target is a client address in foreign_clients -> forward to registration server
1345                 if (not $done) {
1346                         $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1347                         $res = $foreign_clients_db->select_dbentry($sql);
1348                         if (keys(%$res) > 0) {
1349                                 my $hostname = $res->{1}->{'hostname'};
1350                                 my ($host_ip, $host_port) = split(/:/, $hostname);
1351                                 my $local_address =  &get_local_ip_for_remote_ip($host_ip).":$server_port";
1352                                 my $regserver = $res->{1}->{'regserver'};
1353                                 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$regserver'"; 
1354                                 my $res = $known_server_db->select_dbentry($sql);
1355                                 if (keys(%$res) > 0) {
1356                                         my $regserver_key = $res->{1}->{'hostkey'};
1357                                         $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1358                                         $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1359                                         if ($source eq "GOSA") {
1360                                                 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1361                                         }
1362                                         my $error= &send_msg_to_target($msg, $regserver, $regserver_key, $header, $session_id);
1363                                         if ($error) {
1364                                                 &daemon_log("$session_id ERROR: some problems occurred while trying to send msg to registration server: $msg", 1); 
1365                                         }
1366                                 }
1367                                 $done = 1;
1368                                 &daemon_log("$session_id DEBUG: target is a client address in foreign_clients -> forward '$header' to registration server", 11);
1369                         } else {
1370                                 $not_found_in_foreign_clients_db = 1;
1371                         }
1372                 }
1374                 # target is a server address -> forward to server
1375                 if (not $done) {
1376                         $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1377                         $res = $known_server_db->select_dbentry($sql);
1378                         if (keys(%$res) > 0) {
1379                                 my $hostkey = $res->{1}->{'hostkey'};
1381                                 if ($source eq "GOSA") {
1382                                         $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1383                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1385                                 }
1387                                 &send_msg_to_target($msg, $target, $hostkey, $header, $session_id);
1388                                 $done = 1;
1389                                 &daemon_log("$session_id DEBUG: target is a server address -> forward '$header' to server", 11);
1390                         } else {
1391                                 $not_found_in_known_server_db = 1;
1392                         }
1393                 }
1396                 # target is not in foreign_clients_db, known_server_db or known_clients_db, maybe it is a complete new one -> process here
1397                 if ( $not_found_in_foreign_clients_db 
1398                         && $not_found_in_known_server_db
1399                         && $not_found_in_known_clients_db) {
1400                         &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 '$header' here", 11);
1401             if ($header =~ /^gosa_/ || $header =~ /^job_/) { 
1402                 $module = "GosaPackages"; 
1403             }
1404                         my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1405                                         primkey=>[],
1406                                         headertag=>$header,
1407                                         targettag=>$target,
1408                                         xmlmessage=>&encode_base64($msg),
1409                                         timestamp=>&get_time,
1410                                         module=>$module,
1411                                         sessionid=>$session_id,
1412                                 } );
1413                         $done = 1;
1414                 }
1417                 if (not $done) {
1418                         daemon_log("$session_id ERROR: do not know what to do with this message: $msg", 1);
1419                         if ($source eq "GOSA") {
1420                                 my %data = ('error_msg' => &encode_base64($msg), 'error_string' => "Do not know what to do with this message!");
1421                                 my $error_msg = &build_msg("error", $local_address, "GOSA", \%data ); 
1423                                 my $session_reference = $kernel->ID_id_to_session($session_id);
1424                                 if( defined $session_reference ) {
1425                                         $heap = $session_reference->get_heap();
1426                                 }
1427                                 if(exists $heap->{'client'}) {
1428                                         $error_msg = &encrypt_msg($error_msg, $GosaPackages_key);
1429                                         $heap->{'client'}->put($error_msg);
1430                                 }
1431                         }
1432                 }
1434         }
1436         return;
1440 sub next_task {
1441     my ($session, $heap, $task) = @_[SESSION, HEAP, ARG0, ARG1];
1442     my $running_task = POE::Wheel::Run->new(
1443             Program => sub { process_task($session, $heap, $task) },
1444             StdioFilter => POE::Filter::Reference->new(),
1445             StdoutEvent  => "task_result",
1446             StderrEvent  => "task_debug",
1447             CloseEvent   => "task_done",
1448             );
1449     $heap->{task}->{ $running_task->ID } = $running_task;
1452 sub handle_task_result {
1453     my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
1454     my $client_answer = $result->{'answer'};
1455     if( $client_answer =~ s/session_id=(\d+)$// ) {
1456         my $session_id = $1;
1457         if( defined $session_id ) {
1458             my $session_reference = $kernel->ID_id_to_session($session_id);
1459             if( defined $session_reference ) {
1460                 $heap = $session_reference->get_heap();
1461             }
1462         }
1464         if(exists $heap->{'client'}) {
1465             $heap->{'client'}->put($client_answer);
1466         }
1467     }
1468     $kernel->sig(CHLD => "child_reap");
1471 sub handle_task_debug {
1472     my $result = $_[ARG0];
1473     print STDERR "$result\n";
1476 sub handle_task_done {
1477     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1478     delete $heap->{task}->{$task_id};
1479         if (exists $heap->{ldap_handle}->{$task_id}) {
1480                 &release_ldap_handle($heap->{ldap_handle}->{$task_id});
1481         }
1484 sub process_task {
1485     no strict "refs";
1486     #CHECK: Not @_[...]?
1487     my ($session, $heap, $task) = @_;
1488     my $error = 0;
1489     my $answer_l;
1490     my ($answer_header, @answer_target_l, $answer_source);
1491     my $client_answer = "";
1493     # prepare all variables needed to process message
1494     #my $msg = $task->{'xmlmessage'};
1495     my $msg = &decode_base64($task->{'xmlmessage'});
1496     my $incoming_id = $task->{'id'};
1497     my $module = $task->{'module'};
1498     my $header =  $task->{'headertag'};
1499     my $session_id = $task->{'sessionid'};
1500                 my $msg_hash;
1501                 eval {
1502         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
1503                 }; 
1504                 daemon_log("ERROR: XML failure '$@'") if ($@);
1505     my $source = @{$msg_hash->{'source'}}[0];
1506     
1507     # set timestamp of incoming client uptodate, so client will not 
1508     # be deleted from known_clients because of expiration
1509     my $cur_time = &get_time();
1510     my $sql = "UPDATE $known_clients_tn SET timestamp='$cur_time' WHERE hostname='$source'"; 
1511     my $res = $known_clients_db->exec_statement($sql);
1513     ######################
1514     # process incoming msg
1515     if( $error == 0) {
1516         daemon_log("$session_id INFO: Incoming msg (session_id=$session_id) with header '".@{$msg_hash->{'header'}}[0]."'", 5); 
1517         daemon_log("$session_id DEBUG: Processing module ".$module, 26);
1518         $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1520         if ( 0 < @{$answer_l} ) {
1521             my $answer_str = join("\n", @{$answer_l});
1522                         my @headers; 
1523             while ($answer_str =~ /<header>(\w+)<\/header>/g) {
1524                                 push(@headers, $1);
1525             }
1526                         daemon_log("$session_id INFO: got answer message(s) with header: '".join("', '", @headers)."'", 5);
1527             daemon_log("$session_id DEBUG: $module: got answer from module: \n".$answer_str,26);
1528         } else {
1529             daemon_log("$session_id DEBUG: $module: got no answer from module!" ,26);
1530         }
1532     }
1533     if( !$answer_l ) { $error++ };
1535     ########
1536     # answer
1537     if( $error == 0 ) {
1539         foreach my $answer ( @{$answer_l} ) {
1540             # check outgoing msg to xml validity
1541             my ($answer, $answer_hash) = &check_outgoing_xml_validity($answer, $session_id);
1542             if( not defined $answer_hash ) { next; }
1543             
1544             $answer_header = @{$answer_hash->{'header'}}[0];
1545             @answer_target_l = @{$answer_hash->{'target'}};
1546             $answer_source = @{$answer_hash->{'source'}}[0];
1548             # deliver msg to all targets 
1549             foreach my $answer_target ( @answer_target_l ) {
1551                 # targets of msg are all gosa-si-clients in known_clients_db
1552                 if( $answer_target eq "*" ) {
1553                     # answer is for all clients
1554                     my $sql_statement= "SELECT * FROM known_clients";
1555                     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
1556                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1557                         my $host_name = $hit->{hostname};
1558                         my $host_key = $hit->{hostkey};
1559                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1560                         &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1561                     }
1562                 }
1564                 # targets of msg are all gosa-si-server in known_server_db
1565                 elsif( $answer_target eq "KNOWN_SERVER" ) {
1566                     # answer is for all server in known_server
1567                     my $sql_statement= "SELECT * FROM $known_server_tn";
1568                     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
1569                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1570                         my $host_name = $hit->{hostname};
1571                         my $host_key = $hit->{hostkey};
1572                         $answer =~ s/<target>\S+<\/target>/<target>$host_name<\/target>/g;
1573                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1574                         &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1575                     }
1576                 }
1578                 # target of msg is GOsa
1579                                 elsif( $answer_target eq "GOSA" ) {
1580                                         my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1581                                         my $add_on = "";
1582                     if( defined $session_id ) {
1583                         $add_on = ".session_id=$session_id";
1584                     }
1585                     # answer is for GOSA and has to returned to connected client
1586                     my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1587                     $client_answer = $gosa_answer.$add_on;
1588                 }
1590                 # target of msg is job queue at this host
1591                 elsif( $answer_target eq "JOBDB") {
1592                     $answer =~ /<header>(\S+)<\/header>/;   
1593                     my $header;
1594                     if( defined $1 ) { $header = $1; }
1595                     my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1596                     &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1597                 }
1599                 # Target of msg is a mac address
1600                 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 ) {
1601                     daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients and foreign_clients", 5);
1603                     # Looking for macaddress in known_clients
1604                     my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1605                     my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1606                     my $found_ip_flag = 0;
1607                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1608                         my $host_name = $hit->{hostname};
1609                         my $host_key = $hit->{hostkey};
1610                         $answer =~ s/$answer_target/$host_name/g;
1611                         daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1612                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1613                         &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1614                         $found_ip_flag++ ;
1615                     }   
1617                     # Looking for macaddress in foreign_clients
1618                     if ($found_ip_flag == 0) {
1619                         my $sql = "SELECT * FROM $foreign_clients_tn WHERE macaddress LIKE '$answer_target'";
1620                         my $res = $foreign_clients_db->select_dbentry($sql);
1621                         while( my ($hit_num, $hit) = each %{ $res } ) {
1622                             my $host_name = $hit->{hostname};
1623                             my $reg_server = $hit->{regserver};
1624                             daemon_log("$session_id INFO: found host '$host_name' with mac '$answer_target', registered at '$reg_server'", 5);
1625                             
1626                             # Fetch key for reg_server
1627                             my $reg_server_key;
1628                             my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$reg_server'";
1629                             my $res = $known_server_db->select_dbentry($sql);
1630                             if (exists $res->{1}) {
1631                                 $reg_server_key = $res->{1}->{'hostkey'}; 
1632                             } else {
1633                                 daemon_log("$session_id ERROR: cannot find hostkey for '$host_name' in '$known_server_tn'", 1); 
1634                                 daemon_log("$session_id ERROR: unable to forward answer to correct registration server, processing is aborted!", 1); 
1635                                 $reg_server_key = undef;
1636                             }
1638                             # Send answer to server where client is registered
1639                             if (defined $reg_server_key) {
1640                                 $answer =~ s/$answer_target/$host_name/g;
1641                                 my $error = &send_msg_to_target($answer, $reg_server, $reg_server_key, $answer_header, $session_id);
1642                                 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1643                                 $found_ip_flag++ ;
1644                             }
1645                         }
1646                     }
1648                     # No mac to ip matching found
1649                     if( $found_ip_flag == 0) {
1650                         daemon_log("$session_id WARNING: no host found in known_clients or foreign_clients with mac address '$answer_target'", 3);
1651                         &reactivate_job_with_delay($session_id, $answer_target, $answer_header, 30);
1652                     }
1654                 # Answer is for one specific host   
1655                 } else {
1656                     # get encrypt_key
1657                     my $encrypt_key = &get_encrypt_key($answer_target);
1658                     if( not defined $encrypt_key ) {
1659                         # unknown target
1660                         daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1661                         next;
1662                     }
1663                     my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1664                     &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1665                 }
1666             }
1667         }
1668     }
1670     my $filter = POE::Filter::Reference->new();
1671     my %result = ( 
1672             status => "seems ok to me",
1673             answer => $client_answer,
1674             );
1676     my $output = $filter->put( [ \%result ] );
1677     print @$output;
1682 sub session_start {
1683     my ($kernel) = $_[KERNEL];
1684     $global_kernel = $kernel;
1685     $kernel->yield('register_at_foreign_servers');
1686         $kernel->yield('create_fai_server_db', $fai_server_tn );
1687         $kernel->yield('create_fai_release_db', $fai_release_tn );
1688     $kernel->yield('watch_for_next_tasks');
1689         $kernel->sig(USR1 => "sig_handler");
1690         $kernel->sig(USR2 => "recreate_packages_db");
1691         $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1692         $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay); 
1693     $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay); 
1694         $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1695     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1696         $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1697     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1699     # Start opsi check
1700     if ($opsi_enabled eq "true") {
1701         $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay); 
1702     }
1707 sub watch_for_done_jobs {
1708         #CHECK: $heap for what?
1709         my ($kernel,$heap) = @_[KERNEL, HEAP];
1711         my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((status='done') AND (modified='0'))";
1712         my $res = $job_db->select_dbentry( $sql_statement );
1714         while( my ($id, $hit) = each %{$res} ) {
1715                 my $jobdb_id = $hit->{id};
1716                 my $sql_statement = "DELETE FROM $job_queue_tn WHERE id=$jobdb_id"; 
1717                 my $res = $job_db->del_dbentry($sql_statement); 
1718         }
1720         $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1724 sub watch_for_opsi_jobs {
1725     my ($kernel) = $_[KERNEL];
1727     # 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 
1728     # opsi install job is to parse the xml message. There is still the correct header.
1729     my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((xmlmessage LIKE '%opsi_install_client</header>%') AND (status='processing') AND (siserver='localhost'))";
1730         my $res = $job_db->select_dbentry( $sql_statement );
1732     # Ask OPSI for an update of the running jobs
1733     while (my ($id, $hit) = each %$res ) {
1734         # Determine current parameters of the job
1735         my $hostId = $hit->{'plainname'};
1736         my $macaddress = $hit->{'macaddress'};
1737         my $progress = $hit->{'progress'};
1739         my $result= {};
1740         
1741         # For hosts, only return the products that are or get installed
1742         my $callobj;
1743         $callobj = {
1744             method  => 'getProductStates_hash',
1745             params  => [ $hostId ],
1746             id  => 1,
1747         };
1748         
1749         my $hres = $opsi_client->call($opsi_url, $callobj);
1750         #my ($hres_err, $hres_err_string) = &check_opsi_res($hres);
1751         if (not &check_opsi_res($hres)) {
1752             my $htmp= $hres->result->{$hostId};
1753         
1754             # Check state != not_installed or action == setup -> load and add
1755             my $products= 0;
1756             my $installed= 0;
1757             my $installing = 0;
1758             my $error= 0;  
1759             my @installed_list;
1760             my @error_list;
1761             my $act_status = "none";
1762             foreach my $product (@{$htmp}){
1764                 if ($product->{'installationStatus'} ne "not_installed" or
1765                         $product->{'actionRequest'} eq "setup"){
1767                     # Increase number of products for this host
1768                     $products++;
1769         
1770                     if ($product->{'installationStatus'} eq "failed"){
1771                         $result->{$product->{'productId'}}= "error";
1772                         unshift(@error_list, $product->{'productId'});
1773                         $error++;
1774                     }
1775                     if ($product->{'installationStatus'} eq "installed" && $product->{'actionRequest'}  eq "none"){
1776                         $result->{$product->{'productId'}}= "installed";
1777                         unshift(@installed_list, $product->{'productId'});
1778                         $installed++;
1779                     }
1780                     if ($product->{'installationStatus'} eq "installing"){
1781                         $result->{$product->{'productId'}}= "installing";
1782                         $installing++;
1783                         $act_status = "installing - ".$product->{'productId'};
1784                     }
1785                 }
1786             }
1787         
1788             # Estimate "rough" progress, avoid division by zero
1789             if ($products == 0) {
1790                 $result->{'progress'}= 0;
1791             } else {
1792                 $result->{'progress'}= int($installed * 100 / $products);
1793             }
1795             # Set updates in job queue
1796             if ((not $error) && (not $installing) && ($installed)) {
1797                 $act_status = "installed - ".join(", ", @installed_list);
1798             }
1799             if ($error) {
1800                 $act_status = "error - ".join(", ", @error_list);
1801             }
1802             if ($progress ne $result->{'progress'} ) {
1803                 # Updating progress and result 
1804                 my $update_statement = "UPDATE $job_queue_tn SET modified='1', progress='".$result->{'progress'}."', result='$act_status' WHERE macaddress LIKE '$macaddress' AND siserver='localhost'";
1805                 my $update_res = $job_db->update_dbentry($update_statement);
1806             }
1807             if ($progress eq 100) { 
1808                 # Updateing status
1809                 my $done_statement = "UPDATE $job_queue_tn SET modified='1', ";
1810                 if ($error) {
1811                     $done_statement .= "status='error'";
1812                 } else {
1813                     $done_statement .= "status='done'";
1814                 }
1815                 $done_statement .= " WHERE macaddress LIKE '$macaddress' AND siserver='localhost'";
1816                 my $done_res = $job_db->update_dbentry($done_statement);
1817             }
1820         }
1821     }
1823     $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay);
1827 # If a job got an update or was modified anyway, send to all other si-server an update message of this jobs.
1828 sub watch_for_modified_jobs {
1829     my ($kernel,$heap) = @_[KERNEL, HEAP];
1831     my $sql_statement = "SELECT * FROM $job_queue_tn WHERE (modified='1')"; 
1832     my $res = $job_db->select_dbentry( $sql_statement );
1833     
1834     # if db contains no jobs which should be update, do nothing
1835     if (keys %$res != 0) {
1837         if ($job_synchronization  eq "true") {
1838             # make out of the db result a gosa-si message   
1839             my $update_msg = &db_res2si_msg ($res, "foreign_job_updates", "KNOWN_SERVER", "MY_LOCAL_ADDRESS");
1840  
1841             # update all other SI-server
1842             &inform_all_other_si_server($update_msg);
1843         }
1845         # set jobs all jobs to modified = 0, wait until the next modification for updates of other si-server
1846         $sql_statement = "UPDATE $job_queue_tn SET modified='0' ";
1847         $res = $job_db->update_dbentry($sql_statement);
1848     }
1850     $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay);
1854 sub watch_for_new_jobs {
1855         if($watch_for_new_jobs_in_progress == 0) {
1856                 $watch_for_new_jobs_in_progress = 1;
1857                 my ($kernel,$heap) = @_[KERNEL, HEAP];
1859                 # check gosa job queue for jobs with executable timestamp
1860                 my $timestamp = &get_time();
1861                 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE siserver='localhost' AND status='waiting' AND (CAST(timestamp AS UNSIGNED)) < $timestamp ORDER BY timestamp";
1862                 my $res = $job_db->exec_statement( $sql_statement );
1864                 # Merge all new jobs that would do the same actions
1865                 my @drops;
1866                 my $hits;
1867                 foreach my $hit (reverse @{$res} ) {
1868                         my $macaddress= lc @{$hit}[8];
1869                         my $headertag= @{$hit}[5];
1870                         if(
1871                                 defined($hits->{$macaddress}) &&
1872                                 defined($hits->{$macaddress}->{$headertag}) &&
1873                                 defined($hits->{$macaddress}->{$headertag}[0])
1874                         ) {
1875                                 push @drops, "DELETE FROM $job_queue_tn WHERE id = $hits->{$macaddress}->{$headertag}[0]";
1876                         }
1877                         $hits->{$macaddress}->{$headertag}= $hit;
1878                 }
1880                 # Delete new jobs with a matching job in state 'processing'
1881                 foreach my $macaddress (keys %{$hits}) {
1882                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1883                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1884                                 if(defined($jobdb_id)) {
1885                                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND headertag='$jobdb_headertag' AND status='processing'";
1886                                         my $res = $job_db->exec_statement( $sql_statement );
1887                                         foreach my $hit (@{$res}) {
1888                                                 push @drops, "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1889                                         }
1890                                 } else {
1891                                         daemon_log("J ERROR: Job without id exists for macaddress $macaddress!", 1);
1892                                 }
1893                         }
1894                 }
1896                 # Commit deletion
1897                 $job_db->exec_statementlist(\@drops);
1899                 # Look for new jobs that could be executed
1900                 foreach my $macaddress (keys %{$hits}) {
1902                         # Look if there is an executing job
1903                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='processing'";
1904                         my $res = $job_db->exec_statement( $sql_statement );
1906                         # Skip new jobs for host if there is a processing job
1907                         if(defined($res) and defined @{$res}[0]) {
1908                                 # Prevent race condition if there is a trigger_activate job waiting and a goto-activation job processing
1909                                 my $row = @{$res}[0] if (ref $res eq 'ARRAY');
1910                                 if(@{$row}[5] eq 'trigger_action_reinstall') {
1911                                         my $sql_statement_2 =  "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='waiting' AND headertag = 'trigger_activate_new'"; 
1912                                         my $res_2 = $job_db->exec_statement( $sql_statement_2 );
1913                                         if(defined($res_2) and defined @{$res_2}[0]) {
1914                                                 # Set status from goto-activation to 'waiting' and update timestamp
1915                                                 $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'");
1916                                         }
1917                                 }
1918                                 next;
1919                         }
1921                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1922                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1923                                 if(defined($jobdb_id)) {
1924                                         my $job_msg = @{$hits->{$macaddress}->{$jobdb_headertag}}[7];
1926                                         daemon_log("J DEBUG: its time to execute $job_msg", 7);
1927                                         my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1928                                         my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1930                                         # expect macaddress is unique!!!!!!
1931                                         my $target = $res_hash->{1}->{hostname};
1933                                         # change header
1934                                         $job_msg =~ s/<header>job_/<header>gosa_/;
1936                                         # add sqlite_id
1937                                         $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1939                                         $job_msg =~ /<header>(\S+)<\/header>/;
1940                                         my $header = $1 ;
1941                                         my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");                    
1943                                         # update status in job queue to ...
1944                     # ... 'processing', for jobs: 'reinstall', 'update'
1945                     if (($header =~ /gosa_trigger_action_reinstall/) 
1946                             || ($header =~ /gosa_trigger_activate_new/)
1947                             || ($header =~ /gosa_trigger_action_update/)) {
1948                         my $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
1949                         my $dbres = $job_db->update_dbentry($sql_statement);
1950                     }
1952                     # ... 'done', for all other jobs, they are no longer needed in the jobqueue
1953                     else {
1954                         my $sql_statement = "UPDATE $job_queue_tn SET status='done' WHERE id=$jobdb_id";
1955                         my $dbres = $job_db->update_dbentry($sql_statement);
1956                     }
1957                 
1959                                         # We don't want parallel processing
1960                                         last;
1961                                 }
1962                         }
1963                 }
1965                 $watch_for_new_jobs_in_progress = 0;
1966                 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1967         }
1971 sub watch_for_new_messages {
1972     my ($kernel,$heap) = @_[KERNEL, HEAP];
1973     my @coll_user_msg;   # collection list of outgoing messages
1974     
1975     # check messaging_db for new incoming messages with executable timestamp
1976     my $timestamp = &get_time();
1977     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS UNSIGNED))<$timestamp AND flag='n' AND direction='in' )";
1978     my $res = $messaging_db->exec_statement( $sql_statement );
1979         foreach my $hit (@{$res}) {
1981         # create outgoing messages
1982         my $message_to = @{$hit}[3];
1983         # translate message_to to plain login name
1984         my @message_to_l = split(/,/, $message_to);  
1985                 my %receiver_h; 
1986                 foreach my $receiver (@message_to_l) {
1987                         if ($receiver =~ /^u_([\s\S]*)$/) {
1988                                 $receiver_h{$1} = 0;
1989                         } elsif ($receiver =~ /^g_([\s\S]*)$/) {
1990                                 my $group_name = $1;
1991                                 # fetch all group members from ldap and add them to receiver hash
1992                                 my $ldap_handle = &get_ldap_handle();
1993                                 if (defined $ldap_handle) {
1994                                                 my $mesg = $ldap_handle->search(
1995                                                                                 base => $ldap_base,
1996                                                                                 scope => 'sub',
1997                                                                                 attrs => ['memberUid'],
1998                                                                                 filter => "cn=$group_name",
1999                                                                                 );
2000                                                 if ($mesg->count) {
2001                                                                 my @entries = $mesg->entries;
2002                                                                 foreach my $entry (@entries) {
2003                                                                                 my @receivers= $entry->get_value("memberUid");
2004                                                                                 foreach my $receiver (@receivers) { 
2005                                                                                                 $receiver_h{$receiver} = 0;
2006                                                                                 }
2007                                                                 }
2008                                                 } 
2009                                                 # translating errors ?
2010                                                 if ($mesg->code) {
2011                                                                 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: $mesg->error", 1);
2012                                                 }
2013                                                 &release_ldap_handle($ldap_handle);
2014                                 # ldap handle error ?           
2015                                 } else {
2016                                         daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: no ldap handle available", 1);
2017                                 }
2018                         } else {
2019                                 my $sbjct = &encode_base64(@{$hit}[1]);
2020                                 my $msg = &encode_base64(@{$hit}[7]);
2021                                 &daemon_log("M WARNING: unknown receiver '$receiver' for a user-message '$sbjct - $msg'", 3); 
2022                         }
2023                 }
2024                 my @receiver_l = keys(%receiver_h);
2026         my $message_id = @{$hit}[0];
2028         #add each outgoing msg to messaging_db
2029         my $receiver;
2030         foreach $receiver (@receiver_l) {
2031             my $sql_statement = "INSERT INTO $messaging_tn (id, subject, message_from, message_to, flag, direction, delivery_time, message, timestamp) ".
2032                 "VALUES ('".
2033                 $message_id."', '".    # id
2034                 @{$hit}[1]."', '".     # subject
2035                 @{$hit}[2]."', '".     # message_from
2036                 $receiver."', '".      # message_to
2037                 "none"."', '".         # flag
2038                 "out"."', '".          # direction
2039                 @{$hit}[6]."', '".     # delivery_time
2040                 @{$hit}[7]."', '".     # message
2041                 $timestamp."'".     # timestamp
2042                 ")";
2043             &daemon_log("M DEBUG: $sql_statement", 1);
2044             my $res = $messaging_db->exec_statement($sql_statement);
2045             &daemon_log("M INFO: message '".@{$hit}[0]."' is prepared for delivery to receiver '$receiver'", 5);
2046         }
2048         # set incoming message to flag d=deliverd
2049         $sql_statement = "UPDATE $messaging_tn SET flag='p' WHERE id='$message_id'"; 
2050         &daemon_log("M DEBUG: $sql_statement", 7);
2051         $res = $messaging_db->update_dbentry($sql_statement);
2052         &daemon_log("M INFO: message '$message_id' is set to flag 'p' (processed)", 5);
2053     }
2055     $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay); 
2056     return;
2059 sub watch_for_delivery_messages {
2060     my ($kernel, $heap) = @_[KERNEL, HEAP];
2062     # select outgoing messages
2063     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( flag='p' AND direction='out' )";
2064     my $res = $messaging_db->exec_statement( $sql_statement );
2065     
2066     # build out msg for each    usr
2067     foreach my $hit (@{$res}) {
2068         my $receiver = @{$hit}[3];
2069         my $msg_id = @{$hit}[0];
2070         my $subject = @{$hit}[1];
2071         my $message = @{$hit}[7];
2073         # resolve usr -> host where usr is logged in
2074         my $sql = "SELECT * FROM $login_users_tn WHERE (user='$receiver')"; 
2075         my $res = $login_users_db->exec_statement($sql);
2077         # receiver is logged in nowhere
2078         if (not ref(@$res[0]) eq "ARRAY") { next; }    
2080         # receiver ist logged in at a client registered at local server
2081                 my $send_succeed = 0;
2082                 foreach my $hit (@$res) {
2083                                 my $receiver_host = @$hit[0];
2084                 my $delivered2host = 0;
2085                                 &daemon_log("M DEBUG: user '$receiver' is logged in at host '$receiver_host'", 7);
2087                                 # Looking for host in know_clients_db 
2088                                 my $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$receiver_host')";
2089                                 my $res = $known_clients_db->exec_statement($sql);
2091                 # Host is known in known_clients_db
2092                 if (ref(@$res[0]) eq "ARRAY") {
2093                     my $receiver_key = @{@{$res}[0]}[2];
2094                     my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
2095                     my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
2096                     my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0); 
2097                     if ($error == 0 ) {
2098                         $send_succeed++ ;
2099                         $delivered2host++ ;
2100                         &daemon_log("M DEBUG: send message for user '$receiver' to host '$receiver_host'", 7); 
2101                     } else {
2102                         &daemon_log("M DEBUG: cannot send message for user '$receiver' to host '$receiver_host'", 7); 
2103                     }
2104                 }
2105                 
2106                 # Message already send, do not need to do anything more, otherwise ...
2107                 if ($delivered2host) { next;}
2108     
2109                 # ...looking for host in foreign_clients_db
2110                 $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$receiver_host')";
2111                 $res = $foreign_clients_db->exec_statement($sql);
2112   
2113                                 # Host is known in foreign_clients_db 
2114                                 if (ref(@$res[0]) eq "ARRAY") { 
2115                     my $registration_server = @{@{$res}[0]}[2];
2116                     
2117                     # Fetch encryption key for registration server
2118                     my $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$registration_server')";
2119                     my $res = $known_server_db->exec_statement($sql);
2120                     if (ref(@$res[0]) eq "ARRAY") { 
2121                         my $registration_server_key = @{@{$res}[0]}[3];
2122                         my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
2123                         my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
2124                         my $error = &send_msg_to_target($out_msg, $registration_server, $registration_server_key, "usr_msg", 0); 
2125                         if ($error == 0 ) {
2126                             $send_succeed++ ;
2127                             $delivered2host++ ;
2128                             &daemon_log("M DEBUG: send message for user '$receiver' to server '$registration_server'", 7); 
2129                         } else {
2130                             &daemon_log("M ERROR: cannot send message for user '$receiver' to server '$registration_server'", 1); 
2131                         }
2133                     } else {
2134                         &daemon_log("M ERROR: host '$receiver_host' is reported to be ".
2135                                 "registrated at server '$registration_server', ".
2136                                 "but no data available in known_server_db ", 1); 
2137                     }
2138                 }
2139                 
2140                 if (not $delivered2host) {
2141                     &daemon_log("M ERROR: unable to send user message to host '$receiver_host'", 1);
2142                 }
2143                 }
2145                 if ($send_succeed) {
2146                                 # set outgoing msg at db to deliverd
2147                                 my $sql = "UPDATE $messaging_tn SET flag='d' WHERE (id='$msg_id' AND direction='out' AND message_to='$receiver')"; 
2148                                 my $res = $messaging_db->exec_statement($sql); 
2149                 &daemon_log("M INFO: send message for user '$receiver' to logged in hosts", 5);
2150                 } else {
2151             &daemon_log("M WARNING: failed to deliver message for user '$receiver'", 3); 
2152         }
2153         }
2155     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay); 
2156     return;
2160 sub watch_for_done_messages {
2161     my ($kernel,$heap) = @_[KERNEL, HEAP];
2163     my $sql = "SELECT * FROM $messaging_tn WHERE (flag='p' AND direction='in')"; 
2164     my $res = $messaging_db->exec_statement($sql); 
2166     foreach my $hit (@{$res}) {
2167         my $msg_id = @{$hit}[0];
2169         my $sql = "SELECT * FROM $messaging_tn WHERE (id='$msg_id' AND direction='out' AND (NOT flag='s'))"; 
2170         my $res = $messaging_db->exec_statement($sql);
2172         # not all usr msgs have been seen till now
2173         if ( ref(@$res[0]) eq "ARRAY") { next; }
2174         
2175         $sql = "DELETE FROM $messaging_tn WHERE (id='$msg_id')"; 
2176         $res = $messaging_db->exec_statement($sql);
2177     
2178     }
2180     $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay); 
2181     return;
2185 sub watch_for_old_known_clients {
2186     my ($kernel,$heap) = @_[KERNEL, HEAP];
2188     my $sql_statement = "SELECT * FROM $known_clients_tn";
2189     my $res = $known_clients_db->select_dbentry( $sql_statement );
2191     my $cur_time = int(&get_time());
2193     while ( my ($hit_num, $hit) = each %$res) {
2194         my $expired_timestamp = int($hit->{'timestamp'});
2195         $expired_timestamp =~ /(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/;
2196         my $dt = DateTime->new( year   => $1,
2197                 month  => $2,
2198                 day    => $3,
2199                 hour   => $4,
2200                 minute => $5,
2201                 second => $6,
2202                 );
2204         $dt->add( seconds => 2 * int($hit->{'keylifetime'}) );
2205         $expired_timestamp = $dt->ymd('').$dt->hms('');
2206         if ($cur_time > $expired_timestamp) {
2207             my $hostname = $hit->{'hostname'};
2208             my $del_sql = "DELETE FROM $known_clients_tn WHERE hostname='$hostname'"; 
2209             my $del_res = $known_clients_db->exec_statement($del_sql);
2211             &main::daemon_log("0 INFO: timestamp '".$hit->{'timestamp'}."' of client '$hostname' is expired('$expired_timestamp'), client will be deleted from known_clients_db", 5);
2212         }
2214     }
2216     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
2220 sub watch_for_next_tasks {
2221     my ($kernel,$heap) = @_[KERNEL, HEAP];
2223     my $sql = "SELECT * FROM $incoming_tn";
2224     my $res = $incoming_db->select_dbentry($sql);
2225     
2226     while ( my ($hit_num, $hit) = each %$res) {
2227         my $headertag = $hit->{'headertag'};
2228         if ($headertag =~ /^answer_(\d+)/) {
2229             # do not start processing, this message is for a still running POE::Wheel
2230             next;
2231         }
2232         my $message_id = $hit->{'id'};
2233         my $session_id = $hit->{'sessionid'};
2234         &daemon_log("$session_id DEBUG: start processing for message with incoming id: '$message_id'", 11);
2236         $kernel->yield('next_task', $hit);
2238         my $sql = "DELETE FROM $incoming_tn WHERE id=$message_id";
2239         my $res = $incoming_db->exec_statement($sql);
2240     }
2242     $kernel->delay_set('watch_for_next_tasks', 1); 
2246 sub get_ldap_handle {
2247         my ($session_id) = @_;
2248         my $heap;
2250         if (not defined $session_id ) { $session_id = 0 };
2251         if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
2253         my ($package, $file, $row, $subroutine, $hasArgs, $wantArray, $evalText, $isRequire) = caller(1);
2254         my $caller_text = "subroutine $subroutine";
2255         if ($subroutine eq "(eval)") {
2256                 $caller_text = "eval block within file '$file' for '$evalText'"; 
2257         }
2258         daemon_log("$session_id DEBUG: new ldap handle for '$caller_text' required!", 42);
2260 get_handle:
2261         my $ldap_handle = Net::LDAP->new( $ldap_uri );
2262         if (not ref $ldap_handle) {
2263                 daemon_log("$session_id ERROR: Connection to LDAP URI '$ldap_uri' failed! Retrying!", 1);
2264                 usleep(100000);
2265                 goto get_handle;
2266         } else {
2267                 daemon_log("$session_id DEBUG: Connection to LDAP URI '$ldap_uri' established.", 42);
2268         }
2270         $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);
2271         return $ldap_handle;
2275 sub release_ldap_handle {
2276         my ($ldap_handle, $session_id) = @_ ;
2277         if (not defined $session_id ) { $session_id = 0 };
2279         if(ref $ldap_handle) {
2280           $ldap_handle->disconnect();
2281   }
2282         &main::daemon_log("$session_id DEBUG: Released a ldap handle!", 42);
2283         return;
2287 sub change_fai_state {
2288         my ($st, $targets, $session_id) = @_;
2289         $session_id = 0 if not defined $session_id;
2290         # Set FAI state to localboot
2291         my %mapActions= (
2292                 reboot    => '',
2293                 update    => 'softupdate',
2294                 localboot => 'localboot',
2295                 reinstall => 'install',
2296                 rescan    => '',
2297                 wake      => '',
2298                 memcheck  => 'memcheck',
2299                 sysinfo   => 'sysinfo',
2300                 install   => 'install',
2301         );
2303         # Return if this is unknown
2304         if (!exists $mapActions{ $st }){
2305                 daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1); 
2306                 return;
2307         }
2309         my $state= $mapActions{ $st };
2311         # Build search filter for hosts
2312         my $search= "(&(objectClass=GOhard)";
2313         foreach (@{$targets}){
2314                 $search.= "(macAddress=$_)";
2315         }
2316         $search.= ")";
2318         # If there's any host inside of the search string, procress them
2319         if (!($search =~ /macAddress/)){
2320                 daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);    
2321                 return;
2322         }
2324         my $ldap_handle = &get_ldap_handle($session_id);
2325         # Perform search for Unit Tag
2326         my $mesg = $ldap_handle->search(
2327                 base   => $ldap_base,
2328                 scope  => 'sub',
2329                 attrs  => ['dn', 'FAIstate', 'objectClass'],
2330                 filter => "$search"
2331         );
2333         if ($mesg->count) {
2334                 my @entries = $mesg->entries;
2335                 if (0 == @entries) {
2336                         daemon_log("$session_id ERROR: ldap search failed: ldap_base=$ldap_base, filter=$search", 1); 
2337                 }
2339                 foreach my $entry (@entries) {
2340                         # Only modify entry if it is not set to '$state'
2341                         if ($entry->get_value("FAIstate") ne "$state"){
2342                                 daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
2343                                 my $result;
2344                                 my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
2345                                 if (exists $tmp{'FAIobject'}){
2346                                         if ($state eq ''){
2347                                                 $result= $ldap_handle->modify($entry->dn, changes => [ delete => [ FAIstate => [] ] ]);
2348                                         } else {
2349                                                 $result= $ldap_handle->modify($entry->dn, changes => [ replace => [ FAIstate => $state ] ]);
2350                                         }
2351                                 } elsif ($state ne ''){
2352                                         $result= $ldap_handle->modify($entry->dn, changes => [ add => [ objectClass => 'FAIobject' ], add => [ FAIstate => $state ] ]);
2353                                 }
2355                                 # Errors?
2356                                 if ($result->code){
2357                                         daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2358                                 }
2359                         } else {
2360                                 daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 42); 
2361                         }  
2362                 }
2363         } else {
2364                 daemon_log("$session_id ERROR: LDAP search failed: ldap_base=$ldap_base, filter=$search", 1);
2365         }
2366         &release_ldap_handle($ldap_handle, $session_id);                  
2368         return;
2372 sub change_goto_state {
2373     my ($st, $targets, $session_id) = @_;
2374     $session_id = 0  if not defined $session_id;
2376     # Switch on or off?
2377     my $state= $st eq 'active' ? 'active': 'locked';
2379     my $ldap_handle = &get_ldap_handle($session_id);
2380     if( defined($ldap_handle) ) {
2382       # Build search filter for hosts
2383       my $search= "(&(objectClass=GOhard)";
2384       foreach (@{$targets}){
2385         $search.= "(macAddress=$_)";
2386       }
2387       $search.= ")";
2389       # If there's any host inside of the search string, procress them
2390       if (!($search =~ /macAddress/)){
2391               &release_ldap_handle($ldap_handle);
2392         return;
2393       }
2395       # Perform search for Unit Tag
2396       my $mesg = $ldap_handle->search(
2397           base   => $ldap_base,
2398           scope  => 'sub',
2399           attrs  => ['dn', 'gotoMode'],
2400           filter => "$search"
2401           );
2403       if ($mesg->count) {
2404         my @entries = $mesg->entries;
2405         foreach my $entry (@entries) {
2407           # Only modify entry if it is not set to '$state'
2408           if ($entry->get_value("gotoMode") ne $state){
2410             daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
2411             my $result;
2412             $result= $ldap_handle->modify($entry->dn, changes => [replace => [ gotoMode => $state ] ]);
2414             # Errors?
2415             if ($result->code){
2416               &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2417             }
2419           }
2420         }
2421       } else {
2422                 daemon_log("$session_id ERROR: LDAP search failed in function change_goto_state: ldap_base=$ldap_base, filter=$search", 1);
2423           }
2425     }
2426         &release_ldap_handle($ldap_handle, $session_id);
2427         return;
2431 sub run_recreate_packages_db {
2432     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2433     my $session_id = $session->ID;
2434         &main::daemon_log("$session_id INFO: Recreating FAI Packages DB ('$fai_release_tn', '$fai_server_tn', '$packages_list_tn')", 5);
2435         $kernel->yield('create_fai_release_db', $fai_release_tn);
2436         $kernel->yield('create_fai_server_db', $fai_server_tn);
2437         return;
2441 sub run_create_fai_server_db {
2442     my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2443     my $session_id = $session->ID;
2444     my $task = POE::Wheel::Run->new(
2445             Program => sub { &create_fai_server_db($table_name,$kernel, undef, $session_id) },
2446             StdoutEvent  => "session_run_result",
2447             StderrEvent  => "session_run_debug",
2448             CloseEvent   => "session_run_done",
2449             );
2451     $heap->{task}->{ $task->ID } = $task;
2452     return;
2456 sub create_fai_server_db {
2457         my ($table_name, $kernel, $dont_create_packages_list, $session_id) = @_;
2458         my $result;
2460         if (not defined $session_id) { $session_id = 0; }
2461         my $ldap_handle = &get_ldap_handle($session_id);
2462         if(defined($ldap_handle)) {
2463                 daemon_log("$session_id INFO: create_fai_server_db: start", 5);
2464                 my $mesg= $ldap_handle->search(
2465                         base   => $ldap_base,
2466                         scope  => 'sub',
2467                         attrs  => ['FAIrepository', 'gosaUnitTag'],
2468                         filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
2469                 );
2470                 if($mesg->{'resultCode'} == 0 &&
2471                         $mesg->count != 0) {
2472                         foreach my $entry (@{$mesg->{entries}}) {
2473                                 if($entry->exists('FAIrepository')) {
2474                                         # Add an entry for each Repository configured for server
2475                                         foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
2476                                                 my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
2477                                                 my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
2478                                                 $result= $fai_server_db->add_dbentry( { 
2479                                                                 table => $table_name,
2480                                                                 primkey => ['server', 'fai_release', 'tag'],
2481                                                                 server => $tmp_url,
2482                                                                 fai_release => $tmp_release,
2483                                                                 sections => $tmp_sections,
2484                                                                 tag => (length($tmp_tag)>0)?$tmp_tag:"",
2485                                                         } );
2486                                         }
2487                                 }
2488                         }
2489                 }
2490                 daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
2491                 &release_ldap_handle($ldap_handle);
2493                 # TODO: Find a way to post the 'create_packages_list_db' event
2494                 if(not defined($dont_create_packages_list)) {
2495                         &create_packages_list_db(undef, $session_id);
2496                 }
2497         }       
2499         return $result;
2503 sub run_create_fai_release_db {
2504         my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2505         my $session_id = $session->ID;
2506         my $task = POE::Wheel::Run->new(
2507                 Program => sub { &create_fai_release_db($table_name, $session_id) },
2508                 StdoutEvent  => "session_run_result",
2509                 StderrEvent  => "session_run_debug",
2510                 CloseEvent   => "session_run_done",
2511         );
2513         $heap->{task}->{ $task->ID } = $task;
2514         return;
2518 sub create_fai_release_db {
2519         my ($table_name, $session_id) = @_;
2520         my $result;
2522         # used for logging
2523         if (not defined $session_id) { $session_id = 0; }
2525         my $ldap_handle = &get_ldap_handle($session_id);
2526         if(defined($ldap_handle)) {
2527                 daemon_log("$session_id INFO: create_fai_release_db: start",5);
2528                 my $mesg= $ldap_handle->search(
2529                         base   => $ldap_base,
2530                         scope  => 'sub',
2531                         attrs  => [],
2532                         filter => "(&(objectClass=organizationalUnit)(ou=fai))",
2533                 );
2534                 if(($mesg->code == 0) && ($mesg->count != 0))
2535                 {
2536                         daemon_log("$session_id DEBUG: create_fai_release_db: count " . $mesg->count,138);
2538                         # Walk through all possible FAI container ou's
2539                         my @sql_list;
2540                         my $timestamp= &get_time();
2541                         foreach my $ou (@{$mesg->{entries}}) {
2542                                 my $tmp_classes= resolve_fai_classes($ou->dn, $ldap_handle, $session_id);
2543                                 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
2544                                         my @tmp_array=get_fai_release_entries($tmp_classes);
2545                                         if(@tmp_array) {
2546                                                 foreach my $entry (@tmp_array) {
2547                                                         if(defined($entry) && ref($entry) eq 'HASH') {
2548                                                                 my $sql= 
2549                                                                 "INSERT INTO $table_name "
2550                                                                 ."(timestamp, fai_release, class, type, state) VALUES ("
2551                                                                 .$timestamp.","
2552                                                                 ."'".$entry->{'release'}."',"
2553                                                                 ."'".$entry->{'class'}."',"
2554                                                                 ."'".$entry->{'type'}."',"
2555                                                                 ."'".$entry->{'state'}."')";
2556                                                                 push @sql_list, $sql;
2557                                                         }
2558                                                 }
2559                                         }
2560                                 }
2561                         }
2563                         daemon_log("$session_id DEBUG: create_fai_release_db: Inserting ".scalar @sql_list." entries to DB",138);
2564             &release_ldap_handle($ldap_handle);
2565                         if(@sql_list) {
2566                                 unshift @sql_list, "VACUUM";
2567                                 unshift @sql_list, "DELETE FROM $table_name";
2568                                 $fai_release_db->exec_statementlist(\@sql_list);
2569                         }
2570                         daemon_log("$session_id DEBUG: create_fai_release_db: Done with inserting",138);
2571                 } else {
2572                         daemon_log("$session_id INFO: create_fai_release_db: error: " . $mesg->code, 5);
2573                 }
2574                 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
2575         }
2576         return $result;
2579 sub get_fai_types {
2580         my $tmp_classes = shift || return undef;
2581         my @result;
2583         foreach my $type(keys %{$tmp_classes}) {
2584                 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
2585                         my $entry = {
2586                                 type => $type,
2587                                 state => $tmp_classes->{$type}[0],
2588                         };
2589                         push @result, $entry;
2590                 }
2591         }
2593         return @result;
2596 sub get_fai_state {
2597         my $result = "";
2598         my $tmp_classes = shift || return $result;
2600         foreach my $type(keys %{$tmp_classes}) {
2601                 if(defined($tmp_classes->{$type}[0])) {
2602                         $result = $tmp_classes->{$type}[0];
2603                         
2604                 # State is equal for all types in class
2605                         last;
2606                 }
2607         }
2609         return $result;
2612 sub resolve_fai_classes {
2613         my ($fai_base, $ldap_handle, $session_id) = @_;
2614         if (not defined $session_id) { $session_id = 0; }
2615         my $result;
2616         my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
2617         my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
2618         my $fai_classes;
2620         daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base", 138);
2621         my $mesg= $ldap_handle->search(
2622                 base   => $fai_base,
2623                 scope  => 'sub',
2624                 attrs  => ['cn','objectClass','FAIstate'],
2625                 filter => $fai_filter,
2626         );
2627         daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries", 138);
2629         if($mesg->{'resultCode'} == 0 &&
2630                 $mesg->count != 0) {
2631                 foreach my $entry (@{$mesg->{entries}}) {
2632                         if($entry->exists('cn')) {
2633                                 my $tmp_dn= $entry->dn();
2634                                 $tmp_dn= substr( $tmp_dn, 0, length($tmp_dn)
2635                                         - length($fai_base) - 1 );
2637                                 # Skip classname and ou dn parts for class
2638                                 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?)$/;
2640                                 # Skip classes without releases
2641                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2642                                         next;
2643                                 }
2645                                 my $tmp_cn= $entry->get_value('cn');
2646                                 my $tmp_state= $entry->get_value('FAIstate');
2648                                 my $tmp_type;
2649                                 # Get FAI type
2650                                 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
2651                                         if(grep $_ eq $oclass, @possible_fai_classes) {
2652                                                 $tmp_type= $oclass;
2653                                                 last;
2654                                         }
2655                                 }
2657                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2658                                         # A Subrelease
2659                                         my @sub_releases = split(/,/, $tmp_release);
2661                                         # Walk through subreleases and build hash tree
2662                                         my $hash;
2663                                         while(my $tmp_sub_release = pop @sub_releases) {
2664                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2665                                         }
2666                                         eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
2667                                 } else {
2668                                         # A branch, no subrelease
2669                                         push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
2670                                 }
2671                         } elsif (!$entry->exists('cn')) {
2672                                 my $tmp_dn= $entry->dn();
2673                                 $tmp_dn= substr( $tmp_dn, 0, length($tmp_dn)
2674                                         - length($fai_base) - 1 );
2675                                 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?)$/;
2677                                 # Skip classes without releases
2678                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2679                                         next;
2680                                 }
2682                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2683                                         # A Subrelease
2684                                         my @sub_releases= split(/,/, $tmp_release);
2686                                         # Walk through subreleases and build hash tree
2687                                         my $hash;
2688                                         while(my $tmp_sub_release = pop @sub_releases) {
2689                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2690                                         }
2691                                         # Remove the last two characters
2692                                         chop($hash);
2693                                         chop($hash);
2695                                         eval('$fai_classes->'.$hash.'= {}');
2696                                 } else {
2697                                         # A branch, no subrelease
2698                                         if(!exists($fai_classes->{$tmp_release})) {
2699                                                 $fai_classes->{$tmp_release} = {};
2700                                         }
2701                                 }
2702                         }
2703                 }
2705                 # The hash is complete, now we can honor the copy-on-write based missing entries
2706                 foreach my $release (keys %$fai_classes) {
2707                         $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
2708                 }
2709         }
2710         return $result;
2713 sub apply_fai_inheritance {
2714        my $fai_classes = shift || return {};
2715        my $tmp_classes;
2717        # Get the classes from the branch
2718        foreach my $class (keys %{$fai_classes}) {
2719                # Skip subreleases
2720                if($class =~ /^ou=.*$/) {
2721                        next;
2722                } else {
2723                        $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
2724                }
2725        }
2727        # Apply to each subrelease
2728        foreach my $subrelease (keys %{$fai_classes}) {
2729                if($subrelease =~ /ou=/) {
2730                        foreach my $tmp_class (keys %{$tmp_classes}) {
2731                                if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
2732                                        $fai_classes->{$subrelease}->{$tmp_class} =
2733                                        deep_copy($tmp_classes->{$tmp_class});
2734                                } else {
2735                                        foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
2736                                                if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
2737                                                        $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
2738                                                        deep_copy($tmp_classes->{$tmp_class}->{$type});
2739                                                }
2740                                        }
2741                                }
2742                        }
2743                }
2744        }
2746        # Find subreleases in deeper levels
2747        foreach my $subrelease (keys %{$fai_classes}) {
2748                if($subrelease =~ /ou=/) {
2749                        foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
2750                                if($subsubrelease =~ /ou=/) {
2751                                        apply_fai_inheritance($fai_classes->{$subrelease});
2752                                }
2753                        }
2754                }
2755        }
2757        return $fai_classes;
2760 sub get_fai_release_entries {
2761         my $tmp_classes = shift || return;
2762         my $parent = shift || "";
2763         my @result = shift || ();
2765         foreach my $entry (keys %{$tmp_classes}) {
2766                 if(defined($entry)) {
2767                         if($entry =~ /^ou=.*$/) {
2768                                 my $release_name = $entry;
2769                                 $release_name =~ s/ou=//g;
2770                                 if(length($parent)>0) {
2771                                         $release_name = $parent."/".$release_name;
2772                                 }
2773                                 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2774                                 foreach my $bufentry(@bufentries) {
2775                                         push @result, $bufentry;
2776                                 }
2777                         } else {
2778                                 my @types = get_fai_types($tmp_classes->{$entry});
2779                                 foreach my $type (@types) {
2780                                         push @result, 
2781                                         {
2782                                                 'class' => $entry,
2783                                                 'type' => $type->{'type'},
2784                                                 'release' => $parent,
2785                                                 'state' => $type->{'state'},
2786                                         };
2787                                 }
2788                         }
2789                 }
2790         }
2792         return @result;
2795 sub deep_copy {
2796         my $this = shift;
2797         if (not ref $this) {
2798                 $this;
2799         } elsif (ref $this eq "ARRAY") {
2800                 [map deep_copy($_), @$this];
2801         } elsif (ref $this eq "HASH") {
2802                 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2803         } else { die "what type is $_?" }
2807 sub session_run_result {
2808     my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];    
2809     $kernel->sig(CHLD => "child_reap");
2812 sub session_run_debug {
2813     my $result = $_[ARG0];
2814     print STDERR "$result\n";
2817 sub session_run_done {
2818     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2819     delete $heap->{task}->{$task_id};
2820         if (exists $heap->{ldap_handle}->{$task_id}) {
2821                 &release_ldap_handle($heap->{ldap_handle}->{$task_id});
2822         }
2823         delete $heap->{ldap_handle}->{$task_id};
2827 sub create_sources_list {
2828         my $session_id = shift || 0;
2829         my $result="/tmp/gosa_si_tmp_sources_list";
2831         # Remove old file
2832         if(stat($result)) {
2833                 unlink($result);
2834                 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7); 
2835         }
2837         my $fh;
2838         open($fh, ">$result");
2839         if (not defined $fh) {
2840                 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7); 
2841                 return undef;
2842         }
2843         if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2844                 my $ldap_handle = &get_ldap_handle($session_id);
2845                 my $mesg=$ldap_handle->search(
2846                         base    => $main::ldap_server_dn,
2847                         scope   => 'base',
2848                         attrs   => 'FAIrepository',
2849                         filter  => 'objectClass=FAIrepositoryServer'
2850                 );
2851                 if($mesg->count) {
2852                         foreach my $entry(@{$mesg->{'entries'}}) {
2853                                 foreach my $value(@{$entry->get_value('FAIrepository', asref => 1)}) {
2854                                         my ($server, $tag, $release, $sections)= split /\|/, $value;
2855                                         my $line = "deb $server $release";
2856                                         $sections =~ s/,/ /g;
2857                                         $line.= " $sections";
2858                                         print $fh $line."\n";
2859                                 }
2860                         }
2861                 }
2862                 &release_ldap_handle($ldap_handle);
2863         } else {
2864                 if (defined $main::ldap_server_dn){
2865                         &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1); 
2866                 } else {
2867                         &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2868                 }
2869         }
2870         close($fh);
2872         return $result;
2876 sub run_create_packages_list_db {
2877     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2878         my $session_id = $session->ID;
2879         my $task = POE::Wheel::Run->new(
2880                                         Priority => +20,
2881                                         Program => sub {&create_packages_list_db(undef, $session_id)},
2882                                         StdoutEvent  => "session_run_result",
2883                                         StderrEvent  => "session_run_debug",
2884                                         CloseEvent   => "session_run_done",
2885                                         );
2886         $heap->{task}->{ $task->ID } = $task;
2890 sub create_packages_list_db {
2891         my ($sources_file, $session_id) = @_;
2892         
2893         # it should not be possible to trigger a recreation of packages_list_db
2894         # while packages_list_db is under construction, so set flag packages_list_under_construction
2895         # which is tested befor recreation can be started
2896         if (-r $packages_list_under_construction) {
2897                 daemon_log("$session_id WARNING: packages_list_db is right now under construction, please wait until this process is finished", 3);
2898                 return;
2899         } else {
2900                 daemon_log("$session_id INFO: create_packages_list_db: start", 5); 
2901                 # set packages_list_under_construction to true
2902                 system("touch $packages_list_under_construction");
2903                 @packages_list_statements=();
2904         }
2906         if (not defined $session_id) { $session_id = 0; }
2908         if (not defined $sources_file) { 
2909                 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5); 
2910                 $sources_file = &create_sources_list($session_id);
2911         }
2913         if (not defined $sources_file) {
2914                 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1); 
2915                 unlink($packages_list_under_construction);
2916                 return;
2917         }
2919         my $line;
2921         open(CONFIG, "<$sources_file") or do {
2922                 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2923                 unlink($packages_list_under_construction);
2924                 return;
2925         };
2927         # Read lines
2928         while ($line = <CONFIG>){
2929                 # Unify
2930                 chop($line);
2931                 $line =~ s/^\s+//;
2932                 $line =~ s/^\s+/ /;
2934                 # Strip comments
2935                 $line =~ s/#.*$//g;
2937                 # Skip empty lines
2938                 if ($line =~ /^\s*$/){
2939                         next;
2940                 }
2942                 # Interpret deb line
2943                 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2944                         my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2945                         my $section;
2946                         foreach $section (split(' ', $sections)){
2947                                 &parse_package_info( $baseurl, $dist, $section, $session_id );
2948                         }
2949                 }
2950         }
2952         close (CONFIG);
2954         if(keys(%repo_dirs)) {
2955                 find(\&cleanup_and_extract, keys( %repo_dirs ));
2956                 &main::strip_packages_list_statements();
2957                 $packages_list_db->exec_statementlist(\@packages_list_statements);
2958         }
2959         unlink($packages_list_under_construction);
2960         daemon_log("$session_id INFO: create_packages_list_db: finished", 5); 
2961         return;
2964 # This function should do some intensive task to minimize the db-traffic
2965 sub strip_packages_list_statements {
2966         my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2967         my @new_statement_list=();
2968         my $hash;
2969         my $insert_hash;
2970         my $update_hash;
2971         my $delete_hash;
2972         my $known_packages_hash;
2973         my $local_timestamp=get_time();
2975         foreach my $existing_entry (@existing_entries) {
2976                 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
2977         }
2979         foreach my $statement (@packages_list_statements) {
2980                 if($statement =~ /^INSERT/i) {
2981                         # Assign the values from the insert statement
2982                         my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~ 
2983                         /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
2984                         if(exists($hash->{$distribution}->{$package}->{$version})) {
2985                                 # If section or description has changed, update the DB
2986                                 if( 
2987                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or 
2988                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
2989                                 ) {
2990                                         @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
2991                                 } else {
2992                                         # package is already present in database. cache this knowledge for later use
2993                                         @{$known_packages_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2994                                 }
2995                         } else {
2996                                 # Insert a non-existing entry to db
2997                                 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2998                         }
2999                 } elsif ($statement =~ /^UPDATE/i) {
3000                         my ($template,$package,$version) = ($1,$2,$3) if $statement =~
3001                         /^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;
3002                         foreach my $distribution (keys %{$hash}) {
3003                                 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
3004                                         # update the insertion hash to execute only one query per package (insert instead insert+update)
3005                                         @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
3006                                 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
3007                                         if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
3008                                                 my $section;
3009                                                 my $description;
3010                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
3011                                                         length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
3012                                                         $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
3013                                                 }
3014                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
3015                                                         $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
3016                                                 }
3017                                                 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
3018                                         }
3019                                 }
3020                         }
3021                 }
3022         }
3024         # Check for orphaned entries
3025         foreach my $existing_entry (@existing_entries) {
3026                 my $distribution= @{$existing_entry}[0];
3027                 my $package= @{$existing_entry}[1];
3028                 my $version= @{$existing_entry}[2];
3029                 my $section= @{$existing_entry}[3];
3031                 if(
3032                         exists($insert_hash->{$distribution}->{$package}->{$version}) ||
3033                         exists($update_hash->{$distribution}->{$package}->{$version}) ||
3034                         exists($known_packages_hash->{$distribution}->{$package}->{$version})
3035                 ) {
3036                         next;
3037                 } else {
3038                         # Insert entry to delete hash
3039                         @{$delete_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section);
3040                 }
3041         }
3043         # unroll the insert hash
3044         foreach my $distribution (keys %{$insert_hash}) {
3045                 foreach my $package (keys %{$insert_hash->{$distribution}}) {
3046                         foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
3047                                 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
3048                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
3049                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
3050                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
3051                                 ."'$local_timestamp')";
3052                         }
3053                 }
3054         }
3056         # unroll the update hash
3057         foreach my $distribution (keys %{$update_hash}) {
3058                 foreach my $package (keys %{$update_hash->{$distribution}}) {
3059                         foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
3060                                 my $set = "";
3061                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
3062                                         $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
3063                                 }
3064                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
3065                                         $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
3066                                 }
3067                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
3068                                         $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
3069                                 }
3070                                 if(defined($set) and length($set) > 0) {
3071                                         $set .= "timestamp = '$local_timestamp'";
3072                                 } else {
3073                                         next;
3074                                 }
3075                                 push @new_statement_list, 
3076                                 "UPDATE $main::packages_list_tn SET $set WHERE"
3077                                 ." distribution = '$distribution'"
3078                                 ." AND package = '$package'"
3079                                 ." AND version = '$version'";
3080                         }
3081                 }
3082         }
3083         
3084         # unroll the delete hash
3085         foreach my $distribution (keys %{$delete_hash}) {
3086                 foreach my $package (keys %{$delete_hash->{$distribution}}) {
3087                         foreach my $version (keys %{$delete_hash->{$distribution}->{$package}}) {
3088                                 my $section = @{$delete_hash->{$distribution}->{$package}->{$version}}[3];
3089                                 push @new_statement_list, "DELETE FROM $main::packages_list_tn WHERE distribution='$distribution' AND package='$package' AND version='$version' AND section='$section'";
3090                         }
3091                 }
3092         }
3094         unshift(@new_statement_list, "VACUUM");
3096         @packages_list_statements = @new_statement_list;
3100 sub parse_package_info {
3101     my ($baseurl, $dist, $section, $session_id)= @_;
3102     my ($package);
3103     if (not defined $session_id) { $session_id = 0; }
3104     my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
3105     $repo_dirs{ "${repo_path}/pool" } = 1;
3107     foreach $package ("Packages.gz"){
3108         daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
3109         get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
3110         parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
3111     }
3112     
3116 sub get_package {
3117     my ($url, $dest, $session_id)= @_;
3118     if (not defined $session_id) { $session_id = 0; }
3120     my $tpath = dirname($dest);
3121     -d "$tpath" || mkpath "$tpath";
3123     # This is ugly, but I've no time to take a look at "how it works in perl"
3124     if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
3125         system("gunzip -cd '$dest' > '$dest.in'");
3126         daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 7);
3127         unlink($dest);
3128         daemon_log("$session_id DEBUG: delete file '$dest'", 7); 
3129     } else {
3130         daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' into '$dest' failed!", 1);
3131     }
3132     return 0;
3136 sub parse_package {
3137     my ($path, $dist, $srv_path, $session_id)= @_;
3138     if (not defined $session_id) { $session_id = 0;}
3139     my ($package, $version, $section, $description);
3140     my $PACKAGES;
3141     my $timestamp = &get_time();
3143     if(not stat("$path.in")) {
3144         daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
3145         return;
3146     }
3148     open($PACKAGES, "<$path.in");
3149     if(not defined($PACKAGES)) {
3150         daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1); 
3151         return;
3152     }
3154     # Read lines
3155     while (<$PACKAGES>){
3156         my $line = $_;
3157         # Unify
3158         chop($line);
3160         # Use empty lines as a trigger
3161         if ($line =~ /^\s*$/){
3162             my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
3163             push(@packages_list_statements, $sql);
3164             $package = "none";
3165             $version = "none";
3166             $section = "none";
3167             $description = "none"; 
3168             next;
3169         }
3171         # Trigger for package name
3172         if ($line =~ /^Package:\s/){
3173             ($package)= ($line =~ /^Package: (.*)$/);
3174             next;
3175         }
3177         # Trigger for version
3178         if ($line =~ /^Version:\s/){
3179             ($version)= ($line =~ /^Version: (.*)$/);
3180             next;
3181         }
3183         # Trigger for description
3184         if ($line =~ /^Description:\s/){
3185             ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
3186             next;
3187         }
3189         # Trigger for section
3190         if ($line =~ /^Section:\s/){
3191             ($section)= ($line =~ /^Section: (.*)$/);
3192             next;
3193         }
3195         # Trigger for filename
3196         if ($line =~ /^Filename:\s/){
3197             my ($filename) = ($line =~ /^Filename: (.*)$/);
3198             store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
3199             next;
3200         }
3201     }
3203     close( $PACKAGES );
3204     unlink( "$path.in" );
3208 sub store_fileinfo {
3209     my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
3211     my %fileinfo = (
3212         'package' => $package,
3213         'dist' => $dist,
3214         'version' => $vers,
3215     );
3217     $repo_files{ "${srvdir}/$file" } = \%fileinfo;
3221 sub cleanup_and_extract {
3222         my $fileinfo = $repo_files{ $File::Find::name };
3224         if( defined $fileinfo ) {
3225                 my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
3226                 my $sql;
3227                 my $package = $fileinfo->{ 'package' };
3228                 my $newver = $fileinfo->{ 'version' };
3230                 mkpath($dir);
3231                 system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
3233                 if( -f "$dir/DEBIAN/templates" ) {
3235                         daemon_log("0 DEBUG: Found debconf templates in '$package' - $newver", 7);
3237                         my $tmpl= ""; {
3238                                 local $/=undef;
3239                                 open FILE, "$dir/DEBIAN/templates";
3240                                 $tmpl = &encode_base64(<FILE>);
3241                                 close FILE;
3242                         }
3243                         rmtree("$dir/DEBIAN/templates");
3245                         $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
3246                         push @packages_list_statements, $sql;
3247                 }
3248         }
3250         return;
3254 sub prepare_server_registration 
3256         # Add foreign server from cfg file
3257         my @foreign_server_list;
3258         if ($foreign_server_string ne "") {
3259             my @cfg_foreign_server_list = split(",", $foreign_server_string);
3260             foreach my $foreign_server (@cfg_foreign_server_list) {
3261                 push(@foreign_server_list, $foreign_server);
3262             }
3263         
3264             daemon_log("0 INFO: found foreign server in config file: ".join(", ", @foreign_server_list), 5);
3265         }
3266         
3267         # Perform a DNS lookup for server registration if flag is true
3268         if ($dns_lookup eq "true") {
3269             # Add foreign server from dns
3270             my @tmp_servers;
3271             if (not $server_domain) {
3272                 # Try our DNS Searchlist
3273                 for my $domain(get_dns_domains()) {
3274                     chomp($domain);
3275                     my ($tmp_domains, $error_string) = &get_server_addresses($domain);
3276                     if(@$tmp_domains) {
3277                         for my $tmp_server(@$tmp_domains) {
3278                             push @tmp_servers, $tmp_server;
3279                         }
3280                     }
3281                 }
3282                 if(@tmp_servers && length(@tmp_servers)==0) {
3283                     daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3284                 }
3285             } else {
3286                 @tmp_servers = &get_server_addresses($server_domain);
3287                 if( 0 == @tmp_servers ) {
3288                     daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3289                 }
3290             }
3291         
3292             daemon_log("0 INFO: found foreign server via DNS ".join(", ", @tmp_servers), 5);    
3293         
3294             foreach my $server (@tmp_servers) { 
3295                 unshift(@foreign_server_list, $server); 
3296             }
3297         } else {
3298             daemon_log("0 INFO: DNS lookup for server registration is disabled", 5);
3299         }
3300         
3301         # eliminate duplicate entries
3302         @foreign_server_list = &del_doubles(@foreign_server_list);
3303         my $all_foreign_server = join(", ", @foreign_server_list);
3304         daemon_log("0 INFO: found foreign server in config file and DNS: '$all_foreign_server'", 5);
3305         
3306         # add all found foreign servers to known_server
3307         my $cur_timestamp = &get_time();
3308         foreach my $foreign_server (@foreign_server_list) {
3309         
3310                 # do not add myself to known_server_db
3311                 if (&is_local($foreign_server)) { next; }
3312                 ######################################
3313         
3314             my $res = $known_server_db->add_dbentry( {table=>$known_server_tn, 
3315                     primkey=>['hostname'],
3316                     hostname=>$foreign_server,
3317                     macaddress=>"",
3318                     status=>'not_yet_registered',
3319                     hostkey=>"none",
3320                     loaded_modules => "none", 
3321                     timestamp=>$cur_timestamp,
3322                                 update_time=>'19700101000000',
3323                     } );
3324         }
3327 sub register_at_foreign_servers {   
3328     my ($kernel) = $_[KERNEL];
3330         # Update status and update-time of all si-server with expired update_time and 
3331         # block them for race conditional registration processes of other si-servers.
3332         my $act_time = &get_time();
3333         my $block_statement = "UPDATE $known_server_tn SET status='new_server',update_time='19700101000000' WHERE (CAST(update_time AS UNSIGNED))<$act_time ";
3334         my $block_res = $known_server_db->exec_statement($block_statement);
3336         # Fetch all si-server from db where update_time is younger than act_time
3337         my $fetch_statement = "SELECT * FROM $known_server_tn WHERE update_time='19700101000000'"; 
3338         my $fetch_res = $known_server_db->exec_statement($fetch_statement);
3340     # Detect already connected clients. Will be added to registration msg later. 
3341     my $client_sql = "SELECT * FROM $known_clients_tn"; 
3342     my $client_res = $known_clients_db->exec_statement($client_sql);
3344         # Send registration messag to all fetched si-server
3345     foreach my $hit (@$fetch_res) {
3346         my $hostname = @$hit[0];
3347         my $hostkey = &create_passwd;
3349         # Add already connected clients to registration message 
3350         my $myhash = &create_xml_hash('new_server', $server_address, $hostname);
3351         &add_content2xml_hash($myhash, 'key', $hostkey);
3352         map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res);
3354         # Add locally loaded gosa-si modules to registration message
3355         my $loaded_modules = {};
3356         while (my ($package, $pck_info) = each %$known_modules) {
3357                         next if ((!defined(@$pck_info[2])) || (!(ref (@$pck_info[2]) eq 'HASH')));
3358                         foreach my $act_module (keys(%{@$pck_info[2]})) {
3359                                 $loaded_modules->{$act_module} = ""; 
3360                         }
3361                 }
3362         map(&add_content2xml_hash($myhash, "loaded_modules", $_), keys(%$loaded_modules));
3364         # Add macaddress to registration message
3365         my ($host_ip, $host_port) = split(/:/, $hostname);
3366         my $local_ip = &get_local_ip_for_remote_ip($host_ip);
3367         my $network_interface= &get_interface_for_ip($local_ip);
3368         my $host_mac = &get_mac_for_interface($network_interface);
3369         &add_content2xml_hash($myhash, 'macaddress', $host_mac);
3370         
3371         # Build registration message and send it
3372         my $foreign_server_msg = &create_xml_string($myhash);
3373         my $error = &send_msg_to_target($foreign_server_msg, $hostname, $ServerPackages_key, "new_server", 0); 
3374     }
3377         # After n sec perform a check of all server registration processes
3378     $kernel->delay_set("control_server_registration", 2); 
3380         return;
3384 sub control_server_registration {
3385         my ($kernel) = $_[KERNEL];
3386         
3387         # Check if all registration processes succeed or not
3388         my $select_statement = "SELECT * FROM $known_server_tn WHERE status='new_server'"; 
3389         my $select_res = $known_server_db->exec_statement($select_statement);
3391         # If at least one registration process failed, maybe in case of a race condition
3392         # with a foreign registration process
3393         if (@$select_res > 0) 
3394         {
3395                 # Release block statement 'new_server' to make the server accessible
3396                 # for foreign registration processes
3397                 my $update_statement = "UPDATE $known_server_tn SET status='waiting' WHERE status='new_server'";        
3398                 my $update_res = $known_server_db->exec_statement($update_statement);
3400                 # Set a random delay to avoid the registration race condition
3401                 my $new_foreign_servers_register_delay = int(rand(4))+1;
3402                 $kernel->delay_set("register_at_foreign_servers", $new_foreign_servers_register_delay);
3403         }
3404         # If all registration processes succeed
3405         else
3406         {
3407                 $kernel->delay_set("register_at_foreign_servers", $foreign_servers_register_delay);
3408         }
3410         return;
3414 #==== MAIN = main ==============================================================
3415 #  parse commandline options
3416 Getopt::Long::Configure( "bundling" );
3417 GetOptions("h|help" => \&usage,
3418         "c|config=s" => \$cfg_file,
3419         "f|foreground" => \$foreground,
3420         "v|verbose+" => \$verbose,
3421         "no-arp+" => \$no_arp,
3422                 "d=s" => \$debug_parts,
3423            ) or (&usage("", 1)&&(exit(-1))); 
3425 #  read and set config parameters
3426 &check_cmdline_param ;
3427 &read_configfile($cfg_file, %cfg_defaults);
3428 &check_pid;
3430 $SIG{CHLD} = 'IGNORE';
3432 # forward error messages to logfile
3433 if( ! $foreground ) {
3434   open( STDIN,  '+>/dev/null' );
3435   open( STDOUT, '+>&STDIN'    );
3436   open( STDERR, '+>&STDIN'    );
3439 # Just fork, if we are not in foreground mode
3440 if( ! $foreground ) { 
3441     chdir '/'                 or die "Can't chdir to /: $!";
3442     $pid = fork;
3443     setsid                    or die "Can't start a new session: $!";
3444     umask 0;
3445 } else { 
3446     $pid = $$; 
3449 # Do something useful - put our PID into the pid_file
3450 if( 0 != $pid ) {
3451     open( LOCK_FILE, ">$pid_file" );
3452     print LOCK_FILE "$pid\n";
3453     close( LOCK_FILE );
3454     if( !$foreground ) { 
3455         exit( 0 ) 
3456     };
3459 # parse head url and revision from svn
3460 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
3461 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
3462 $server_headURL = defined $1 ? $1 : 'unknown' ;
3463 $server_revision = defined $2 ? $2 : 'unknown' ;
3464 if ($server_headURL =~ /\/tag\// || 
3465         $server_headURL =~ /\/branches\// ) {
3466     $server_status = "stable"; 
3467 } else {
3468     $server_status = "developmental" ;
3470 # Prepare log file and set permissions
3471 $root_uid = getpwnam('root');
3472 $adm_gid = getgrnam('adm');
3473 open(FH, ">>$log_file");
3474 close FH;
3475 chmod(0440, $log_file);
3476 chown($root_uid, $adm_gid, $log_file);
3477 chown($root_uid, $adm_gid, "/var/lib/gosa-si");
3479 daemon_log(" ", 1);
3480 daemon_log("$0 started!", 1);
3481 daemon_log("status: $server_status", 1);
3482 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1); 
3484 # Buildup data bases
3486     no strict "refs";
3488     if ($db_module eq "DBmysql") {
3489         # connect to incoming_db
3490         $incoming_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3492         # connect to gosa-si job queue
3493         $job_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3495         # connect to known_clients_db
3496         $known_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3498         # connect to foreign_clients_db
3499         $foreign_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3501         # connect to known_server_db
3502         $known_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3504         # connect to login_usr_db
3505         $login_users_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3507         # connect to fai_server_db 
3508         $fai_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3510         # connect to fai_release_db
3511         $fai_release_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3513         # connect to packages_list_db
3514         $packages_list_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3516         # connect to messaging_db
3517         $messaging_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3519     } elsif ($db_module eq "DBsqlite") {
3520         # connect to incoming_db
3521         unlink($incoming_file_name);
3522         $incoming_db = GOSA::DBsqlite->new($incoming_file_name);
3523         chmod(0640, $incoming_file_name);
3524         chown($root_uid, $adm_gid, $incoming_file_name);
3525         
3526         # connect to gosa-si job queue
3527         $job_db = GOSA::DBsqlite->new($job_queue_file_name);
3528         chmod(0640, $job_queue_file_name);
3529         chown($root_uid, $adm_gid, $job_queue_file_name);
3530         
3531         # connect to known_clients_db
3532         #unlink($known_clients_file_name);
3533         $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
3534         chmod(0640, $known_clients_file_name);
3535         chown($root_uid, $adm_gid, $known_clients_file_name);
3536         
3537         # connect to foreign_clients_db
3538         #unlink($foreign_clients_file_name);
3539         $foreign_clients_db = GOSA::DBsqlite->new($foreign_clients_file_name);
3540         chmod(0640, $foreign_clients_file_name);
3541         chown($root_uid, $adm_gid, $foreign_clients_file_name);
3542         
3543         # connect to known_server_db
3544         unlink($known_server_file_name);   # do not delete, gosa-si-server should be forced to check config file and dns at each start
3545         $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
3546         chmod(0640, $known_server_file_name);
3547         chown($root_uid, $adm_gid, $known_server_file_name);
3548         
3549         # connect to login_usr_db
3550         #unlink($login_users_file_name);
3551         $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
3552         chmod(0640, $login_users_file_name);
3553         chown($root_uid, $adm_gid, $login_users_file_name);
3554         
3555         # connect to fai_server_db
3556         unlink($fai_server_file_name);
3557         $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
3558         chmod(0640, $fai_server_file_name);
3559         chown($root_uid, $adm_gid, $fai_server_file_name);
3560         
3561         # connect to fai_release_db
3562         unlink($fai_release_file_name);
3563         $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
3564         chmod(0640, $fai_release_file_name);
3565         chown($root_uid, $adm_gid, $fai_release_file_name);
3566         
3567         # connect to packages_list_db
3568         unlink($packages_list_under_construction);
3569         $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
3570         chmod(0640, $packages_list_file_name);
3571         chown($root_uid, $adm_gid, $packages_list_file_name);
3572         
3573         # connect to messaging_db
3574         #unlink($messaging_file_name);
3575         $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
3576         chmod(0640, $messaging_file_name);
3577         chown($root_uid, $adm_gid, $messaging_file_name);
3578     }
3581 # Creating tables
3582 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
3583 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
3584 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
3585 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
3586 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
3587 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
3588 $foreign_clients_db->create_table($foreign_clients_tn, \@foreign_clients_col_names);
3589 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
3590 $incoming_db->create_table($incoming_tn, \@incoming_col_names);
3591 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
3593 # create xml object used for en/decrypting
3594 $xml = new XML::Simple();
3596 # Import all modules
3597 &import_modules;
3599 # Check wether all modules are gosa-si valid passwd check
3600 &password_check;
3602 # Check DNS and config file for server registration
3603 if ($serverPackages_enabled eq "true") { &prepare_server_registration; }
3605 # Create functions hash
3606 while (my ($module, @mod_info) = each %$known_modules) 
3608         while (my ($plugin, $functions) = each %{$mod_info[0][2]})
3609         {
3610                 while (my ($function, $nothing) = each %$functions )
3611                 {
3612                         $known_functions->{$function} = $nothing;
3613                 }
3614         }
3617 # Prepare for using Opsi 
3618 if ($opsi_enabled eq "true") {
3619     use JSON::RPC::Client;
3620     use XML::Quote qw(:all);
3621     $opsi_url= "https://".$opsi_admin.":".$opsi_password."@".$opsi_server.":4447/rpc";
3622     $opsi_client = new JSON::RPC::Client;
3626 POE::Component::Server::TCP->new(
3627         Alias => "TCP_SERVER",
3628         Port => $server_port,
3629         ClientInput => sub {
3630                 my ($kernel, $input, $heap, $session) = @_[KERNEL, ARG0, HEAP, SESSION];
3631         my $session_id = $session->ID;
3632                 if ($input =~ /;([\d\.]+:[\d]+)$/) 
3633                 {
3634                         &daemon_log("$session_id DEBUG: incoming message from '$1'", 11);
3635                 }
3636                 else
3637                 {
3638                         my $remote_ip = $heap->{'remote_ip'};
3639                         &daemon_log("$session_id DEBUG: incoming message from '$remote_ip'", 11);
3640                 }
3641                 push(@msgs_to_decrypt, $input);
3642                 $kernel->yield("msg_to_decrypt");
3643         },
3644         InlineStates => {
3645                 msg_to_decrypt => \&msg_to_decrypt,
3646                 next_task => \&next_task,
3647                 task_result => \&handle_task_result,
3648                 task_done   => \&handle_task_done,
3649                 task_debug  => \&handle_task_debug,
3650                 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3651         }
3652 );
3654 daemon_log("0 INFO: start socket for incoming xml messages at port '$server_port' ", 1);
3656 # create session for repeatedly checking the job queue for jobs
3657 POE::Session->create(
3658         inline_states => {
3659                 _start => \&session_start,
3660         register_at_foreign_servers => \&register_at_foreign_servers,
3661                 control_server_registration => \&control_server_registration,
3662         sig_handler => \&sig_handler,
3663         next_task => \&next_task,
3664         task_result => \&handle_task_result,
3665         task_done   => \&handle_task_done,
3666         task_debug  => \&handle_task_debug,
3667         watch_for_next_tasks => \&watch_for_next_tasks,
3668         watch_for_new_messages => \&watch_for_new_messages,
3669         watch_for_delivery_messages => \&watch_for_delivery_messages,
3670         watch_for_done_messages => \&watch_for_done_messages,
3671                 watch_for_new_jobs => \&watch_for_new_jobs,
3672         watch_for_modified_jobs => \&watch_for_modified_jobs,
3673         watch_for_done_jobs => \&watch_for_done_jobs,
3674         watch_for_opsi_jobs => \&watch_for_opsi_jobs,
3675         watch_for_old_known_clients => \&watch_for_old_known_clients,
3676         create_packages_list_db => \&run_create_packages_list_db,
3677         create_fai_server_db => \&run_create_fai_server_db,
3678         create_fai_release_db => \&run_create_fai_release_db,
3679                 recreate_packages_db => \&run_recreate_packages_db,
3680         session_run_result => \&session_run_result,
3681         session_run_debug => \&session_run_debug,
3682         session_run_done => \&session_run_done,
3683         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3684         }
3685 );
3688 POE::Kernel->run();
3689 exit;