Code

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