Code

3bce9a9b76daa4511f3a4e271d9653bed94dc5ee
[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     $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     "address"      => [\$foreign_server_string, ""],
273     "dns-lookup"            => [\$dns_lookup, "true"],
274     "domain"  => [\$server_domain, ""],
275     "key"     => [\$ServerPackages_key, "none"],
276     "key-lifetime" => [\$foreign_servers_register_delay, 120],
277     "job-synchronization-enabled" => [\$job_synchronization, "true"],
278     "synchronization-loop" => [\$modified_jobs_loop_delay, 5],
279     },
280 "ArpHandler" => {
281     "enabled"   => [\$arp_enabled, "true"],
282     "interface" => [\$arp_interface, "all"],
283         },
284 "Opsi" => {
285     "enabled"  => [\$opsi_enabled, "false"], 
286     "server"   => [\$opsi_server, "localhost"],
287     "admin"    => [\$opsi_admin, "opsi-admin"],
288     "password" => [\$opsi_password, "secret"],
289    },
291 );
294 #===  FUNCTION  ================================================================
295 #         NAME:  usage
296 #   PARAMETERS:  nothing
297 #      RETURNS:  nothing
298 #  DESCRIPTION:  print out usage text to STDERR
299 #===============================================================================
300 sub usage {
301     print STDERR << "EOF" ;
302 usage: $prg [-hvf] [-c config] [-d number]
304            -h        : this (help) message
305            -c <file> : config file
306            -f        : foreground, process will not be forked to background
307            -v        : be verbose (multiple to increase verbosity)
308                               'v': error logs
309                             'vvv': warning plus error logs                                              
310                           'vvvvv': info plus warning logs
311                         'vvvvvvv': debug plus info logs
312            -no-arp   : starts $prg without connection to arp module
313            -d <int>  : if verbose level is higher than 7x 'v' specified parts can be debugged
314                            1 : receiving messages
315                            2 : sending messages
316                            4 : encrypting/decrypting messages
317                            8 : verification if a message complies gosa-si requirements
318                           16 : message processing
319                           32 : ldap connectivity
320                           64 : database status and connectivity
321                          128 : main process 
322  
323 EOF
324     print "\n" ;
328 #===  FUNCTION  ================================================================
329 #         NAME:  logging
330 #   PARAMETERS:  level - string - default 'info'
331 #                msg - string -
332 #                facility - string - default 'LOG_DAEMON'
333 #      RETURNS:  nothing
334 #  DESCRIPTION:  function for logging
335 #===============================================================================
336 sub daemon_log {
337     my( $msg, $level ) = @_;
338     if (not defined $msg) { return }
339     if (not defined $level) { $level = 1 }
340         my $to_be_logged = 0;
342         # Write log line if line level is lower than verbosity given in commandline
343         if ($level <= $verbose) 
344         { 
345                 $to_be_logged = 1 ;
346         }
348         # Write if debug flag is set and bitstring matches
349         if ($debug_parts > 0)
350         {
351                 my $tmp_level = ($level - 10 >= 0) ? $level - 10 : 0 ;
352                 my $tmp_level_bitstring = unpack("B32", pack("N", $tmp_level));
353                 if (int($debug_parts_bitstring & $tmp_level_bitstring)) 
354                 {
355                         $to_be_logged = 1;
356                 }
357         }
359         if ($to_be_logged) 
360         {
361                 if(defined $log_file){
362                         my $open_log_fh = sysopen(LOG_HANDLE, $log_file, O_WRONLY | O_CREAT | O_APPEND , 0440);
363                         if(not $open_log_fh) {
364                                 print STDERR "cannot open $log_file: $!";
365                                 return;
366                         }
367                         # Check owner and group of log_file and update settings if necessary
368                         my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = stat($log_file);
369                         if((not $uid eq $root_uid) || (not $gid eq $adm_gid)) {
370                                 chown($root_uid, $adm_gid, $log_file);
371                         }
373                         # Prepare time string for log message
374                         my ($seconds,$minutes,$hours,$monthday,$month,$year,$weekday,$yearday,$sommertime) = localtime(time);
375                         $hours = $hours < 10 ? $hours = "0".$hours : $hours;
376                         $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
377                         $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
378                         $month = $monthnames[$month];
379                         $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
380                         $year+=1900;
382                         
383                         # Build log message and write it to log file and commandline
384                         chomp($msg);
385                         my $log_msg = "$month $monthday $hours:$minutes:$seconds $prg $msg\n";
386                         flock(LOG_HANDLE, LOCK_EX);
387                         seek(LOG_HANDLE, 0, 2);
388                         print LOG_HANDLE $log_msg;
389                         flock(LOG_HANDLE, LOCK_UN);
390                         if( $foreground ) 
391                         { 
392                                 print STDERR $log_msg;
393                         }
394                         close( LOG_HANDLE );
395                 }
396         }
400 #===  FUNCTION  ================================================================
401 #         NAME:  check_cmdline_param
402 #   PARAMETERS:  nothing
403 #      RETURNS:  nothing
404 #  DESCRIPTION:  validates commandline parameter
405 #===============================================================================
406 sub check_cmdline_param () {
407     my $err_counter = 0;
409         # Check configuration file
410         if(not defined($cfg_file)) {
411                 $cfg_file = "/etc/gosa-si/server.conf";
412                 if(! -r $cfg_file) {
413                         print STDERR "Please specify a config file.\n";
414                         $err_counter++;
415                 }
416     }
418         # Prepare identification which gosa-si parts should be debugged and which not
419         if (defined $debug_parts) 
420         {
421                 if ($debug_parts =~ /^\d+$/)
422                 {
423                         $debug_parts_bitstring = unpack("B32", pack("N", $debug_parts));
424                 }
425                 else
426                 {
427                         print STDERR "Value '$debug_parts' invalid for option d (number expected)\n";
428                         $err_counter++;
429                 }
430         }
432         # Exit if an error occour
433     if( $err_counter > 0 ) 
434         {
435         &usage( "", 1 );
436         exit( -1 );
437     }
441 #===  FUNCTION  ================================================================
442 #         NAME:  check_pid
443 #   PARAMETERS:  nothing
444 #      RETURNS:  nothing
445 #  DESCRIPTION:  handels pid processing
446 #===============================================================================
447 sub check_pid {
448     $pid = -1;
449     # Check, if we are already running
450     if( open(LOCK_FILE, "<$pid_file") ) {
451         $pid = <LOCK_FILE>;
452         if( defined $pid ) {
453             chomp( $pid );
454             if( -f "/proc/$pid/stat" ) {
455                 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
456                 if( $stat ) {
457                                         print STDERR "\nERROR: Already running!\n";
458                     close( LOCK_FILE );
459                     exit -1;
460                 }
461             }
462         }
463         close( LOCK_FILE );
464         unlink( $pid_file );
465     }
467     # create a syslog msg if it is not to possible to open PID file
468     if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
469         my($msg) = "Couldn't obtain lockfile '$pid_file' ";
470         if (open(LOCK_FILE, '<', $pid_file)
471                 && ($pid = <LOCK_FILE>))
472         {
473             chomp($pid);
474             $msg .= "(PID $pid)\n";
475         } else {
476             $msg .= "(unable to read PID)\n";
477         }
478         if( ! ($foreground) ) {
479             openlog( $0, "cons,pid", "daemon" );
480             syslog( "warning", $msg );
481             closelog();
482         }
483         else {
484             print( STDERR " $msg " );
485         }
486         exit( -1 );
487     }
490 #===  FUNCTION  ================================================================
491 #         NAME:  import_modules
492 #   PARAMETERS:  module_path - string - abs. path to the directory the modules 
493 #                are stored
494 #      RETURNS:  nothing
495 #  DESCRIPTION:  each file in module_path which ends with '.pm' and activation 
496 #                state is on is imported by "require 'file';"
497 #===============================================================================
498 sub import_modules {
499     if (not -e $modules_path) {
500         daemon_log("0 ERROR: cannot find directory or directory is not readable: $modules_path", 1);   
501     }
503     opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
505     while (defined (my $file = readdir (DIR))) {
506         if (not $file =~ /(\S*?).pm$/) {
507             next;
508         }
509                 my $mod_name = $1;
511         # ArpHandler switch
512         if( $file =~ /ArpHandler.pm/ ) {
513             if( $arp_enabled eq "false" ) { next; }
514         }
515         
516         eval { require $file; };
517         if ($@) {
518             daemon_log("0 ERROR: gosa-si-server could not load module $file", 1);
519             daemon_log("$@", 1);
520             exit;
521                 } else {
522                         my $info = eval($mod_name.'::get_module_info()');
523                         # Only load module if get_module_info() returns a non-null object
524                         if( $info ) {
525                                 my ($input_address, $input_key, $event_hash) = @{$info};
526                                 $known_modules->{$mod_name} = $info;
527                                 daemon_log("0 INFO: module $mod_name loaded", 5);
528                         }
529                 }
530     }   
531     close (DIR);
534 #===  FUNCTION  ================================================================
535 #         NAME:  password_check
536 #   PARAMETERS:  nothing
537 #      RETURNS:  nothing
538 #  DESCRIPTION:  escalates an critical error if two modules exist which are avaialable by 
539 #                the same password
540 #===============================================================================
541 sub password_check {
542     my $passwd_hash = {};
543     while (my ($mod_name, $mod_info) = each %$known_modules) {
544         my $mod_passwd = @$mod_info[1];
545         if (not defined $mod_passwd) { next; }
546         if (not exists $passwd_hash->{$mod_passwd}) {
547             $passwd_hash->{$mod_passwd} = $mod_name;
549         # escalates critical error
550         } else {
551             &daemon_log("0 ERROR: two loaded modules do have the same password. Please modify the 'key'-parameter in config file");
552             &daemon_log("0 ERROR: module='$mod_name' and module='".$passwd_hash->{$mod_passwd}."'");
553             exit( -1 );
554         }
555     }
560 #===  FUNCTION  ================================================================
561 #         NAME:  sig_int_handler
562 #   PARAMETERS:  signal - string - signal arose from system
563 #      RETURNS:  nothing
564 #  DESCRIPTION:  handels tasks to be done befor signal becomes active
565 #===============================================================================
566 sub sig_int_handler {
567     my ($signal) = @_;
569 #       if (defined($ldap_handle)) {
570 #               $ldap_handle->disconnect;
571 #       }
572     # TODO alle verbliebenden ldap verbindungen aus allen heaps beenden
573     
575     daemon_log("shutting down gosa-si-server", 1);
576     system("kill `ps -C gosa-si-server -o pid=`");
578 $SIG{INT} = \&sig_int_handler;
581 sub check_key_and_xml_validity {
582     my ($crypted_msg, $module_key, $session_id) = @_;
583     my $msg;
584     my $msg_hash;
585     my $error_string;
586     eval{
587         $msg = &decrypt_msg($crypted_msg, $module_key);
589         if ($msg =~ /<xml>/i){
590             $msg =~ s/\s+/ /g;  # just for better daemon_log
591             daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 18);
592             $msg_hash = $xml->XMLin($msg, ForceArray=>1);
594             ##############
595             # check header
596             if( not exists $msg_hash->{'header'} ) { die "no header specified"; }
597             my $header_l = $msg_hash->{'header'};
598             if( (1 > @{$header_l}) || ( ( 'HASH' eq ref @{$header_l}[0]) && (1 > keys %{@{$header_l}[0]}) ) ) { die 'empty header tag'; }
599             if( 1 < @{$header_l} ) { die 'more than one header specified'; }
600             my $header = @{$header_l}[0];
601             if( 0 == length $header) { die 'empty string in header tag'; }
603             ##############
604             # check source
605             if( not exists $msg_hash->{'source'} ) { die "no source specified"; }
606             my $source_l = $msg_hash->{'source'};
607             if( (1 > @{$source_l}) || ( ( 'HASH' eq ref @{$source_l}[0]) && (1 > keys %{@{$source_l}[0]}) ) ) { die 'empty source tag'; }
608             if( 1 < @{$source_l} ) { die 'more than one source specified'; }
609             my $source = @{$source_l}[0];
610             if( 0 == length $source) { die 'source error'; }
612             ##############
613             # check target
614             if( not exists $msg_hash->{'target'} ) { die "no target specified"; }
615             my $target_l = $msg_hash->{'target'};
616             if( (1 > @{$target_l}) || ( ('HASH' eq ref @{$target_l}[0]) && (1 > keys %{@{$target_l}[0]}) ) ) { die 'empty target tag'; }
617         }
618     };
619     if($@) {
620         daemon_log("$session_id ERROR: do not understand the message: $@", 1);
621         $msg = undef;
622         $msg_hash = undef;
623     }
625     return ($msg, $msg_hash);
629 sub check_outgoing_xml_validity {
630     my ($msg, $session_id) = @_;
632     my $msg_hash;
633     eval{
634         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
636         ##############
637         # check header
638         my $header_l = $msg_hash->{'header'};
639         if( 1 != @{$header_l} ) {
640             die 'no or more than one headers specified';
641         }
642         my $header = @{$header_l}[0];
643         if( 0 == length $header) {
644             die 'header has length 0';
645         }
647         ##############
648         # check source
649         my $source_l = $msg_hash->{'source'};
650         if( 1 != @{$source_l} ) {
651             die 'no or more than 1 sources specified';
652         }
653         my $source = @{$source_l}[0];
654         if( 0 == length $source) {
655             die 'source has length 0';
656         }
658                                 # Check if source contains hostname instead of ip address
659                                 if($source =~ /^[a-z][a-z0-9\.]+:\d+$/i) {
660                                                 my ($hostname,$port) = split(/:/, $source);
661                                                 my $ip_address = inet_ntoa(scalar gethostbyname($hostname));
662                                                 if(defined($ip_address) && $ip_address =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/ && $port =~ /^\d+$/) {
663                                                         # Write ip address to $source variable
664                                                         $source = "$ip_address:$port";
665                                                 }
666                                 }
667         unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
668                 $source =~ /^GOSA$/i) {
669             die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
670         }
671         
672         ##############
673         # check target  
674         my $target_l = $msg_hash->{'target'};
675         if( 0 == @{$target_l} ) {
676             die "no targets specified";
677         }
678         foreach my $target (@$target_l) {
679             if( 0 == length $target) {
680                 die "target has length 0";
681             }
682             unless( $target =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
683                     $target =~ /^GOSA$/i ||
684                     $target =~ /^\*$/ ||
685                     $target =~ /KNOWN_SERVER/i ||
686                     $target =~ /JOBDB/i ||
687                     $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 ){
688                 die "target '$target' is not a complete ip-address with port or a valid target name or a mac-address";
689             }
690         }
691     };
692     if($@) {
693         daemon_log("$session_id ERROR: outgoing msg is not gosa-si envelope conform: $@", 1);
694         daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 1);
695         $msg_hash = undef;
696     }
698     return ($msg_hash);
702 sub input_from_known_server {
703     my ($input, $remote_ip, $session_id) = @_ ;  
704     my ($msg, $msg_hash, $module);
706     my $sql_statement= "SELECT * FROM known_server";
707     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
709     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
710         my $host_name = $hit->{hostname};
711         if( not $host_name =~ "^$remote_ip") {
712             next;
713         }
714         my $host_key = $hit->{hostkey};
715         daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 14);
716         daemon_log("$session_id DEBUG: input_from_known_server: host_key: $host_key", 14);
718         # check if module can open msg envelope with module key
719         my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
720         if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
721             daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 14);
722             daemon_log("$@", 14);
723             next;
724         }
725         else {
726             $msg = $tmp_msg;
727             $msg_hash = $tmp_msg_hash;
728             $module = "ServerPackages";
729             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 14);
730             last;
731         }
732     }
734     if( (!$msg) || (!$msg_hash) || (!$module) ) {
735         daemon_log("$session_id DEBUG: Incoming message is not from a known server", 14);
736     }
737   
738     return ($msg, $msg_hash, $module);
742 sub input_from_known_client {
743     my ($input, $remote_ip, $session_id) = @_ ;  
744     my ($msg, $msg_hash, $module);
746     my $sql_statement= "SELECT * FROM known_clients";
747     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
748     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
749         my $host_name = $hit->{hostname};
750         if( not $host_name =~ /^$remote_ip/) {
751                 next;
752                 }
753         my $host_key = $hit->{hostkey};
754         &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 14);
755         &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 14);
757         # check if module can open msg envelope with module key
758         ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
760         if( (!$msg) || (!$msg_hash) ) {
761             &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 14);
762             next;
763         }
764         else {
765             $module = "ClientPackages";
766             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 14);
767             last;
768         }
769     }
771     if( (!$msg) || (!$msg_hash) || (!$module) ) {
772         &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 14);
773     }
775     return ($msg, $msg_hash, $module);
779 sub input_from_unknown_host {
780         no strict "refs";
781         my ($input, $session_id) = @_ ;
782         my ($msg, $msg_hash, $module);
783         my $error_string;
785         my %act_modules = %$known_modules;
787         while( my ($mod, $info) = each(%act_modules)) {
789                 # check a key exists for this module
790                 my $module_key = ${$mod."_key"};
791                 if( not defined $module_key ) {
792                         if( $mod eq 'ArpHandler' ) {
793                                 next;
794                         }
795                         daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
796                         next;
797                 }
798                 daemon_log("$session_id DEBUG: $mod: $module_key", 14);
800                 # check if module can open msg envelope with module key
801                 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
802                 if( (not defined $msg) || (not defined $msg_hash) ) {
803                         next;
804                 } else {
805                         $module = $mod;
806             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 18);
807                         last;
808                 }
809         }
811         if( (!$msg) || (!$msg_hash) || (!$module)) {
812                 daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 14);
813         }
815         return ($msg, $msg_hash, $module);
819 sub create_ciphering {
820     my ($passwd) = @_;
821         if((!defined($passwd)) || length($passwd)==0) {
822                 $passwd = "";
823         }
824     $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
825     my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
826     my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
827     $my_cipher->set_iv($iv);
828     return $my_cipher;
832 sub encrypt_msg {
833     my ($msg, $key) = @_;
834     my $my_cipher = &create_ciphering($key);
835     my $len;
836     {
837             use bytes;
838             $len= 16-length($msg)%16;
839     }
840     $msg = "\0"x($len).$msg;
841     $msg = $my_cipher->encrypt($msg);
842     chomp($msg = &encode_base64($msg));
843     # there are no newlines allowed inside msg
844     $msg=~ s/\n//g;
845     return $msg;
849 sub decrypt_msg {
851     my ($msg, $key) = @_ ;
852     $msg = &decode_base64($msg);
853     my $my_cipher = &create_ciphering($key);
854     $msg = $my_cipher->decrypt($msg); 
855     $msg =~ s/\0*//g;
856     return $msg;
860 sub get_encrypt_key {
861     my ($target) = @_ ;
862     my $encrypt_key;
863     my $error = 0;
865     # target can be in known_server
866     if( not defined $encrypt_key ) {
867         my $sql_statement= "SELECT * FROM known_server WHERE hostname='$target'";
868         my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
869         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
870             my $host_name = $hit->{hostname};
871             if( $host_name ne $target ) {
872                 next;
873             }
874             $encrypt_key = $hit->{hostkey};
875             last;
876         }
877     }
879     # target can be in known_client
880     if( not defined $encrypt_key ) {
881         my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$target'";
882         my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
883         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
884             my $host_name = $hit->{hostname};
885             if( $host_name ne $target ) {
886                 next;
887             }
888             $encrypt_key = $hit->{hostkey};
889             last;
890         }
891     }
893     return $encrypt_key;
897 #===  FUNCTION  ================================================================
898 #         NAME:  open_socket
899 #   PARAMETERS:  PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
900 #                [PeerPort] string necessary if port not appended by PeerAddr
901 #      RETURNS:  socket IO::Socket::INET
902 #  DESCRIPTION:  open a socket to PeerAddr
903 #===============================================================================
904 sub open_socket {
905     my ($PeerAddr, $PeerPort) = @_ ;
906     if(defined($PeerPort)){
907         $PeerAddr = $PeerAddr.":".$PeerPort;
908     }
909     my $socket;
910     $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
911             Porto => "tcp",
912             Type => SOCK_STREAM,
913             Timeout => 5,
914             );
915     if(not defined $socket) {
916         return;
917     }
918 #    &daemon_log("DEBUG: open_socket: $PeerAddr", 7);
919     return $socket;
923 sub send_msg_to_target {
924     my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
925     my $error = 0;
926     my $header;
927     my $timestamp = &get_time();
928     my $new_status;
929     my $act_status;
930     my ($sql_statement, $res);
931   
932     if( $msg_header ) {
933         $header = "'$msg_header'-";
934     } else {
935         $header = "";
936     }
938         # Memorize own source address
939         my $own_source_address = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
940         $own_source_address .= ":".$server_port;
942         # Patch 0.0.0.0 source to real address
943         $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$own_source_address<\/source>/s;
944         # Patch GOSA source to real address and add forward_to_gosa tag
945         $msg =~ s/<source>GOSA<\/source>/<source>$own_source_address<\/source> <forward_to_gosa>$own_source_address,$session_id<\/forward_to_gosa>/ ;
947     # encrypt xml msg
948     my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
950     # opensocket
951     my $socket = &open_socket($address);
952     if( !$socket ) {
953         daemon_log("$session_id ERROR: Cannot open socket to host '$address'. Message processing aborted!", 1);
954         $error++;
955     }
956     
957     if( $error == 0 ) {
958         # send xml msg
959         print $socket $crypted_msg.";$own_source_address\n";
960         daemon_log("$session_id INFO: send ".$header."msg to $address", 5);
961         daemon_log("$session_id DEBUG: message:\n$msg", 12);
962         
963     }
965     # close socket in any case
966     if( $socket ) {
967         close $socket;
968     }
970     if( $error > 0 ) { $new_status = "down"; }
971     else { $new_status = $msg_header; }
974     # known_clients
975     $sql_statement = "SELECT * FROM $known_clients_tn WHERE hostname='$address'";
976     $res = $known_clients_db->select_dbentry($sql_statement);
977     if( keys(%$res) == 1) {
978         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
979         if ($act_status eq "down" && $new_status eq "down") {
980             $sql_statement = "DELETE FROM known_clients WHERE hostname='$address'";
981             $res = $known_clients_db->del_dbentry($sql_statement);
982             daemon_log("$session_id WARNING: failed 2x to send msg to host '$address', delete host from known_clients", 3);
983         } else { 
984             $sql_statement = "UPDATE known_clients SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
985             $res = $known_clients_db->update_dbentry($sql_statement);
986             if($new_status eq "down"){
987                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
988             } else {
989                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
990             }
991         }
992     }
994     # known_server
995     $sql_statement = "SELECT * FROM $known_server_tn WHERE hostname='$address'";
996     $res = $known_server_db->select_dbentry($sql_statement);
997     if( keys(%$res) == 1) {
998         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
999         if ($act_status eq "down" && $new_status eq "down") {
1000             $sql_statement = "DELETE FROM known_server WHERE hostname='$address'";
1001             $res = $known_server_db->del_dbentry($sql_statement);
1002             daemon_log("$session_id WARNING: failed 2x to send a message to host '$address', delete host from known_server", 3);
1003         } 
1004         else { 
1005             $sql_statement = "UPDATE known_server SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
1006             $res = $known_server_db->update_dbentry($sql_statement);
1007             if($new_status eq "down"){
1008                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
1009             } else {
1010                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
1011             }
1012         }
1013     }
1014     return $error; 
1018 sub update_jobdb_status_for_send_msgs {
1019     my ($session_id, $answer, $error) = @_;
1020     if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
1021                 &daemon_log("$session_id DEBUG: try to update job status", 138); 
1022         my $jobdb_id = $1;
1023     
1024         $answer =~ /<header>(.*)<\/header>/;
1025         my $job_header = $1;
1027         $answer =~ /<target>(.*)<\/target>/;
1028         my $job_target = $1;
1029             
1030         # Sending msg failed
1031         if( $error ) {
1033             # Set jobs to done, jobs do not need to deliver their message in any case
1034             if (($job_header eq "trigger_action_localboot")
1035                     ||($job_header eq "trigger_action_lock")
1036                     ||($job_header eq "trigger_action_halt") 
1037                     ) {
1038                 my $sql_statement = "UPDATE $job_queue_tn SET status='done' WHERE id=$jobdb_id";
1039                 my $res = $job_db->update_dbentry($sql_statement);
1040                 
1041             # Reactivate jobs, jobs need to deliver their message
1042             } elsif (($job_header eq "trigger_action_activate")
1043                     ||($job_header eq "trigger_action_update")
1044                     ||($job_header eq "trigger_action_reinstall") 
1045                     ||($job_header eq "trigger_activate_new")
1046                     ) {
1047                 &reactivate_job_with_delay($session_id, $job_target, $job_header, 30 );
1049             # For all other messages
1050             } else {
1051                 my $sql_statement = "UPDATE $job_queue_tn ".
1052                     "SET status='error', result='can not deliver msg, please consult log file' ".
1053                     "WHERE id=$jobdb_id";
1054                 my $res = $job_db->update_dbentry($sql_statement);
1055             }
1057         # Sending msg was successful
1058         } else {
1059             # Set jobs localboot, lock, activate, halt, reboot and wake to done
1060             # jobs reinstall, update, inst_update do themself setting to done
1061             if (($job_header eq "trigger_action_localboot")
1062                     ||($job_header eq "trigger_action_lock")
1063                     ||($job_header eq "trigger_action_activate")
1064                     ||($job_header eq "trigger_action_halt") 
1065                     ||($job_header eq "trigger_action_reboot")
1066                     ||($job_header eq "trigger_action_wake")
1067                     ||($job_header eq "trigger_wake")
1068                     ) {
1070                 my $sql_statement = "UPDATE $job_queue_tn ".
1071                     "SET status='done' ".
1072                     "WHERE id=$jobdb_id AND status='processed'";
1073                 my $res = $job_db->update_dbentry($sql_statement);
1074             } else { 
1075                 &daemon_log("$session_id DEBUG: sending message succeed but cannot update job status.", 138); 
1076             } 
1077         } 
1078     } else { 
1079         &daemon_log("$session_id DEBUG: cannot update job status, msg has no jobdb_id-tag.", 138); 
1080     }
1083 sub reactivate_job_with_delay {
1084     my ($session_id, $target, $header, $delay) = @_ ;
1085     # 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
1086     
1087     if (not defined $delay) { $delay = 30 } ;
1088     my $delay_timestamp = &calc_timestamp(&get_time(), "plus", $delay);
1090     my $sql = "UPDATE $job_queue_tn Set timestamp='$delay_timestamp', status='waiting' WHERE (macaddress LIKE 'target' AND headertag='$header')"; 
1091     my $res = $job_db->update_dbentry($sql);
1092     daemon_log("$session_id INFO: '$header'-job will be reactivated at '$delay_timestamp' ".
1093             "cause client '$target' is currently not available", 5);
1094     return;
1098 sub sig_handler {
1099         my ($kernel, $signal) = @_[KERNEL, ARG0] ;
1100         daemon_log("0 INFO got signal '$signal'", 1); 
1101         $kernel->sig_handled();
1102         return;
1106 sub msg_to_decrypt {
1107         my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
1108         my $session_id = $session->ID;
1109         my ($msg, $msg_hash, $module);
1110         my $error = 0;
1112         # fetch new msg out of @msgs_to_decrypt
1113         my $tmp_next_msg = shift @msgs_to_decrypt;
1114     my ($next_msg, $msg_source) = split(/;/, $tmp_next_msg);
1116         # msg is from a new client or gosa
1117         ($msg, $msg_hash, $module) = &input_from_unknown_host($next_msg, $session_id);
1119         # msg is from a gosa-si-server
1120         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1121                 if (not defined $msg_source) 
1122                 {
1123                         # Only needed, to be compatible with older gosa-si-server versions
1124                         ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $heap->{'remote_ip'}, $session_id);
1125                 }
1126                 else
1127                 {
1128                         ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $msg_source, $session_id);
1129                 }
1130         }
1131         # msg is from a gosa-si-client
1132         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1133                 ($msg, $msg_hash, $module) = &input_from_known_client($next_msg, $msg_source, $session_id);
1134         }
1135         # an error occurred
1136         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1137                 # If an incoming msg could not be decrypted (maybe a wrong key), decide if msg comes from a client 
1138                 # or a server.  In case of a client, send a ping. If the client could not understand a msg from its 
1139                 # server the client cause a re-registering process. In case of a server, decrease update_time in kown_server_db
1140                 # and trigger a re-registering process for servers
1141                 if (defined $msg_source && $msg_source =~ /:$server_port$/)
1142                 {
1143                         daemon_log("$session_id WARNING: Cannot understand incoming msg from server '$msg_source'. Cause re-registration process for servers.", 3);
1144                         my $update_statement = "UPDATE $known_server_tn SET update_time='19700101000000' WHERE hostname='$msg_source'"; 
1145                         daemon_log("$session_id DEBUG: $update_statement", 7);
1146                         my $upadte_res = $known_server_db->exec_statement($update_statement);
1147                         $kernel->yield("register_at_foreign_servers");
1148                 }
1149                 elsif (defined $msg_source)
1150                 {
1151                         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);
1152                         #my $remote_ip = $heap->{'remote_ip'};
1153                         #my $remote_port = $heap->{'remote_port'};
1154                         my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source><target>$msg_source</target></xml>";
1155                         my ($test_error, $test_error_string) = &send_msg_to_target($ping_msg, "$msg_source", "dummy-key", "gosa_ping", $session_id);
1156                         daemon_log("$session_id WARNING: sending msg to cause re-registering: $ping_msg", 3);
1157                 }
1158                 else
1159                 {
1160                         my $foreign_host = defined $msg_source ? $msg_source : $heap->{'remote_ip'};
1161                         daemon_log("$session_id ERROR: incoming message from host '$foreign_host' cannot be understood. Processing aborted: $tmp_next_msg", 1);
1162                 }
1164                 $error++
1165         }
1168         my $header;
1169         my $target;
1170         my $source;
1171         my $done = 0;
1172         my $sql;
1173         my $res;
1175         # check whether this message should be processed here
1176         if ($error == 0) {
1177                 $header = @{$msg_hash->{'header'}}[0];
1178                 $target = @{$msg_hash->{'target'}}[0];
1179                 $source = @{$msg_hash->{'source'}}[0];
1180                 my $not_found_in_known_clients_db = 0;
1181                 my $not_found_in_known_server_db = 0;
1182                 my $not_found_in_foreign_clients_db = 0;
1183                 my $local_address;
1184                 my $local_mac;
1185                 my ($target_ip, $target_port) = split(':', $target);
1187                 # Determine the local ip address if target is an ip address
1188                 if ($target =~ /^\d+\.\d+\.\d+\.\d+:\d+$/) {
1189                         $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1190                 } else {
1191                         $local_address = $server_address;
1192                 }
1194                 # Determine the local mac address if target is a mac address
1195                 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) {
1196                         my $loc_ip = &get_local_ip_for_remote_ip($heap->{'remote_ip'});
1197                         my $network_interface= &get_interface_for_ip($loc_ip);
1198                         $local_mac = &get_mac_for_interface($network_interface);
1199                 } else {
1200                         $local_mac = $server_mac_address;
1201                 }
1203                 # target and source is equal to GOSA -> process here
1204                 if (not $done) {
1205                         if ($target eq "GOSA" && $source eq "GOSA") {
1206                                 $done = 1;                    
1207                                 &daemon_log("$session_id DEBUG: target and source is 'GOSA' -> process '$header' here", 11);
1208                         }
1209                 }
1211                 # target is own address without forward_to_gosa-tag -> process here
1212                 if (not $done) {
1213                         #if ((($target eq $local_address) || ($target eq $local_mac) ) && (not exists $msg_hash->{'forward_to_gosa'})) {
1214                         if (($target eq $local_address) && (not exists $msg_hash->{'forward_to_gosa'})) {
1215                                 $done = 1;
1216                                 if ($source eq "GOSA") {
1217                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1218                                 }
1219                                 &daemon_log("$session_id DEBUG: target is own address without forward_to_gosa-tag -> process '$header' here", 11);
1220                         }
1221                 }
1223                 # target is own address with forward_to_gosa-tag not pointing to myself -> process here
1224                 if (not $done) {
1225                         my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1226                         my $gosa_at;
1227                         my $gosa_session_id;
1228                         if (($target eq $local_address) && (defined $forward_to_gosa)){
1229                                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1230                                 if ($gosa_at ne $local_address) {
1231                                         $done = 1;
1232                                         &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag not pointing to myself -> process '$header' here", 11); 
1233                                 }
1234                         }
1235                 }
1237                 # Target is a client address and there is a processing function within a plugin -> process loaclly
1238                 if (not $done)
1239                 {
1240                         # Check if target is a client address
1241                         $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')"; 
1242                         $res = $known_clients_db->select_dbentry($sql);
1243                         if ((keys(%$res) > 0) ) 
1244                         {
1245                                 my $hostname = $res->{1}->{'hostname'};
1246                                 my $reduced_header = $header;
1247                                 $reduced_header =~ s/gosa_//;
1248                                 # Check if there is a processing function within a plugin
1249                                 if (exists $known_functions->{$reduced_header}) 
1250                                 {
1251                                         $msg =~ s/<target>\S*<\/target>/<target>$hostname<\/target>/;
1252                                         $done = 1;
1253                                         &daemon_log("$session_id DEBUG: Target is client address with processing function within a plugin -> process '$header' here", 11);
1254                                 }
1255                         }
1256                 }
1258                 # If header has a 'job_' prefix, do always process message locally
1259                 # which means put it into job queue
1260                 if ((not $done) && ($header =~ /job_/))
1261                 {
1262                         $done = 1;
1263                         &daemon_log("$session_id DEBUG: Header has a 'job_' prefix. Put it into job queue. -> process '$header' here", 11);
1264                 }
1266                 # if message should be processed here -> add message to incoming_db
1267                 if ($done) {
1268                         # if a 'job_' or a 'gosa_' message comes from a foreign server, fake module from
1269                         # ServerPackages to GosaPackages so gosa-si-server knows how to process this kind of messages
1270                         if ($header =~ /^gosa_/ || $header =~ /^job_/) {
1271                                 $module = "GosaPackages";
1272                         }
1274                         my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1275                                         primkey=>[],
1276                                         headertag=>$header,
1277                                         targettag=>$target,
1278                                         xmlmessage=>&encode_base64($msg),
1279                                         timestamp=>&get_time,
1280                                         module=>$module,
1281                                         sessionid=>$session_id,
1282                                 } );
1284                 }
1286                 # target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa
1287                 if (not $done) {
1288                         my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1289                         my $gosa_at;
1290                         my $gosa_session_id;
1291                         if (($target eq $local_address) && (defined $forward_to_gosa)){
1292                                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1293                                 if ($gosa_at eq $local_address) {
1294                                         my $session_reference = $kernel->ID_id_to_session($gosa_session_id);
1295                                         if( defined $session_reference ) {
1296                                                 $heap = $session_reference->get_heap();
1297                                         }
1298                                         if(exists $heap->{'client'}) {
1299                                                 $msg = &encrypt_msg($msg, $GosaPackages_key);
1300                                                 $heap->{'client'}->put($msg);
1301                                                 &daemon_log("$session_id DEBUG: incoming '$header' message forwarded to GOsa", 11); 
1302                                         }
1303                                         $done = 1;
1304                                         &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag pointing at myself -> forward '$header' to gosa", 11);
1305                                 }
1306                         }
1308                 }
1310                 # target is a client address in known_clients -> forward to client
1311                 if (not $done) {
1312                         $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')"; 
1313                         $res = $known_clients_db->select_dbentry($sql);
1314                         if (keys(%$res) > 0) 
1315                         {
1316                                 $done = 1; 
1317                                 &daemon_log("$session_id DEBUG: target is a client address in known_clients -> forward '$header' to client", 11);
1318                                 my $hostkey = $res->{1}->{'hostkey'};
1319                                 my $hostname = $res->{1}->{'hostname'};
1320                                 $msg =~ s/<target>\S*<\/target>/<target>$hostname<\/target>/;
1321                                 $msg =~ s/<header>gosa_/<header>/;
1322                                 my $error= &send_msg_to_target($msg, $hostname, $hostkey, $header, $session_id);
1323                                 if ($error) {
1324                                         &daemon_log("$session_id ERROR: Some problems occurred while trying to send msg to client '$hostkey': $msg", 1);
1325                                 }
1326                         } 
1327                         else 
1328                         {
1329                                 $not_found_in_known_clients_db = 1;
1330                         }
1331                 }
1333                 # target is a client address in foreign_clients -> forward to registration server
1334                 if (not $done) {
1335                         $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1336                         $res = $foreign_clients_db->select_dbentry($sql);
1337                         if (keys(%$res) > 0) {
1338                                 my $hostname = $res->{1}->{'hostname'};
1339                                 my ($host_ip, $host_port) = split(/:/, $hostname);
1340                                 my $local_address =  &get_local_ip_for_remote_ip($host_ip).":$server_port";
1341                                 my $regserver = $res->{1}->{'regserver'};
1342                                 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$regserver'"; 
1343                                 my $res = $known_server_db->select_dbentry($sql);
1344                                 if (keys(%$res) > 0) {
1345                                         my $regserver_key = $res->{1}->{'hostkey'};
1346                                         $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1347                                         $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1348                                         if ($source eq "GOSA") {
1349                                                 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1350                                         }
1351                                         my $error= &send_msg_to_target($msg, $regserver, $regserver_key, $header, $session_id);
1352                                         if ($error) {
1353                                                 &daemon_log("$session_id ERROR: some problems occurred while trying to send msg to registration server: $msg", 1); 
1354                                         }
1355                                 }
1356                                 $done = 1;
1357                                 &daemon_log("$session_id DEBUG: target is a client address in foreign_clients -> forward '$header' to registration server", 11);
1358                         } else {
1359                                 $not_found_in_foreign_clients_db = 1;
1360                         }
1361                 }
1363                 # target is a server address -> forward to server
1364                 if (not $done) {
1365                         $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1366                         $res = $known_server_db->select_dbentry($sql);
1367                         if (keys(%$res) > 0) {
1368                                 my $hostkey = $res->{1}->{'hostkey'};
1370                                 if ($source eq "GOSA") {
1371                                         $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1372                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1374                                 }
1376                                 &send_msg_to_target($msg, $target, $hostkey, $header, $session_id);
1377                                 $done = 1;
1378                                 &daemon_log("$session_id DEBUG: target is a server address -> forward '$header' to server", 11);
1379                         } else {
1380                                 $not_found_in_known_server_db = 1;
1381                         }
1382                 }
1385                 # target is not in foreign_clients_db, known_server_db or known_clients_db, maybe it is a complete new one -> process here
1386                 if ( $not_found_in_foreign_clients_db 
1387                         && $not_found_in_known_server_db
1388                         && $not_found_in_known_clients_db) {
1389                         &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);
1390             if ($header =~ /^gosa_/ || $header =~ /^job_/) { 
1391                 $module = "GosaPackages"; 
1392             }
1393                         my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1394                                         primkey=>[],
1395                                         headertag=>$header,
1396                                         targettag=>$target,
1397                                         xmlmessage=>&encode_base64($msg),
1398                                         timestamp=>&get_time,
1399                                         module=>$module,
1400                                         sessionid=>$session_id,
1401                                 } );
1402                         $done = 1;
1403                 }
1406                 if (not $done) {
1407                         daemon_log("$session_id ERROR: do not know what to do with this message: $msg", 1);
1408                         if ($source eq "GOSA") {
1409                                 my %data = ('error_msg' => &encode_base64($msg), 'error_string' => "Do not know what to do with this message!");
1410                                 my $error_msg = &build_msg("error", $local_address, "GOSA", \%data ); 
1412                                 my $session_reference = $kernel->ID_id_to_session($session_id);
1413                                 if( defined $session_reference ) {
1414                                         $heap = $session_reference->get_heap();
1415                                 }
1416                                 if(exists $heap->{'client'}) {
1417                                         $error_msg = &encrypt_msg($error_msg, $GosaPackages_key);
1418                                         $heap->{'client'}->put($error_msg);
1419                                 }
1420                         }
1421                 }
1423         }
1425         return;
1429 sub next_task {
1430     my ($session, $heap, $task) = @_[SESSION, HEAP, ARG0, ARG1];
1431     my $running_task = POE::Wheel::Run->new(
1432             Program => sub { process_task($session, $heap, $task) },
1433             StdioFilter => POE::Filter::Reference->new(),
1434             StdoutEvent  => "task_result",
1435             StderrEvent  => "task_debug",
1436             CloseEvent   => "task_done",
1437             );
1438     $heap->{task}->{ $running_task->ID } = $running_task;
1441 sub handle_task_result {
1442     my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
1443     my $client_answer = $result->{'answer'};
1444     if( $client_answer =~ s/session_id=(\d+)$// ) {
1445         my $session_id = $1;
1446         if( defined $session_id ) {
1447             my $session_reference = $kernel->ID_id_to_session($session_id);
1448             if( defined $session_reference ) {
1449                 $heap = $session_reference->get_heap();
1450             }
1451         }
1453         if(exists $heap->{'client'}) {
1454             $heap->{'client'}->put($client_answer);
1455         }
1456     }
1457     $kernel->sig(CHLD => "child_reap");
1460 sub handle_task_debug {
1461     my $result = $_[ARG0];
1462     print STDERR "$result\n";
1465 sub handle_task_done {
1466     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1467     delete $heap->{task}->{$task_id};
1468         if (exists $heap->{ldap_handle}->{$task_id}) {
1469                 &release_ldap_handle($heap->{ldap_handle}->{$task_id});
1470         }
1473 sub process_task {
1474     no strict "refs";
1475     #CHECK: Not @_[...]?
1476     my ($session, $heap, $task) = @_;
1477     my $error = 0;
1478     my $answer_l;
1479     my ($answer_header, @answer_target_l, $answer_source);
1480     my $client_answer = "";
1482     # prepare all variables needed to process message
1483     #my $msg = $task->{'xmlmessage'};
1484     my $msg = &decode_base64($task->{'xmlmessage'});
1485     my $incoming_id = $task->{'id'};
1486     my $module = $task->{'module'};
1487     my $header =  $task->{'headertag'};
1488     my $session_id = $task->{'sessionid'};
1489                 my $msg_hash;
1490                 eval {
1491         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
1492                 }; 
1493                 daemon_log("ERROR: XML failure '$@'") if ($@);
1494     my $source = @{$msg_hash->{'source'}}[0];
1495     
1496     # set timestamp of incoming client uptodate, so client will not 
1497     # be deleted from known_clients because of expiration
1498     my $cur_time = &get_time();
1499     my $sql = "UPDATE $known_clients_tn SET timestamp='$cur_time' WHERE hostname='$source'"; 
1500     my $res = $known_clients_db->exec_statement($sql);
1502     ######################
1503     # process incoming msg
1504     if( $error == 0) {
1505         daemon_log("$session_id INFO: Incoming msg (session_id=$session_id) with header '".@{$msg_hash->{'header'}}[0]."'", 5); 
1506         daemon_log("$session_id DEBUG: Processing module ".$module, 26);
1507         $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1509         if ( 0 < @{$answer_l} ) {
1510             my $answer_str = join("\n", @{$answer_l});
1511                         my @headers; 
1512             while ($answer_str =~ /<header>(\w+)<\/header>/g) {
1513                                 push(@headers, $1);
1514             }
1515                         daemon_log("$session_id INFO: got answer message(s) with header: '".join("', '", @headers)."'", 5);
1516             daemon_log("$session_id DEBUG: $module: got answer from module: \n".$answer_str,26);
1517         } else {
1518             daemon_log("$session_id DEBUG: $module: got no answer from module!" ,26);
1519         }
1521     }
1522     if( !$answer_l ) { $error++ };
1524     ########
1525     # answer
1526     if( $error == 0 ) {
1528         foreach my $answer ( @{$answer_l} ) {
1529             # check outgoing msg to xml validity
1530             my $answer_hash = &check_outgoing_xml_validity($answer, $session_id);
1531             if( not defined $answer_hash ) { next; }
1532             
1533             $answer_header = @{$answer_hash->{'header'}}[0];
1534             @answer_target_l = @{$answer_hash->{'target'}};
1535             $answer_source = @{$answer_hash->{'source'}}[0];
1537             # deliver msg to all targets 
1538             foreach my $answer_target ( @answer_target_l ) {
1540                 # targets of msg are all gosa-si-clients in known_clients_db
1541                 if( $answer_target eq "*" ) {
1542                     # answer is for all clients
1543                     my $sql_statement= "SELECT * FROM known_clients";
1544                     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
1545                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1546                         my $host_name = $hit->{hostname};
1547                         my $host_key = $hit->{hostkey};
1548                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1549                         &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1550                     }
1551                 }
1553                 # targets of msg are all gosa-si-server in known_server_db
1554                 elsif( $answer_target eq "KNOWN_SERVER" ) {
1555                     # answer is for all server in known_server
1556                     my $sql_statement= "SELECT * FROM $known_server_tn";
1557                     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
1558                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1559                         my $host_name = $hit->{hostname};
1560                         my $host_key = $hit->{hostkey};
1561                         $answer =~ s/<target>\S+<\/target>/<target>$host_name<\/target>/g;
1562                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1563                         &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1564                     }
1565                 }
1567                 # target of msg is GOsa
1568                                 elsif( $answer_target eq "GOSA" ) {
1569                                         my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1570                                         my $add_on = "";
1571                     if( defined $session_id ) {
1572                         $add_on = ".session_id=$session_id";
1573                     }
1574                     # answer is for GOSA and has to returned to connected client
1575                     my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1576                     $client_answer = $gosa_answer.$add_on;
1577                 }
1579                 # target of msg is job queue at this host
1580                 elsif( $answer_target eq "JOBDB") {
1581                     $answer =~ /<header>(\S+)<\/header>/;   
1582                     my $header;
1583                     if( defined $1 ) { $header = $1; }
1584                     my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1585                     &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1586                 }
1588                 # Target of msg is a mac address
1589                 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 ) {
1590                     daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients and foreign_clients", 5);
1592                     # Looking for macaddress in known_clients
1593                     my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1594                     my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1595                     my $found_ip_flag = 0;
1596                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1597                         my $host_name = $hit->{hostname};
1598                         my $host_key = $hit->{hostkey};
1599                         $answer =~ s/$answer_target/$host_name/g;
1600                         daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1601                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1602                         &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1603                         $found_ip_flag++ ;
1604                     }   
1606                     # Looking for macaddress in foreign_clients
1607                     if ($found_ip_flag == 0) {
1608                         my $sql = "SELECT * FROM $foreign_clients_tn WHERE macaddress LIKE '$answer_target'";
1609                         my $res = $foreign_clients_db->select_dbentry($sql);
1610                         while( my ($hit_num, $hit) = each %{ $res } ) {
1611                             my $host_name = $hit->{hostname};
1612                             my $reg_server = $hit->{regserver};
1613                             daemon_log("$session_id INFO: found host '$host_name' with mac '$answer_target', registered at '$reg_server'", 5);
1614                             
1615                             # Fetch key for reg_server
1616                             my $reg_server_key;
1617                             my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$reg_server'";
1618                             my $res = $known_server_db->select_dbentry($sql);
1619                             if (exists $res->{1}) {
1620                                 $reg_server_key = $res->{1}->{'hostkey'}; 
1621                             } else {
1622                                 daemon_log("$session_id ERROR: cannot find hostkey for '$host_name' in '$known_server_tn'", 1); 
1623                                 daemon_log("$session_id ERROR: unable to forward answer to correct registration server, processing is aborted!", 1); 
1624                                 $reg_server_key = undef;
1625                             }
1627                             # Send answer to server where client is registered
1628                             if (defined $reg_server_key) {
1629                                 $answer =~ s/$answer_target/$host_name/g;
1630                                 my $error = &send_msg_to_target($answer, $reg_server, $reg_server_key, $answer_header, $session_id);
1631                                 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1632                                 $found_ip_flag++ ;
1633                             }
1634                         }
1635                     }
1637                     # No mac to ip matching found
1638                     if( $found_ip_flag == 0) {
1639                         daemon_log("$session_id WARNING: no host found in known_clients or foreign_clients with mac address '$answer_target'", 3);
1640                         &reactivate_job_with_delay($session_id, $answer_target, $answer_header, 30);
1641                     }
1643                 # Answer is for one specific host   
1644                 } else {
1645                     # get encrypt_key
1646                     my $encrypt_key = &get_encrypt_key($answer_target);
1647                     if( not defined $encrypt_key ) {
1648                         # unknown target
1649                         daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1650                         next;
1651                     }
1652                     my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1653                     &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1654                 }
1655             }
1656         }
1657     }
1659     my $filter = POE::Filter::Reference->new();
1660     my %result = ( 
1661             status => "seems ok to me",
1662             answer => $client_answer,
1663             );
1665     my $output = $filter->put( [ \%result ] );
1666     print @$output;
1671 sub session_start {
1672     my ($kernel) = $_[KERNEL];
1673     $global_kernel = $kernel;
1674     $kernel->yield('register_at_foreign_servers');
1675         $kernel->yield('create_fai_server_db', $fai_server_tn );
1676         $kernel->yield('create_fai_release_db', $fai_release_tn );
1677     $kernel->yield('watch_for_next_tasks');
1678         $kernel->sig(USR1 => "sig_handler");
1679         $kernel->sig(USR2 => "recreate_packages_db");
1680         $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1681         $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay); 
1682     $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay); 
1683         $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1684     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1685         $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1686     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1688     # Start opsi check
1689     if ($opsi_enabled eq "true") {
1690         $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay); 
1691     }
1696 sub watch_for_done_jobs {
1697         #CHECK: $heap for what?
1698         my ($kernel,$heap) = @_[KERNEL, HEAP];
1700         my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((status='done') AND (modified='0'))";
1701         my $res = $job_db->select_dbentry( $sql_statement );
1703         while( my ($id, $hit) = each %{$res} ) {
1704                 my $jobdb_id = $hit->{id};
1705                 my $sql_statement = "DELETE FROM $job_queue_tn WHERE id=$jobdb_id"; 
1706                 my $res = $job_db->del_dbentry($sql_statement); 
1707         }
1709         $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1713 sub watch_for_opsi_jobs {
1714     my ($kernel) = $_[KERNEL];
1716     # 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 
1717     # opsi install job is to parse the xml message. There is still the correct header.
1718     my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((xmlmessage LIKE '%opsi_install_client</header>%') AND (status='processing') AND (siserver='localhost'))";
1719         my $res = $job_db->select_dbentry( $sql_statement );
1721     # Ask OPSI for an update of the running jobs
1722     while (my ($id, $hit) = each %$res ) {
1723         # Determine current parameters of the job
1724         my $hostId = $hit->{'plainname'};
1725         my $macaddress = $hit->{'macaddress'};
1726         my $progress = $hit->{'progress'};
1728         my $result= {};
1729         
1730         # For hosts, only return the products that are or get installed
1731         my $callobj;
1732         $callobj = {
1733             method  => 'getProductStates_hash',
1734             params  => [ $hostId ],
1735             id  => 1,
1736         };
1737         
1738         my $hres = $opsi_client->call($opsi_url, $callobj);
1739         #my ($hres_err, $hres_err_string) = &check_opsi_res($hres);
1740         if (not &check_opsi_res($hres)) {
1741             my $htmp= $hres->result->{$hostId};
1742         
1743             # Check state != not_installed or action == setup -> load and add
1744             my $products= 0;
1745             my $installed= 0;
1746             my $installing = 0;
1747             my $error= 0;  
1748             my @installed_list;
1749             my @error_list;
1750             my $act_status = "none";
1751             foreach my $product (@{$htmp}){
1753                 if ($product->{'installationStatus'} ne "not_installed" or
1754                         $product->{'actionRequest'} eq "setup"){
1756                     # Increase number of products for this host
1757                     $products++;
1758         
1759                     if ($product->{'installationStatus'} eq "failed"){
1760                         $result->{$product->{'productId'}}= "error";
1761                         unshift(@error_list, $product->{'productId'});
1762                         $error++;
1763                     }
1764                     if ($product->{'installationStatus'} eq "installed" && $product->{'actionRequest'}  eq "none"){
1765                         $result->{$product->{'productId'}}= "installed";
1766                         unshift(@installed_list, $product->{'productId'});
1767                         $installed++;
1768                     }
1769                     if ($product->{'installationStatus'} eq "installing"){
1770                         $result->{$product->{'productId'}}= "installing";
1771                         $installing++;
1772                         $act_status = "installing - ".$product->{'productId'};
1773                     }
1774                 }
1775             }
1776         
1777             # Estimate "rough" progress, avoid division by zero
1778             if ($products == 0) {
1779                 $result->{'progress'}= 0;
1780             } else {
1781                 $result->{'progress'}= int($installed * 100 / $products);
1782             }
1784             # Set updates in job queue
1785             if ((not $error) && (not $installing) && ($installed)) {
1786                 $act_status = "installed - ".join(", ", @installed_list);
1787             }
1788             if ($error) {
1789                 $act_status = "error - ".join(", ", @error_list);
1790             }
1791             if ($progress ne $result->{'progress'} ) {
1792                 # Updating progress and result 
1793                 my $update_statement = "UPDATE $job_queue_tn SET modified='1', progress='".$result->{'progress'}."', result='$act_status' WHERE macaddress LIKE '$macaddress' AND siserver='localhost'";
1794                 my $update_res = $job_db->update_dbentry($update_statement);
1795             }
1796             if ($progress eq 100) { 
1797                 # Updateing status
1798                 my $done_statement = "UPDATE $job_queue_tn SET modified='1', ";
1799                 if ($error) {
1800                     $done_statement .= "status='error'";
1801                 } else {
1802                     $done_statement .= "status='done'";
1803                 }
1804                 $done_statement .= " WHERE macaddress LIKE '$macaddress' AND siserver='localhost'";
1805                 my $done_res = $job_db->update_dbentry($done_statement);
1806             }
1809         }
1810     }
1812     $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay);
1816 # If a job got an update or was modified anyway, send to all other si-server an update message of this jobs.
1817 sub watch_for_modified_jobs {
1818     my ($kernel,$heap) = @_[KERNEL, HEAP];
1820     my $sql_statement = "SELECT * FROM $job_queue_tn WHERE (modified='1')"; 
1821     my $res = $job_db->select_dbentry( $sql_statement );
1822     
1823     # if db contains no jobs which should be update, do nothing
1824     if (keys %$res != 0) {
1826         if ($job_synchronization  eq "true") {
1827             # make out of the db result a gosa-si message   
1828             my $update_msg = &db_res2si_msg ($res, "foreign_job_updates", "KNOWN_SERVER", "MY_LOCAL_ADDRESS");
1829  
1830             # update all other SI-server
1831             &inform_all_other_si_server($update_msg);
1832         }
1834         # set jobs all jobs to modified = 0, wait until the next modification for updates of other si-server
1835         $sql_statement = "UPDATE $job_queue_tn SET modified='0' ";
1836         $res = $job_db->update_dbentry($sql_statement);
1837     }
1839     $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay);
1843 sub watch_for_new_jobs {
1844         if($watch_for_new_jobs_in_progress == 0) {
1845                 $watch_for_new_jobs_in_progress = 1;
1846                 my ($kernel,$heap) = @_[KERNEL, HEAP];
1848                 # check gosa job queue for jobs with executable timestamp
1849                 my $timestamp = &get_time();
1850                 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE siserver='localhost' AND status='waiting' AND (CAST(timestamp AS UNSIGNED)) < $timestamp ORDER BY timestamp";
1851                 my $res = $job_db->exec_statement( $sql_statement );
1853                 # Merge all new jobs that would do the same actions
1854                 my @drops;
1855                 my $hits;
1856                 foreach my $hit (reverse @{$res} ) {
1857                         my $macaddress= lc @{$hit}[8];
1858                         my $headertag= @{$hit}[5];
1859                         if(
1860                                 defined($hits->{$macaddress}) &&
1861                                 defined($hits->{$macaddress}->{$headertag}) &&
1862                                 defined($hits->{$macaddress}->{$headertag}[0])
1863                         ) {
1864                                 push @drops, "DELETE FROM $job_queue_tn WHERE id = $hits->{$macaddress}->{$headertag}[0]";
1865                         }
1866                         $hits->{$macaddress}->{$headertag}= $hit;
1867                 }
1869                 # Delete new jobs with a matching job in state 'processing'
1870                 foreach my $macaddress (keys %{$hits}) {
1871                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1872                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1873                                 if(defined($jobdb_id)) {
1874                                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND headertag='$jobdb_headertag' AND status='processing'";
1875                                         my $res = $job_db->exec_statement( $sql_statement );
1876                                         foreach my $hit (@{$res}) {
1877                                                 push @drops, "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1878                                         }
1879                                 } else {
1880                                         daemon_log("J ERROR: Job without id exists for macaddress $macaddress!", 1);
1881                                 }
1882                         }
1883                 }
1885                 # Commit deletion
1886                 $job_db->exec_statementlist(\@drops);
1888                 # Look for new jobs that could be executed
1889                 foreach my $macaddress (keys %{$hits}) {
1891                         # Look if there is an executing job
1892                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='processing'";
1893                         my $res = $job_db->exec_statement( $sql_statement );
1895                         # Skip new jobs for host if there is a processing job
1896                         if(defined($res) and defined @{$res}[0]) {
1897                                 # Prevent race condition if there is a trigger_activate job waiting and a goto-activation job processing
1898                                 my $row = @{$res}[0] if (ref $res eq 'ARRAY');
1899                                 if(@{$row}[5] eq 'trigger_action_reinstall') {
1900                                         my $sql_statement_2 =  "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='waiting' AND headertag = 'trigger_activate_new'"; 
1901                                         my $res_2 = $job_db->exec_statement( $sql_statement_2 );
1902                                         if(defined($res_2) and defined @{$res_2}[0]) {
1903                                                 # Set status from goto-activation to 'waiting' and update timestamp
1904                                                 $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'");
1905                                         }
1906                                 }
1907                                 next;
1908                         }
1910                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1911                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1912                                 if(defined($jobdb_id)) {
1913                                         my $job_msg = @{$hits->{$macaddress}->{$jobdb_headertag}}[7];
1915                                         daemon_log("J DEBUG: its time to execute $job_msg", 7);
1916                                         my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1917                                         my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1919                                         # expect macaddress is unique!!!!!!
1920                                         my $target = $res_hash->{1}->{hostname};
1922                                         # change header
1923                                         $job_msg =~ s/<header>job_/<header>gosa_/;
1925                                         # add sqlite_id
1926                                         $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1928                                         $job_msg =~ /<header>(\S+)<\/header>/;
1929                                         my $header = $1 ;
1930                                         my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");                    
1932                                         # update status in job queue to ...
1933                     # ... 'processing', for jobs: 'reinstall', 'update'
1934                     if (($header =~ /gosa_trigger_action_reinstall/) 
1935                             || ($header =~ /gosa_trigger_activate_new/)
1936                             || ($header =~ /gosa_trigger_action_update/)) {
1937                         my $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
1938                         my $dbres = $job_db->update_dbentry($sql_statement);
1939                     }
1941                     # ... 'done', for all other jobs, they are no longer needed in the jobqueue
1942                     else {
1943                         my $sql_statement = "UPDATE $job_queue_tn SET status='done' WHERE id=$jobdb_id";
1944                         my $dbres = $job_db->update_dbentry($sql_statement);
1945                     }
1946                 
1948                                         # We don't want parallel processing
1949                                         last;
1950                                 }
1951                         }
1952                 }
1954                 $watch_for_new_jobs_in_progress = 0;
1955                 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1956         }
1960 sub watch_for_new_messages {
1961     my ($kernel,$heap) = @_[KERNEL, HEAP];
1962     my @coll_user_msg;   # collection list of outgoing messages
1963     
1964     # check messaging_db for new incoming messages with executable timestamp
1965     my $timestamp = &get_time();
1966     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS UNSIGNED))<$timestamp AND flag='n' AND direction='in' )";
1967     my $res = $messaging_db->exec_statement( $sql_statement );
1968         foreach my $hit (@{$res}) {
1970         # create outgoing messages
1971         my $message_to = @{$hit}[3];
1972         # translate message_to to plain login name
1973         my @message_to_l = split(/,/, $message_to);  
1974                 my %receiver_h; 
1975                 foreach my $receiver (@message_to_l) {
1976                         if ($receiver =~ /^u_([\s\S]*)$/) {
1977                                 $receiver_h{$1} = 0;
1978                         } elsif ($receiver =~ /^g_([\s\S]*)$/) {
1979                                 my $group_name = $1;
1980                                 # fetch all group members from ldap and add them to receiver hash
1981                                 my $ldap_handle = &get_ldap_handle();
1982                                 if (defined $ldap_handle) {
1983                                                 my $mesg = $ldap_handle->search(
1984                                                                                 base => $ldap_base,
1985                                                                                 scope => 'sub',
1986                                                                                 attrs => ['memberUid'],
1987                                                                                 filter => "cn=$group_name",
1988                                                                                 );
1989                                                 if ($mesg->count) {
1990                                                                 my @entries = $mesg->entries;
1991                                                                 foreach my $entry (@entries) {
1992                                                                                 my @receivers= $entry->get_value("memberUid");
1993                                                                                 foreach my $receiver (@receivers) { 
1994                                                                                                 $receiver_h{$receiver} = 0;
1995                                                                                 }
1996                                                                 }
1997                                                 } 
1998                                                 # translating errors ?
1999                                                 if ($mesg->code) {
2000                                                                 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: $mesg->error", 1);
2001                                                 }
2002                                                 &release_ldap_handle($ldap_handle);
2003                                 # ldap handle error ?           
2004                                 } else {
2005                                         daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: no ldap handle available", 1);
2006                                 }
2007                         } else {
2008                                 my $sbjct = &encode_base64(@{$hit}[1]);
2009                                 my $msg = &encode_base64(@{$hit}[7]);
2010                                 &daemon_log("M WARNING: unknown receiver '$receiver' for a user-message '$sbjct - $msg'", 3); 
2011                         }
2012                 }
2013                 my @receiver_l = keys(%receiver_h);
2015         my $message_id = @{$hit}[0];
2017         #add each outgoing msg to messaging_db
2018         my $receiver;
2019         foreach $receiver (@receiver_l) {
2020             my $sql_statement = "INSERT INTO $messaging_tn (id, subject, message_from, message_to, flag, direction, delivery_time, message, timestamp) ".
2021                 "VALUES ('".
2022                 $message_id."', '".    # id
2023                 @{$hit}[1]."', '".     # subject
2024                 @{$hit}[2]."', '".     # message_from
2025                 $receiver."', '".      # message_to
2026                 "none"."', '".         # flag
2027                 "out"."', '".          # direction
2028                 @{$hit}[6]."', '".     # delivery_time
2029                 @{$hit}[7]."', '".     # message
2030                 $timestamp."'".     # timestamp
2031                 ")";
2032             &daemon_log("M DEBUG: $sql_statement", 1);
2033             my $res = $messaging_db->exec_statement($sql_statement);
2034             &daemon_log("M INFO: message '".@{$hit}[0]."' is prepared for delivery to receiver '$receiver'", 5);
2035         }
2037         # set incoming message to flag d=deliverd
2038         $sql_statement = "UPDATE $messaging_tn SET flag='p' WHERE id='$message_id'"; 
2039         &daemon_log("M DEBUG: $sql_statement", 7);
2040         $res = $messaging_db->update_dbentry($sql_statement);
2041         &daemon_log("M INFO: message '$message_id' is set to flag 'p' (processed)", 5);
2042     }
2044     $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay); 
2045     return;
2048 sub watch_for_delivery_messages {
2049     my ($kernel, $heap) = @_[KERNEL, HEAP];
2051     # select outgoing messages
2052     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( flag='p' AND direction='out' )";
2053     my $res = $messaging_db->exec_statement( $sql_statement );
2054     
2055     # build out msg for each    usr
2056     foreach my $hit (@{$res}) {
2057         my $receiver = @{$hit}[3];
2058         my $msg_id = @{$hit}[0];
2059         my $subject = @{$hit}[1];
2060         my $message = @{$hit}[7];
2062         # resolve usr -> host where usr is logged in
2063         my $sql = "SELECT * FROM $login_users_tn WHERE (user='$receiver')"; 
2064         my $res = $login_users_db->exec_statement($sql);
2066         # receiver is logged in nowhere
2067         if (not ref(@$res[0]) eq "ARRAY") { next; }    
2069         # receiver ist logged in at a client registered at local server
2070                 my $send_succeed = 0;
2071                 foreach my $hit (@$res) {
2072                                 my $receiver_host = @$hit[0];
2073                 my $delivered2host = 0;
2074                                 &daemon_log("M DEBUG: user '$receiver' is logged in at host '$receiver_host'", 7);
2076                                 # Looking for host in know_clients_db 
2077                                 my $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$receiver_host')";
2078                                 my $res = $known_clients_db->exec_statement($sql);
2080                 # Host is known in known_clients_db
2081                 if (ref(@$res[0]) eq "ARRAY") {
2082                     my $receiver_key = @{@{$res}[0]}[2];
2083                     my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
2084                     my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
2085                     my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0); 
2086                     if ($error == 0 ) {
2087                         $send_succeed++ ;
2088                         $delivered2host++ ;
2089                         &daemon_log("M DEBUG: send message for user '$receiver' to host '$receiver_host'", 7); 
2090                     } else {
2091                         &daemon_log("M DEBUG: cannot send message for user '$receiver' to host '$receiver_host'", 7); 
2092                     }
2093                 }
2094                 
2095                 # Message already send, do not need to do anything more, otherwise ...
2096                 if ($delivered2host) { next;}
2097     
2098                 # ...looking for host in foreign_clients_db
2099                 $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$receiver_host')";
2100                 $res = $foreign_clients_db->exec_statement($sql);
2101   
2102                                 # Host is known in foreign_clients_db 
2103                                 if (ref(@$res[0]) eq "ARRAY") { 
2104                     my $registration_server = @{@{$res}[0]}[2];
2105                     
2106                     # Fetch encryption key for registration server
2107                     my $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$registration_server')";
2108                     my $res = $known_server_db->exec_statement($sql);
2109                     if (ref(@$res[0]) eq "ARRAY") { 
2110                         my $registration_server_key = @{@{$res}[0]}[3];
2111                         my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
2112                         my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
2113                         my $error = &send_msg_to_target($out_msg, $registration_server, $registration_server_key, "usr_msg", 0); 
2114                         if ($error == 0 ) {
2115                             $send_succeed++ ;
2116                             $delivered2host++ ;
2117                             &daemon_log("M DEBUG: send message for user '$receiver' to server '$registration_server'", 7); 
2118                         } else {
2119                             &daemon_log("M ERROR: cannot send message for user '$receiver' to server '$registration_server'", 1); 
2120                         }
2122                     } else {
2123                         &daemon_log("M ERROR: host '$receiver_host' is reported to be ".
2124                                 "registrated at server '$registration_server', ".
2125                                 "but no data available in known_server_db ", 1); 
2126                     }
2127                 }
2128                 
2129                 if (not $delivered2host) {
2130                     &daemon_log("M ERROR: unable to send user message to host '$receiver_host'", 1);
2131                 }
2132                 }
2134                 if ($send_succeed) {
2135                                 # set outgoing msg at db to deliverd
2136                                 my $sql = "UPDATE $messaging_tn SET flag='d' WHERE (id='$msg_id' AND direction='out' AND message_to='$receiver')"; 
2137                                 my $res = $messaging_db->exec_statement($sql); 
2138                 &daemon_log("M INFO: send message for user '$receiver' to logged in hosts", 5);
2139                 } else {
2140             &daemon_log("M WARNING: failed to deliver message for user '$receiver'", 3); 
2141         }
2142         }
2144     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay); 
2145     return;
2149 sub watch_for_done_messages {
2150     my ($kernel,$heap) = @_[KERNEL, HEAP];
2152     my $sql = "SELECT * FROM $messaging_tn WHERE (flag='p' AND direction='in')"; 
2153     my $res = $messaging_db->exec_statement($sql); 
2155     foreach my $hit (@{$res}) {
2156         my $msg_id = @{$hit}[0];
2158         my $sql = "SELECT * FROM $messaging_tn WHERE (id='$msg_id' AND direction='out' AND (NOT flag='s'))"; 
2159         my $res = $messaging_db->exec_statement($sql);
2161         # not all usr msgs have been seen till now
2162         if ( ref(@$res[0]) eq "ARRAY") { next; }
2163         
2164         $sql = "DELETE FROM $messaging_tn WHERE (id='$msg_id')"; 
2165         $res = $messaging_db->exec_statement($sql);
2166     
2167     }
2169     $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay); 
2170     return;
2174 sub watch_for_old_known_clients {
2175     my ($kernel,$heap) = @_[KERNEL, HEAP];
2177     my $sql_statement = "SELECT * FROM $known_clients_tn";
2178     my $res = $known_clients_db->select_dbentry( $sql_statement );
2180     my $cur_time = int(&get_time());
2182     while ( my ($hit_num, $hit) = each %$res) {
2183         my $expired_timestamp = int($hit->{'timestamp'});
2184         $expired_timestamp =~ /(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/;
2185         my $dt = DateTime->new( year   => $1,
2186                 month  => $2,
2187                 day    => $3,
2188                 hour   => $4,
2189                 minute => $5,
2190                 second => $6,
2191                 );
2193         $dt->add( seconds => 2 * int($hit->{'keylifetime'}) );
2194         $expired_timestamp = $dt->ymd('').$dt->hms('');
2195         if ($cur_time > $expired_timestamp) {
2196             my $hostname = $hit->{'hostname'};
2197             my $del_sql = "DELETE FROM $known_clients_tn WHERE hostname='$hostname'"; 
2198             my $del_res = $known_clients_db->exec_statement($del_sql);
2200             &main::daemon_log("0 INFO: timestamp '".$hit->{'timestamp'}."' of client '$hostname' is expired('$expired_timestamp'), client will be deleted from known_clients_db", 5);
2201         }
2203     }
2205     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
2209 sub watch_for_next_tasks {
2210     my ($kernel,$heap) = @_[KERNEL, HEAP];
2212     my $sql = "SELECT * FROM $incoming_tn";
2213     my $res = $incoming_db->select_dbentry($sql);
2214     
2215     while ( my ($hit_num, $hit) = each %$res) {
2216         my $headertag = $hit->{'headertag'};
2217         if ($headertag =~ /^answer_(\d+)/) {
2218             # do not start processing, this message is for a still running POE::Wheel
2219             next;
2220         }
2221         my $message_id = $hit->{'id'};
2222         my $session_id = $hit->{'sessionid'};
2223         &daemon_log("$session_id DEBUG: start processing for message with incoming id: '$message_id'", 11);
2225         $kernel->yield('next_task', $hit);
2227         my $sql = "DELETE FROM $incoming_tn WHERE id=$message_id";
2228         my $res = $incoming_db->exec_statement($sql);
2229     }
2231     $kernel->delay_set('watch_for_next_tasks', 1); 
2235 sub get_ldap_handle {
2236         my ($session_id) = @_;
2237         my $heap;
2239         if (not defined $session_id ) { $session_id = 0 };
2240         if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
2242         my ($package, $file, $row, $subroutine, $hasArgs, $wantArray, $evalText, $isRequire) = caller(1);
2243         my $caller_text = "subroutine $subroutine";
2244         if ($subroutine eq "(eval)") {
2245                 $caller_text = "eval block within file '$file' for '$evalText'"; 
2246         }
2247         daemon_log("$session_id DEBUG: new ldap handle for '$caller_text' required!", 42);
2249 get_handle:
2250         my $ldap_handle = Net::LDAP->new( $ldap_uri );
2251         if (not ref $ldap_handle) {
2252                 daemon_log("$session_id ERROR: Connection to LDAP URI '$ldap_uri' failed! Retrying!", 1);
2253                 usleep(100000);
2254                 goto get_handle;
2255         } else {
2256                 daemon_log("$session_id DEBUG: Connection to LDAP URI '$ldap_uri' established.", 42);
2257         }
2259         $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);
2260         return $ldap_handle;
2264 sub release_ldap_handle {
2265         my ($ldap_handle, $session_id) = @_ ;
2266         if (not defined $session_id ) { $session_id = 0 };
2268         if(ref $ldap_handle) {
2269           $ldap_handle->disconnect();
2270   }
2271         &main::daemon_log("$session_id DEBUG: Released a ldap handle!", 42);
2272         return;
2276 sub change_fai_state {
2277         my ($st, $targets, $session_id) = @_;
2278         $session_id = 0 if not defined $session_id;
2279         # Set FAI state to localboot
2280         my %mapActions= (
2281                 reboot    => '',
2282                 update    => 'softupdate',
2283                 localboot => 'localboot',
2284                 reinstall => 'install',
2285                 rescan    => '',
2286                 wake      => '',
2287                 memcheck  => 'memcheck',
2288                 sysinfo   => 'sysinfo',
2289                 install   => 'install',
2290         );
2292         # Return if this is unknown
2293         if (!exists $mapActions{ $st }){
2294                 daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1); 
2295                 return;
2296         }
2298         my $state= $mapActions{ $st };
2300         # Build search filter for hosts
2301         my $search= "(&(objectClass=GOhard)";
2302         foreach (@{$targets}){
2303                 $search.= "(macAddress=$_)";
2304         }
2305         $search.= ")";
2307         # If there's any host inside of the search string, procress them
2308         if (!($search =~ /macAddress/)){
2309                 daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);    
2310                 return;
2311         }
2313         my $ldap_handle = &get_ldap_handle($session_id);
2314         # Perform search for Unit Tag
2315         my $mesg = $ldap_handle->search(
2316                 base   => $ldap_base,
2317                 scope  => 'sub',
2318                 attrs  => ['dn', 'FAIstate', 'objectClass'],
2319                 filter => "$search"
2320         );
2322         if ($mesg->count) {
2323                 my @entries = $mesg->entries;
2324                 if (0 == @entries) {
2325                         daemon_log("$session_id ERROR: ldap search failed: ldap_base=$ldap_base, filter=$search", 1); 
2326                 }
2328                 foreach my $entry (@entries) {
2329                         # Only modify entry if it is not set to '$state'
2330                         if ($entry->get_value("FAIstate") ne "$state"){
2331                                 daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
2332                                 my $result;
2333                                 my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
2334                                 if (exists $tmp{'FAIobject'}){
2335                                         if ($state eq ''){
2336                                                 $result= $ldap_handle->modify($entry->dn, changes => [ delete => [ FAIstate => [] ] ]);
2337                                         } else {
2338                                                 $result= $ldap_handle->modify($entry->dn, changes => [ replace => [ FAIstate => $state ] ]);
2339                                         }
2340                                 } elsif ($state ne ''){
2341                                         $result= $ldap_handle->modify($entry->dn, changes => [ add => [ objectClass => 'FAIobject' ], add => [ FAIstate => $state ] ]);
2342                                 }
2344                                 # Errors?
2345                                 if ($result->code){
2346                                         daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2347                                 }
2348                         } else {
2349                                 daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 42); 
2350                         }  
2351                 }
2352         } else {
2353                 daemon_log("$session_id ERROR: LDAP search failed: ldap_base=$ldap_base, filter=$search", 1);
2354         }
2355         &release_ldap_handle($ldap_handle, $session_id);                  
2357         return;
2361 sub change_goto_state {
2362     my ($st, $targets, $session_id) = @_;
2363     $session_id = 0  if not defined $session_id;
2365     # Switch on or off?
2366     my $state= $st eq 'active' ? 'active': 'locked';
2368     my $ldap_handle = &get_ldap_handle($session_id);
2369     if( defined($ldap_handle) ) {
2371       # Build search filter for hosts
2372       my $search= "(&(objectClass=GOhard)";
2373       foreach (@{$targets}){
2374         $search.= "(macAddress=$_)";
2375       }
2376       $search.= ")";
2378       # If there's any host inside of the search string, procress them
2379       if (!($search =~ /macAddress/)){
2380               &release_ldap_handle($ldap_handle);
2381         return;
2382       }
2384       # Perform search for Unit Tag
2385       my $mesg = $ldap_handle->search(
2386           base   => $ldap_base,
2387           scope  => 'sub',
2388           attrs  => ['dn', 'gotoMode'],
2389           filter => "$search"
2390           );
2392       if ($mesg->count) {
2393         my @entries = $mesg->entries;
2394         foreach my $entry (@entries) {
2396           # Only modify entry if it is not set to '$state'
2397           if ($entry->get_value("gotoMode") ne $state){
2399             daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
2400             my $result;
2401             $result= $ldap_handle->modify($entry->dn, changes => [replace => [ gotoMode => $state ] ]);
2403             # Errors?
2404             if ($result->code){
2405               &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2406             }
2408           }
2409         }
2410       } else {
2411                 daemon_log("$session_id ERROR: LDAP search failed in function change_goto_state: ldap_base=$ldap_base, filter=$search", 1);
2412           }
2414     }
2415         &release_ldap_handle($ldap_handle, $session_id);
2416         return;
2420 sub run_recreate_packages_db {
2421     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2422     my $session_id = $session->ID;
2423         &main::daemon_log("$session_id INFO: Recreating FAI Packages DB ('$fai_release_tn', '$fai_server_tn', '$packages_list_tn')", 5);
2424         $kernel->yield('create_fai_release_db', $fai_release_tn);
2425         $kernel->yield('create_fai_server_db', $fai_server_tn);
2426         return;
2430 sub run_create_fai_server_db {
2431     my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2432     my $session_id = $session->ID;
2433     my $task = POE::Wheel::Run->new(
2434             Program => sub { &create_fai_server_db($table_name,$kernel, undef, $session_id) },
2435             StdoutEvent  => "session_run_result",
2436             StderrEvent  => "session_run_debug",
2437             CloseEvent   => "session_run_done",
2438             );
2440     $heap->{task}->{ $task->ID } = $task;
2441     return;
2445 sub create_fai_server_db {
2446         my ($table_name, $kernel, $dont_create_packages_list, $session_id) = @_;
2447         my $result;
2449         if (not defined $session_id) { $session_id = 0; }
2450         my $ldap_handle = &get_ldap_handle($session_id);
2451         if(defined($ldap_handle)) {
2452                 daemon_log("$session_id INFO: create_fai_server_db: start", 5);
2453                 my $mesg= $ldap_handle->search(
2454                         base   => $ldap_base,
2455                         scope  => 'sub',
2456                         attrs  => ['FAIrepository', 'gosaUnitTag'],
2457                         filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
2458                 );
2459                 if($mesg->{'resultCode'} == 0 &&
2460                         $mesg->count != 0) {
2461                         foreach my $entry (@{$mesg->{entries}}) {
2462                                 if($entry->exists('FAIrepository')) {
2463                                         # Add an entry for each Repository configured for server
2464                                         foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
2465                                                 my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
2466                                                 my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
2467                                                 $result= $fai_server_db->add_dbentry( { 
2468                                                                 table => $table_name,
2469                                                                 primkey => ['server', 'fai_release', 'tag'],
2470                                                                 server => $tmp_url,
2471                                                                 fai_release => $tmp_release,
2472                                                                 sections => $tmp_sections,
2473                                                                 tag => (length($tmp_tag)>0)?$tmp_tag:"",
2474                                                         } );
2475                                         }
2476                                 }
2477                         }
2478                 }
2479                 daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
2480                 &release_ldap_handle($ldap_handle);
2482                 # TODO: Find a way to post the 'create_packages_list_db' event
2483                 if(not defined($dont_create_packages_list)) {
2484                         &create_packages_list_db(undef, $session_id);
2485                 }
2486         }       
2488         return $result;
2492 sub run_create_fai_release_db {
2493         my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2494         my $session_id = $session->ID;
2495         my $task = POE::Wheel::Run->new(
2496                 Program => sub { &create_fai_release_db($table_name, $session_id) },
2497                 StdoutEvent  => "session_run_result",
2498                 StderrEvent  => "session_run_debug",
2499                 CloseEvent   => "session_run_done",
2500         );
2502         $heap->{task}->{ $task->ID } = $task;
2503         return;
2507 sub create_fai_release_db {
2508         my ($table_name, $session_id) = @_;
2509         my $result;
2511         # used for logging
2512         if (not defined $session_id) { $session_id = 0; }
2514         my $ldap_handle = &get_ldap_handle($session_id);
2515         if(defined($ldap_handle)) {
2516                 daemon_log("$session_id INFO: create_fai_release_db: start",5);
2517                 my $mesg= $ldap_handle->search(
2518                         base   => $ldap_base,
2519                         scope  => 'sub',
2520                         attrs  => [],
2521                         filter => "(&(objectClass=organizationalUnit)(ou=fai))",
2522                 );
2523                 if(($mesg->code == 0) && ($mesg->count != 0))
2524                 {
2525                         daemon_log("$session_id DEBUG: create_fai_release_db: count " . $mesg->count,138);
2527                         # Walk through all possible FAI container ou's
2528                         my @sql_list;
2529                         my $timestamp= &get_time();
2530                         foreach my $ou (@{$mesg->{entries}}) {
2531                                 my $tmp_classes= resolve_fai_classes($ou->dn, $ldap_handle, $session_id);
2532                                 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
2533                                         my @tmp_array=get_fai_release_entries($tmp_classes);
2534                                         if(@tmp_array) {
2535                                                 foreach my $entry (@tmp_array) {
2536                                                         if(defined($entry) && ref($entry) eq 'HASH') {
2537                                                                 my $sql= 
2538                                                                 "INSERT INTO $table_name "
2539                                                                 ."(timestamp, fai_release, class, type, state) VALUES ("
2540                                                                 .$timestamp.","
2541                                                                 ."'".$entry->{'release'}."',"
2542                                                                 ."'".$entry->{'class'}."',"
2543                                                                 ."'".$entry->{'type'}."',"
2544                                                                 ."'".$entry->{'state'}."')";
2545                                                                 push @sql_list, $sql;
2546                                                         }
2547                                                 }
2548                                         }
2549                                 }
2550                         }
2552                         daemon_log("$session_id DEBUG: create_fai_release_db: Inserting ".scalar @sql_list." entries to DB",138);
2553             &release_ldap_handle($ldap_handle);
2554                         if(@sql_list) {
2555                                 unshift @sql_list, "VACUUM";
2556                                 unshift @sql_list, "DELETE FROM $table_name";
2557                                 $fai_release_db->exec_statementlist(\@sql_list);
2558                         }
2559                         daemon_log("$session_id DEBUG: create_fai_release_db: Done with inserting",138);
2560                 } else {
2561                         daemon_log("$session_id INFO: create_fai_release_db: error: " . $mesg->code, 5);
2562                 }
2563                 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
2564         }
2565         return $result;
2568 sub get_fai_types {
2569         my $tmp_classes = shift || return undef;
2570         my @result;
2572         foreach my $type(keys %{$tmp_classes}) {
2573                 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
2574                         my $entry = {
2575                                 type => $type,
2576                                 state => $tmp_classes->{$type}[0],
2577                         };
2578                         push @result, $entry;
2579                 }
2580         }
2582         return @result;
2585 sub get_fai_state {
2586         my $result = "";
2587         my $tmp_classes = shift || return $result;
2589         foreach my $type(keys %{$tmp_classes}) {
2590                 if(defined($tmp_classes->{$type}[0])) {
2591                         $result = $tmp_classes->{$type}[0];
2592                         
2593                 # State is equal for all types in class
2594                         last;
2595                 }
2596         }
2598         return $result;
2601 sub resolve_fai_classes {
2602         my ($fai_base, $ldap_handle, $session_id) = @_;
2603         if (not defined $session_id) { $session_id = 0; }
2604         my $result;
2605         my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
2606         my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
2607         my $fai_classes;
2609         daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base", 138);
2610         my $mesg= $ldap_handle->search(
2611                 base   => $fai_base,
2612                 scope  => 'sub',
2613                 attrs  => ['cn','objectClass','FAIstate'],
2614                 filter => $fai_filter,
2615         );
2616         daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries", 138);
2618         if($mesg->{'resultCode'} == 0 &&
2619                 $mesg->count != 0) {
2620                 foreach my $entry (@{$mesg->{entries}}) {
2621                         if($entry->exists('cn')) {
2622                                 my $tmp_dn= $entry->dn();
2623                                 $tmp_dn= substr( $tmp_dn, 0, length($tmp_dn)
2624                                         - length($fai_base) - 1 );
2626                                 # Skip classname and ou dn parts for class
2627                                 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?)$/;
2629                                 # Skip classes without releases
2630                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2631                                         next;
2632                                 }
2634                                 my $tmp_cn= $entry->get_value('cn');
2635                                 my $tmp_state= $entry->get_value('FAIstate');
2637                                 my $tmp_type;
2638                                 # Get FAI type
2639                                 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
2640                                         if(grep $_ eq $oclass, @possible_fai_classes) {
2641                                                 $tmp_type= $oclass;
2642                                                 last;
2643                                         }
2644                                 }
2646                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2647                                         # A Subrelease
2648                                         my @sub_releases = split(/,/, $tmp_release);
2650                                         # Walk through subreleases and build hash tree
2651                                         my $hash;
2652                                         while(my $tmp_sub_release = pop @sub_releases) {
2653                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2654                                         }
2655                                         eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
2656                                 } else {
2657                                         # A branch, no subrelease
2658                                         push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
2659                                 }
2660                         } elsif (!$entry->exists('cn')) {
2661                                 my $tmp_dn= $entry->dn();
2662                                 $tmp_dn= substr( $tmp_dn, 0, length($tmp_dn)
2663                                         - length($fai_base) - 1 );
2664                                 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?)$/;
2666                                 # Skip classes without releases
2667                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2668                                         next;
2669                                 }
2671                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2672                                         # A Subrelease
2673                                         my @sub_releases= split(/,/, $tmp_release);
2675                                         # Walk through subreleases and build hash tree
2676                                         my $hash;
2677                                         while(my $tmp_sub_release = pop @sub_releases) {
2678                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2679                                         }
2680                                         # Remove the last two characters
2681                                         chop($hash);
2682                                         chop($hash);
2684                                         eval('$fai_classes->'.$hash.'= {}');
2685                                 } else {
2686                                         # A branch, no subrelease
2687                                         if(!exists($fai_classes->{$tmp_release})) {
2688                                                 $fai_classes->{$tmp_release} = {};
2689                                         }
2690                                 }
2691                         }
2692                 }
2694                 # The hash is complete, now we can honor the copy-on-write based missing entries
2695                 foreach my $release (keys %$fai_classes) {
2696                         $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
2697                 }
2698         }
2699         return $result;
2702 sub apply_fai_inheritance {
2703        my $fai_classes = shift || return {};
2704        my $tmp_classes;
2706        # Get the classes from the branch
2707        foreach my $class (keys %{$fai_classes}) {
2708                # Skip subreleases
2709                if($class =~ /^ou=.*$/) {
2710                        next;
2711                } else {
2712                        $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
2713                }
2714        }
2716        # Apply to each subrelease
2717        foreach my $subrelease (keys %{$fai_classes}) {
2718                if($subrelease =~ /ou=/) {
2719                        foreach my $tmp_class (keys %{$tmp_classes}) {
2720                                if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
2721                                        $fai_classes->{$subrelease}->{$tmp_class} =
2722                                        deep_copy($tmp_classes->{$tmp_class});
2723                                } else {
2724                                        foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
2725                                                if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
2726                                                        $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
2727                                                        deep_copy($tmp_classes->{$tmp_class}->{$type});
2728                                                }
2729                                        }
2730                                }
2731                        }
2732                }
2733        }
2735        # Find subreleases in deeper levels
2736        foreach my $subrelease (keys %{$fai_classes}) {
2737                if($subrelease =~ /ou=/) {
2738                        foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
2739                                if($subsubrelease =~ /ou=/) {
2740                                        apply_fai_inheritance($fai_classes->{$subrelease});
2741                                }
2742                        }
2743                }
2744        }
2746        return $fai_classes;
2749 sub get_fai_release_entries {
2750         my $tmp_classes = shift || return;
2751         my $parent = shift || "";
2752         my @result = shift || ();
2754         foreach my $entry (keys %{$tmp_classes}) {
2755                 if(defined($entry)) {
2756                         if($entry =~ /^ou=.*$/) {
2757                                 my $release_name = $entry;
2758                                 $release_name =~ s/ou=//g;
2759                                 if(length($parent)>0) {
2760                                         $release_name = $parent."/".$release_name;
2761                                 }
2762                                 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2763                                 foreach my $bufentry(@bufentries) {
2764                                         push @result, $bufentry;
2765                                 }
2766                         } else {
2767                                 my @types = get_fai_types($tmp_classes->{$entry});
2768                                 foreach my $type (@types) {
2769                                         push @result, 
2770                                         {
2771                                                 'class' => $entry,
2772                                                 'type' => $type->{'type'},
2773                                                 'release' => $parent,
2774                                                 'state' => $type->{'state'},
2775                                         };
2776                                 }
2777                         }
2778                 }
2779         }
2781         return @result;
2784 sub deep_copy {
2785         my $this = shift;
2786         if (not ref $this) {
2787                 $this;
2788         } elsif (ref $this eq "ARRAY") {
2789                 [map deep_copy($_), @$this];
2790         } elsif (ref $this eq "HASH") {
2791                 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2792         } else { die "what type is $_?" }
2796 sub session_run_result {
2797     my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];    
2798     $kernel->sig(CHLD => "child_reap");
2801 sub session_run_debug {
2802     my $result = $_[ARG0];
2803     print STDERR "$result\n";
2806 sub session_run_done {
2807     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2808     delete $heap->{task}->{$task_id};
2809         if (exists $heap->{ldap_handle}->{$task_id}) {
2810                 &release_ldap_handle($heap->{ldap_handle}->{$task_id});
2811         }
2812         delete $heap->{ldap_handle}->{$task_id};
2816 sub create_sources_list {
2817         my $session_id = shift || 0;
2818         my $result="/tmp/gosa_si_tmp_sources_list";
2820         # Remove old file
2821         if(stat($result)) {
2822                 unlink($result);
2823                 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7); 
2824         }
2826         my $fh;
2827         open($fh, ">$result");
2828         if (not defined $fh) {
2829                 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7); 
2830                 return undef;
2831         }
2832         if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2833                 my $ldap_handle = &get_ldap_handle($session_id);
2834                 my $mesg=$ldap_handle->search(
2835                         base    => $main::ldap_server_dn,
2836                         scope   => 'base',
2837                         attrs   => 'FAIrepository',
2838                         filter  => 'objectClass=FAIrepositoryServer'
2839                 );
2840                 if($mesg->count) {
2841                         foreach my $entry(@{$mesg->{'entries'}}) {
2842                                 foreach my $value(@{$entry->get_value('FAIrepository', asref => 1)}) {
2843                                         my ($server, $tag, $release, $sections)= split /\|/, $value;
2844                                         my $line = "deb $server $release";
2845                                         $sections =~ s/,/ /g;
2846                                         $line.= " $sections";
2847                                         print $fh $line."\n";
2848                                 }
2849                         }
2850                 }
2851                 &release_ldap_handle($ldap_handle);
2852         } else {
2853                 if (defined $main::ldap_server_dn){
2854                         &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1); 
2855                 } else {
2856                         &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2857                 }
2858         }
2859         close($fh);
2861         return $result;
2865 sub run_create_packages_list_db {
2866     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2867         my $session_id = $session->ID;
2868         my $task = POE::Wheel::Run->new(
2869                                         Priority => +20,
2870                                         Program => sub {&create_packages_list_db(undef, $session_id)},
2871                                         StdoutEvent  => "session_run_result",
2872                                         StderrEvent  => "session_run_debug",
2873                                         CloseEvent   => "session_run_done",
2874                                         );
2875         $heap->{task}->{ $task->ID } = $task;
2879 sub create_packages_list_db {
2880         my ($sources_file, $session_id) = @_;
2881         
2882         # it should not be possible to trigger a recreation of packages_list_db
2883         # while packages_list_db is under construction, so set flag packages_list_under_construction
2884         # which is tested befor recreation can be started
2885         if (-r $packages_list_under_construction) {
2886                 daemon_log("$session_id WARNING: packages_list_db is right now under construction, please wait until this process is finished", 3);
2887                 return;
2888         } else {
2889                 daemon_log("$session_id INFO: create_packages_list_db: start", 5); 
2890                 # set packages_list_under_construction to true
2891                 system("touch $packages_list_under_construction");
2892                 @packages_list_statements=();
2893         }
2895         if (not defined $session_id) { $session_id = 0; }
2897         if (not defined $sources_file) { 
2898                 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5); 
2899                 $sources_file = &create_sources_list($session_id);
2900         }
2902         if (not defined $sources_file) {
2903                 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1); 
2904                 unlink($packages_list_under_construction);
2905                 return;
2906         }
2908         my $line;
2910         open(CONFIG, "<$sources_file") or do {
2911                 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2912                 unlink($packages_list_under_construction);
2913                 return;
2914         };
2916         # Read lines
2917         while ($line = <CONFIG>){
2918                 # Unify
2919                 chop($line);
2920                 $line =~ s/^\s+//;
2921                 $line =~ s/^\s+/ /;
2923                 # Strip comments
2924                 $line =~ s/#.*$//g;
2926                 # Skip empty lines
2927                 if ($line =~ /^\s*$/){
2928                         next;
2929                 }
2931                 # Interpret deb line
2932                 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2933                         my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2934                         my $section;
2935                         foreach $section (split(' ', $sections)){
2936                                 &parse_package_info( $baseurl, $dist, $section, $session_id );
2937                         }
2938                 }
2939         }
2941         close (CONFIG);
2943         if(keys(%repo_dirs)) {
2944                 find(\&cleanup_and_extract, keys( %repo_dirs ));
2945                 &main::strip_packages_list_statements();
2946                 $packages_list_db->exec_statementlist(\@packages_list_statements);
2947         }
2948         unlink($packages_list_under_construction);
2949         daemon_log("$session_id INFO: create_packages_list_db: finished", 5); 
2950         return;
2953 # This function should do some intensive task to minimize the db-traffic
2954 sub strip_packages_list_statements {
2955         my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2956         my @new_statement_list=();
2957         my $hash;
2958         my $insert_hash;
2959         my $update_hash;
2960         my $delete_hash;
2961         my $known_packages_hash;
2962         my $local_timestamp=get_time();
2964         foreach my $existing_entry (@existing_entries) {
2965                 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
2966         }
2968         foreach my $statement (@packages_list_statements) {
2969                 if($statement =~ /^INSERT/i) {
2970                         # Assign the values from the insert statement
2971                         my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~ 
2972                         /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
2973                         if(exists($hash->{$distribution}->{$package}->{$version})) {
2974                                 # If section or description has changed, update the DB
2975                                 if( 
2976                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or 
2977                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
2978                                 ) {
2979                                         @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
2980                                 } else {
2981                                         # package is already present in database. cache this knowledge for later use
2982                                         @{$known_packages_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2983                                 }
2984                         } else {
2985                                 # Insert a non-existing entry to db
2986                                 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2987                         }
2988                 } elsif ($statement =~ /^UPDATE/i) {
2989                         my ($template,$package,$version) = ($1,$2,$3) if $statement =~
2990                         /^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;
2991                         foreach my $distribution (keys %{$hash}) {
2992                                 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
2993                                         # update the insertion hash to execute only one query per package (insert instead insert+update)
2994                                         @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
2995                                 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
2996                                         if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
2997                                                 my $section;
2998                                                 my $description;
2999                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
3000                                                         length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
3001                                                         $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
3002                                                 }
3003                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
3004                                                         $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
3005                                                 }
3006                                                 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
3007                                         }
3008                                 }
3009                         }
3010                 }
3011         }
3013         # Check for orphaned entries
3014         foreach my $existing_entry (@existing_entries) {
3015                 my $distribution= @{$existing_entry}[0];
3016                 my $package= @{$existing_entry}[1];
3017                 my $version= @{$existing_entry}[2];
3018                 my $section= @{$existing_entry}[3];
3020                 if(
3021                         exists($insert_hash->{$distribution}->{$package}->{$version}) ||
3022                         exists($update_hash->{$distribution}->{$package}->{$version}) ||
3023                         exists($known_packages_hash->{$distribution}->{$package}->{$version})
3024                 ) {
3025                         next;
3026                 } else {
3027                         # Insert entry to delete hash
3028                         @{$delete_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section);
3029                 }
3030         }
3032         # unroll the insert hash
3033         foreach my $distribution (keys %{$insert_hash}) {
3034                 foreach my $package (keys %{$insert_hash->{$distribution}}) {
3035                         foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
3036                                 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
3037                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
3038                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
3039                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
3040                                 ."'$local_timestamp')";
3041                         }
3042                 }
3043         }
3045         # unroll the update hash
3046         foreach my $distribution (keys %{$update_hash}) {
3047                 foreach my $package (keys %{$update_hash->{$distribution}}) {
3048                         foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
3049                                 my $set = "";
3050                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
3051                                         $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
3052                                 }
3053                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
3054                                         $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
3055                                 }
3056                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
3057                                         $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
3058                                 }
3059                                 if(defined($set) and length($set) > 0) {
3060                                         $set .= "timestamp = '$local_timestamp'";
3061                                 } else {
3062                                         next;
3063                                 }
3064                                 push @new_statement_list, 
3065                                 "UPDATE $main::packages_list_tn SET $set WHERE"
3066                                 ." distribution = '$distribution'"
3067                                 ." AND package = '$package'"
3068                                 ." AND version = '$version'";
3069                         }
3070                 }
3071         }
3072         
3073         # unroll the delete hash
3074         foreach my $distribution (keys %{$delete_hash}) {
3075                 foreach my $package (keys %{$delete_hash->{$distribution}}) {
3076                         foreach my $version (keys %{$delete_hash->{$distribution}->{$package}}) {
3077                                 my $section = @{$delete_hash->{$distribution}->{$package}->{$version}}[3];
3078                                 push @new_statement_list, "DELETE FROM $main::packages_list_tn WHERE distribution='$distribution' AND package='$package' AND version='$version' AND section='$section'";
3079                         }
3080                 }
3081         }
3083         unshift(@new_statement_list, "VACUUM");
3085         @packages_list_statements = @new_statement_list;
3089 sub parse_package_info {
3090     my ($baseurl, $dist, $section, $session_id)= @_;
3091     my ($package);
3092     if (not defined $session_id) { $session_id = 0; }
3093     my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
3094     $repo_dirs{ "${repo_path}/pool" } = 1;
3096     foreach $package ("Packages.gz"){
3097         daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
3098         get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
3099         parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
3100     }
3101     
3105 sub get_package {
3106     my ($url, $dest, $session_id)= @_;
3107     if (not defined $session_id) { $session_id = 0; }
3109     my $tpath = dirname($dest);
3110     -d "$tpath" || mkpath "$tpath";
3112     # This is ugly, but I've no time to take a look at "how it works in perl"
3113     if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
3114         system("gunzip -cd '$dest' > '$dest.in'");
3115         daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 7);
3116         unlink($dest);
3117         daemon_log("$session_id DEBUG: delete file '$dest'", 7); 
3118     } else {
3119         daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' into '$dest' failed!", 1);
3120     }
3121     return 0;
3125 sub parse_package {
3126     my ($path, $dist, $srv_path, $session_id)= @_;
3127     if (not defined $session_id) { $session_id = 0;}
3128     my ($package, $version, $section, $description);
3129     my $PACKAGES;
3130     my $timestamp = &get_time();
3132     if(not stat("$path.in")) {
3133         daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
3134         return;
3135     }
3137     open($PACKAGES, "<$path.in");
3138     if(not defined($PACKAGES)) {
3139         daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1); 
3140         return;
3141     }
3143     # Read lines
3144     while (<$PACKAGES>){
3145         my $line = $_;
3146         # Unify
3147         chop($line);
3149         # Use empty lines as a trigger
3150         if ($line =~ /^\s*$/){
3151             my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
3152             push(@packages_list_statements, $sql);
3153             $package = "none";
3154             $version = "none";
3155             $section = "none";
3156             $description = "none"; 
3157             next;
3158         }
3160         # Trigger for package name
3161         if ($line =~ /^Package:\s/){
3162             ($package)= ($line =~ /^Package: (.*)$/);
3163             next;
3164         }
3166         # Trigger for version
3167         if ($line =~ /^Version:\s/){
3168             ($version)= ($line =~ /^Version: (.*)$/);
3169             next;
3170         }
3172         # Trigger for description
3173         if ($line =~ /^Description:\s/){
3174             ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
3175             next;
3176         }
3178         # Trigger for section
3179         if ($line =~ /^Section:\s/){
3180             ($section)= ($line =~ /^Section: (.*)$/);
3181             next;
3182         }
3184         # Trigger for filename
3185         if ($line =~ /^Filename:\s/){
3186             my ($filename) = ($line =~ /^Filename: (.*)$/);
3187             store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
3188             next;
3189         }
3190     }
3192     close( $PACKAGES );
3193     unlink( "$path.in" );
3197 sub store_fileinfo {
3198     my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
3200     my %fileinfo = (
3201         'package' => $package,
3202         'dist' => $dist,
3203         'version' => $vers,
3204     );
3206     $repo_files{ "${srvdir}/$file" } = \%fileinfo;
3210 sub cleanup_and_extract {
3211         my $fileinfo = $repo_files{ $File::Find::name };
3213         if( defined $fileinfo ) {
3214                 my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
3215                 my $sql;
3216                 my $package = $fileinfo->{ 'package' };
3217                 my $newver = $fileinfo->{ 'version' };
3219                 mkpath($dir);
3220                 system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
3222                 if( -f "$dir/DEBIAN/templates" ) {
3224                         daemon_log("0 DEBUG: Found debconf templates in '$package' - $newver", 7);
3226                         my $tmpl= ""; {
3227                                 local $/=undef;
3228                                 open FILE, "$dir/DEBIAN/templates";
3229                                 $tmpl = &encode_base64(<FILE>);
3230                                 close FILE;
3231                         }
3232                         rmtree("$dir/DEBIAN/templates");
3234                         $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
3235                         push @packages_list_statements, $sql;
3236                 }
3237         }
3239         return;
3243 sub register_at_foreign_servers {   
3244     my ($kernel) = $_[KERNEL];
3246         # Update status and update-time of all si-server with expired update_time and 
3247         # block them for race conditional registration processes of other si-servers.
3248         my $act_time = &get_time();
3249         my $block_statement = "UPDATE $known_server_tn SET status='new_server',update_time='19700101000000' WHERE (CAST(update_time AS UNSIGNED))<$act_time ";
3250         my $block_res = $known_server_db->exec_statement($block_statement);
3252         # Fetch all si-server from db where update_time is younger than act_time
3253         my $fetch_statement = "SELECT * FROM $known_server_tn WHERE update_time='19700101000000'"; 
3254         my $fetch_res = $known_server_db->exec_statement($fetch_statement);
3256     # Detect already connected clients. Will be added to registration msg later. 
3257     my $client_sql = "SELECT * FROM $known_clients_tn"; 
3258     my $client_res = $known_clients_db->exec_statement($client_sql);
3260         # Send registration messag to all fetched si-server
3261     foreach my $hit (@$fetch_res) {
3262         my $hostname = @$hit[0];
3263         my $hostkey = &create_passwd;
3265         # Add already connected clients to registration message 
3266         my $myhash = &create_xml_hash('new_server', $server_address, $hostname);
3267         &add_content2xml_hash($myhash, 'key', $hostkey);
3268         map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res);
3270         # Add locally loaded gosa-si modules to registration message
3271         my $loaded_modules = {};
3272         while (my ($package, $pck_info) = each %$known_modules) {
3273                         next if ((!defined(@$pck_info[2])) || (!(ref (@$pck_info[2]) eq 'HASH')));
3274                         foreach my $act_module (keys(%{@$pck_info[2]})) {
3275                                 $loaded_modules->{$act_module} = ""; 
3276                         }
3277                 }
3278         map(&add_content2xml_hash($myhash, "loaded_modules", $_), keys(%$loaded_modules));
3280         # Add macaddress to registration message
3281         my ($host_ip, $host_port) = split(/:/, $hostname);
3282         my $local_ip = &get_local_ip_for_remote_ip($host_ip);
3283         my $network_interface= &get_interface_for_ip($local_ip);
3284         my $host_mac = &get_mac_for_interface($network_interface);
3285         &add_content2xml_hash($myhash, 'macaddress', $host_mac);
3286         
3287         # Build registration message and send it
3288         my $foreign_server_msg = &create_xml_string($myhash);
3289         my $error = &send_msg_to_target($foreign_server_msg, $hostname, $ServerPackages_key, "new_server", 0); 
3290     }
3293         # After n sec perform a check of all server registration processes
3294     $kernel->delay_set("control_server_registration", 2); 
3296         return;
3300 sub control_server_registration {
3301         my ($kernel) = $_[KERNEL];
3302         
3303         # Check if all registration processes succeed or not
3304         my $select_statement = "SELECT * FROM $known_server_tn WHERE status='new_server'"; 
3305         my $select_res = $known_server_db->exec_statement($select_statement);
3307         # If at least one registration process failed, maybe in case of a race condition
3308         # with a foreign registration process
3309         if (@$select_res > 0) 
3310         {
3311                 # Release block statement 'new_server' to make the server accessible
3312                 # for foreign registration processes
3313                 my $update_statement = "UPDATE $known_server_tn SET status='waiting' WHERE status='new_server'";        
3314                 my $update_res = $known_server_db->exec_statement($update_statement);
3316                 # Set a random delay to avoid the registration race condition
3317                 my $new_foreign_servers_register_delay = int(rand(4))+1;
3318                 $kernel->delay_set("register_at_foreign_servers", $new_foreign_servers_register_delay);
3319         }
3320         # If all registration processes succeed
3321         else
3322         {
3323                 $kernel->delay_set("register_at_foreign_servers", $foreign_servers_register_delay);
3324         }
3326         return;
3330 #==== MAIN = main ==============================================================
3331 #  parse commandline options
3332 Getopt::Long::Configure( "bundling" );
3333 GetOptions("h|help" => \&usage,
3334         "c|config=s" => \$cfg_file,
3335         "f|foreground" => \$foreground,
3336         "v|verbose+" => \$verbose,
3337         "no-arp+" => \$no_arp,
3338                 "d=s" => \$debug_parts,
3339            ) or (&usage("", 1)&&(exit(-1))); 
3341 #  read and set config parameters
3342 &check_cmdline_param ;
3343 &read_configfile($cfg_file, %cfg_defaults);
3344 &check_pid;
3346 $SIG{CHLD} = 'IGNORE';
3348 # forward error messages to logfile
3349 if( ! $foreground ) {
3350   open( STDIN,  '+>/dev/null' );
3351   open( STDOUT, '+>&STDIN'    );
3352   open( STDERR, '+>&STDIN'    );
3355 # Just fork, if we are not in foreground mode
3356 if( ! $foreground ) { 
3357     chdir '/'                 or die "Can't chdir to /: $!";
3358     $pid = fork;
3359     setsid                    or die "Can't start a new session: $!";
3360     umask 0;
3361 } else { 
3362     $pid = $$; 
3365 # Do something useful - put our PID into the pid_file
3366 if( 0 != $pid ) {
3367     open( LOCK_FILE, ">$pid_file" );
3368     print LOCK_FILE "$pid\n";
3369     close( LOCK_FILE );
3370     if( !$foreground ) { 
3371         exit( 0 ) 
3372     };
3375 # parse head url and revision from svn
3376 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
3377 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
3378 $server_headURL = defined $1 ? $1 : 'unknown' ;
3379 $server_revision = defined $2 ? $2 : 'unknown' ;
3380 if ($server_headURL =~ /\/tag\// || 
3381         $server_headURL =~ /\/branches\// ) {
3382     $server_status = "stable"; 
3383 } else {
3384     $server_status = "developmental" ;
3386 # Prepare log file and set permissions
3387 $root_uid = getpwnam('root');
3388 $adm_gid = getgrnam('adm');
3389 open(FH, ">>$log_file");
3390 close FH;
3391 chmod(0440, $log_file);
3392 chown($root_uid, $adm_gid, $log_file);
3393 chown($root_uid, $adm_gid, "/var/lib/gosa-si");
3395 daemon_log(" ", 1);
3396 daemon_log("$0 started!", 1);
3397 daemon_log("status: $server_status", 1);
3398 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1); 
3400 # Buildup data bases
3402     no strict "refs";
3404     if ($db_module eq "DBmysql") {
3405         # connect to incoming_db
3406         $incoming_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3408         # connect to gosa-si job queue
3409         $job_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3411         # connect to known_clients_db
3412         $known_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3414         # connect to foreign_clients_db
3415         $foreign_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3417         # connect to known_server_db
3418         $known_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3420         # connect to login_usr_db
3421         $login_users_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3423         # connect to fai_server_db 
3424         $fai_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3426         # connect to fai_release_db
3427         $fai_release_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3429         # connect to packages_list_db
3430         $packages_list_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3432         # connect to messaging_db
3433         $messaging_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3435     } elsif ($db_module eq "DBsqlite") {
3436         # connect to incoming_db
3437         unlink($incoming_file_name);
3438         $incoming_db = GOSA::DBsqlite->new($incoming_file_name);
3439         chmod(0640, $incoming_file_name);
3440         chown($root_uid, $adm_gid, $incoming_file_name);
3441         
3442         # connect to gosa-si job queue
3443         $job_db = GOSA::DBsqlite->new($job_queue_file_name);
3444         chmod(0640, $job_queue_file_name);
3445         chown($root_uid, $adm_gid, $job_queue_file_name);
3446         
3447         # connect to known_clients_db
3448         #unlink($known_clients_file_name);
3449         $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
3450         chmod(0640, $known_clients_file_name);
3451         chown($root_uid, $adm_gid, $known_clients_file_name);
3452         
3453         # connect to foreign_clients_db
3454         #unlink($foreign_clients_file_name);
3455         $foreign_clients_db = GOSA::DBsqlite->new($foreign_clients_file_name);
3456         chmod(0640, $foreign_clients_file_name);
3457         chown($root_uid, $adm_gid, $foreign_clients_file_name);
3458         
3459         # connect to known_server_db
3460         #unlink($known_server_file_name);
3461         $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
3462         chmod(0640, $known_server_file_name);
3463         chown($root_uid, $adm_gid, $known_server_file_name);
3464         
3465         # connect to login_usr_db
3466         #unlink($login_users_file_name);
3467         $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
3468         chmod(0640, $login_users_file_name);
3469         chown($root_uid, $adm_gid, $login_users_file_name);
3470         
3471         # connect to fai_server_db
3472         unlink($fai_server_file_name);
3473         $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
3474         chmod(0640, $fai_server_file_name);
3475         chown($root_uid, $adm_gid, $fai_server_file_name);
3476         
3477         # connect to fai_release_db
3478         unlink($fai_release_file_name);
3479         $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
3480         chmod(0640, $fai_release_file_name);
3481         chown($root_uid, $adm_gid, $fai_release_file_name);
3482         
3483         # connect to packages_list_db
3484         unlink($packages_list_under_construction);
3485         $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
3486         chmod(0640, $packages_list_file_name);
3487         chown($root_uid, $adm_gid, $packages_list_file_name);
3488         
3489         # connect to messaging_db
3490         #unlink($messaging_file_name);
3491         $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
3492         chmod(0640, $messaging_file_name);
3493         chown($root_uid, $adm_gid, $messaging_file_name);
3494     }
3498 # Creating tables
3499 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
3500 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
3501 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
3502 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
3503 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
3504 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
3505 $foreign_clients_db->create_table($foreign_clients_tn, \@foreign_clients_col_names);
3506 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
3507 $incoming_db->create_table($incoming_tn, \@incoming_col_names);
3508 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
3510 # create xml object used for en/decrypting
3511 $xml = new XML::Simple();
3514 # foreign servers 
3515 my @foreign_server_list;
3517 # add foreign server from cfg file
3518 if ($foreign_server_string ne "") {
3519     my @cfg_foreign_server_list = split(",", $foreign_server_string);
3520     foreach my $foreign_server (@cfg_foreign_server_list) {
3521         push(@foreign_server_list, $foreign_server);
3522     }
3524     daemon_log("0 INFO: found foreign server in config file: ".join(", ", @foreign_server_list), 5);
3527 # Perform a DNS lookup for server registration if flag is true
3528 if ($dns_lookup eq "true") {
3529     # Add foreign server from dns
3530     my @tmp_servers;
3531     if (not $server_domain) {
3532         # Try our DNS Searchlist
3533         for my $domain(get_dns_domains()) {
3534             chomp($domain);
3535             my ($tmp_domains, $error_string) = &get_server_addresses($domain);
3536             if(@$tmp_domains) {
3537                 for my $tmp_server(@$tmp_domains) {
3538                     push @tmp_servers, $tmp_server;
3539                 }
3540             }
3541         }
3542         if(@tmp_servers && length(@tmp_servers)==0) {
3543             daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3544         }
3545     } else {
3546         @tmp_servers = &get_server_addresses($server_domain);
3547         if( 0 == @tmp_servers ) {
3548             daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3549         }
3550     }
3552     daemon_log("0 INFO: found foreign server via DNS ".join(", ", @tmp_servers), 5);    
3554     foreach my $server (@tmp_servers) { 
3555         unshift(@foreign_server_list, $server); 
3556     }
3557 } else {
3558     daemon_log("0 INFO: DNS lookup for server registration is disabled", 5);
3562 # eliminate duplicate entries
3563 @foreign_server_list = &del_doubles(@foreign_server_list);
3564 my $all_foreign_server = join(", ", @foreign_server_list);
3565 daemon_log("0 INFO: found foreign server in config file and DNS: '$all_foreign_server'", 5);
3567 # add all found foreign servers to known_server
3568 my $cur_timestamp = &get_time();
3569 foreach my $foreign_server (@foreign_server_list) {
3571         # do not add myself to known_server_db
3572         if (&is_local($foreign_server)) { next; }
3573         ######################################
3575     my $res = $known_server_db->add_dbentry( {table=>$known_server_tn, 
3576             primkey=>['hostname'],
3577             hostname=>$foreign_server,
3578             macaddress=>"",
3579             status=>'not_yet_registered',
3580             hostkey=>"none",
3581             loaded_modules => "none", 
3582             timestamp=>$cur_timestamp,
3583                         update_time=>'19700101000000',
3584             } );
3588 # Import all modules
3589 &import_modules;
3591 # Check wether all modules are gosa-si valid passwd check
3592 &password_check;
3594 # Create functions hash
3595 #print STDERR Dumper $known_modules;
3596 while (my ($module, @mod_info) = each %$known_modules) 
3598 #print STDERR Dumper $module;
3599         while (my ($plugin, $functions) = each %{$mod_info[0][2]})
3600         {
3601 #print STDERR Dumper $functions;
3602                 while (my ($function, $nothing) = each %$functions )
3603                 {
3604                         $known_functions->{$function} = $nothing;
3605                 }
3606         }
3609 # Prepare for using Opsi 
3610 if ($opsi_enabled eq "true") {
3611     use JSON::RPC::Client;
3612     use XML::Quote qw(:all);
3613     $opsi_url= "https://".$opsi_admin.":".$opsi_password."@".$opsi_server.":4447/rpc";
3614     $opsi_client = new JSON::RPC::Client;
3618 POE::Component::Server::TCP->new(
3619         Alias => "TCP_SERVER",
3620         Port => $server_port,
3621         ClientInput => sub {
3622                 my ($kernel, $input, $heap, $session) = @_[KERNEL, ARG0, HEAP, SESSION];
3623         my $session_id = $session->ID;
3624                 if ($input =~ /;([\d\.]+:[\d]+)$/) 
3625                 {
3626                         &daemon_log("$session_id DEBUG: incoming message from '$1'", 11);
3627                 }
3628                 else
3629                 {
3630                         my $remote_ip = $heap->{'remote_ip'};
3631                         &daemon_log("$session_id DEBUG: incoming message from '$remote_ip'", 11);
3632                 }
3633                 push(@msgs_to_decrypt, $input);
3634                 $kernel->yield("msg_to_decrypt");
3635         },
3636         InlineStates => {
3637                 msg_to_decrypt => \&msg_to_decrypt,
3638                 next_task => \&next_task,
3639                 task_result => \&handle_task_result,
3640                 task_done   => \&handle_task_done,
3641                 task_debug  => \&handle_task_debug,
3642                 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3643         }
3644 );
3646 daemon_log("0 INFO: start socket for incoming xml messages at port '$server_port' ", 1);
3648 # create session for repeatedly checking the job queue for jobs
3649 POE::Session->create(
3650         inline_states => {
3651                 _start => \&session_start,
3652         register_at_foreign_servers => \&register_at_foreign_servers,
3653                 control_server_registration => \&control_server_registration,
3654         sig_handler => \&sig_handler,
3655         next_task => \&next_task,
3656         task_result => \&handle_task_result,
3657         task_done   => \&handle_task_done,
3658         task_debug  => \&handle_task_debug,
3659         watch_for_next_tasks => \&watch_for_next_tasks,
3660         watch_for_new_messages => \&watch_for_new_messages,
3661         watch_for_delivery_messages => \&watch_for_delivery_messages,
3662         watch_for_done_messages => \&watch_for_done_messages,
3663                 watch_for_new_jobs => \&watch_for_new_jobs,
3664         watch_for_modified_jobs => \&watch_for_modified_jobs,
3665         watch_for_done_jobs => \&watch_for_done_jobs,
3666         watch_for_opsi_jobs => \&watch_for_opsi_jobs,
3667         watch_for_old_known_clients => \&watch_for_old_known_clients,
3668         create_packages_list_db => \&run_create_packages_list_db,
3669         create_fai_server_db => \&run_create_fai_server_db,
3670         create_fai_release_db => \&run_create_fai_release_db,
3671                 recreate_packages_db => \&run_recreate_packages_db,
3672         session_run_result => \&session_run_result,
3673         session_run_debug => \&session_run_debug,
3674         session_run_done => \&session_run_done,
3675         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3676         }
3677 );
3680 POE::Kernel->run();
3681 exit;