Code

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