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 #===============================================================================
24 # TODO
25 #
26 # max_children wird momentan nicht mehr verwendet, jede eingehende nachricht bekommt ein eigenes POE child
28 use strict;
29 use warnings;
30 use Getopt::Long;
31 use Config::IniFiles;
32 use POSIX;
34 use Fcntl;
35 use IO::Socket::INET;
36 use IO::Handle;
37 use IO::Select;
38 use Symbol qw(qualify_to_ref);
39 use Crypt::Rijndael;
40 use MIME::Base64;
41 use Digest::MD5 qw(md5 md5_hex md5_base64);
42 use XML::Simple;
43 use Data::Dumper;
44 use Sys::Syslog qw( :DEFAULT setlogsock);
45 use Cwd;
46 use File::Spec;
47 use File::Basename;
48 use File::Find;
49 use File::Copy;
50 use File::Path;
51 use GOSA::DBsqlite;
52 use GOSA::GosaSupportDaemon;
53 use POE qw(Component::Server::TCP Wheel::Run Filter::Reference);
54 use Net::LDAP;
55 use Net::LDAP::Util qw(:escape);
57 my $modules_path = "/usr/lib/gosa-si/modules";
58 use lib "/usr/lib/gosa-si/modules";
60 # revision number of server and program name
61 my $server_version = '$HeadURL: https://oss.gonicus.de/repositories/gosa/trunk/gosa-si/gosa-si-server $:$Rev: 10826 $';
62 my $server_headURL;
63 my $server_revision;
64 my $server_status;
65 our $prg= basename($0);
67 our $global_kernel;
68 my (%cfg_defaults, $foreground, $verbose, $ping_timeout);
69 my ($bus_activ, $bus, $msg_to_bus, $bus_cipher);
70 my ($server);
71 my ($gosa_server, $job_queue_timeout, $job_queue_loop_delay);
72 my ($messaging_db_loop_delay);
73 my ($known_modules);
74 my ($pid_file, $procid, $pid, $log_file);
75 my ($arp_activ, $arp_fifo);
76 my ($xml);
77 my $sources_list;
78 my $max_clients;
79 my %repo_files=();
80 my $repo_path;
81 my %repo_dirs=();
82 # variables declared in config file are always set to 'our'
83 our (%cfg_defaults, $log_file, $pid_file,
84 $server_ip, $server_port, $SIPackages_key,
85 $arp_activ, $gosa_unit_tag,
86 $GosaPackages_key, $gosa_ip, $gosa_port, $gosa_timeout,
87 );
89 # additional variable which should be globaly accessable
90 our $server_address;
91 our $server_mac_address;
92 our $bus_address;
93 our $gosa_address;
94 our $no_bus;
95 our $no_arp;
96 our $verbose;
97 our $forground;
98 our $cfg_file;
99 our ($ldap_uri, $ldap_base, $ldap_admin_dn, $ldap_admin_password, $ldap_server_dn);
102 # specifies the verbosity of the daemon_log
103 $verbose = 0 ;
105 # if foreground is not null, script will be not forked to background
106 $foreground = 0 ;
108 # specifies the timeout seconds while checking the online status of a registrating client
109 $ping_timeout = 5;
111 $no_bus = 0;
112 $bus_activ = "true";
113 $no_arp = 0;
114 my $packages_list_under_construction = "/tmp/packages_list_creation_in_progress";
115 my @packages_list_statements;
116 my $watch_for_new_jobs_in_progress = 0;
118 # holds all incoming decrypted messages
119 our $incoming_db;
120 our $incoming_tn = 'incoming';
121 my $incoming_file_name;
122 my @incoming_col_names = ("id INTEGER PRIMARY KEY",
123 "timestamp DEFAULT 'none'",
124 "headertag DEFAULT 'none'",
125 "targettag DEFAULT 'none'",
126 "xmlmessage DEFAULT 'none'",
127 "module DEFAULT 'none'",
128 );
130 # holds all gosa jobs
131 our $job_db;
132 our $job_queue_tn = 'jobs';
133 my $job_queue_file_name;
134 my @job_queue_col_names = ("id INTEGER PRIMARY KEY",
135 "timestamp DEFAULT 'none'",
136 "status DEFAULT 'none'",
137 "result DEFAULT 'none'",
138 "progress DEFAULT 'none'",
139 "headertag DEFAULT 'none'",
140 "targettag DEFAULT 'none'",
141 "xmlmessage DEFAULT 'none'",
142 "macaddress DEFAULT 'none'",
143 "plainname DEFAULT 'none'",
144 );
146 # holds all other gosa-sd as well as the gosa-sd-bus
147 our $known_server_db;
148 our $known_server_tn = "known_server";
149 my $known_server_file_name;
150 my @known_server_col_names = ("hostname", "status", "hostkey", "timestamp");
152 # holds all registrated clients
153 our $known_clients_db;
154 our $known_clients_tn = "known_clients";
155 my $known_clients_file_name;
156 my @known_clients_col_names = ("hostname", "status", "hostkey", "timestamp", "macaddress", "events");
158 # holds all logged in user at each client
159 our $login_users_db;
160 our $login_users_tn = "login_users";
161 my $login_users_file_name;
162 my @login_users_col_names = ("client", "user", "timestamp");
164 # holds all fai server, the debian release and tag
165 our $fai_server_db;
166 our $fai_server_tn = "fai_server";
167 my $fai_server_file_name;
168 our @fai_server_col_names = ("timestamp", "server", "release", "sections", "tag");
170 our $fai_release_db;
171 our $fai_release_tn = "fai_release";
172 my $fai_release_file_name;
173 our @fai_release_col_names = ("timestamp", "release", "class", "type", "state");
175 # holds all packages available from different repositories
176 our $packages_list_db;
177 our $packages_list_tn = "packages_list";
178 my $packages_list_file_name;
179 our @packages_list_col_names = ("distribution", "package", "version", "section", "description", "template", "timestamp");
180 my $outdir = "/tmp/packages_list_db";
181 my $arch = "i386";
183 # holds all messages which should be delivered to a user
184 our $messaging_db;
185 our $messaging_tn = "messaging";
186 our @messaging_col_names = ("id INTEGER", "subject", "message_from", "message_to",
187 "flag", "direction", "delivery_time", "message", "timestamp" );
188 my $messaging_file_name;
190 # path to directory to store client install log files
191 our $client_fai_log_dir = "/var/log/fai";
193 # queue which stores taskes until one of the $max_children children are ready to process the task
194 my @tasks = qw();
195 my @msgs_to_decrypt = qw();
196 my $max_children = 2;
199 %cfg_defaults = (
200 "general" => {
201 "log-file" => [\$log_file, "/var/run/".$prg.".log"],
202 "pid-file" => [\$pid_file, "/var/run/".$prg.".pid"],
203 },
204 "bus" => {
205 "activ" => [\$bus_activ, "true"],
206 },
207 "server" => {
208 "port" => [\$server_port, "20081"],
209 "known-clients" => [\$known_clients_file_name, '/var/lib/gosa-si/clients.db' ],
210 "known-servers" => [\$known_server_file_name, '/var/lib/gosa-si/servers.db'],
211 "incoming" => [\$incoming_file_name, '/var/lib/gosa-si/incoming.db'],
212 "login-users" => [\$login_users_file_name, '/var/lib/gosa-si/users.db'],
213 "fai-server" => [\$fai_server_file_name, '/var/lib/gosa-si/fai_server.db'],
214 "fai-release" => [\$fai_release_file_name, '/var/lib/gosa-si/fai_release.db'],
215 "packages-list" => [\$packages_list_file_name, '/var/lib/gosa-si/packages.db'],
216 "messaging" => [\$messaging_file_name, '/var/lib/gosa-si/messaging.db'],
217 "source-list" => [\$sources_list, '/etc/apt/sources.list'],
218 "repo-path" => [\$repo_path, '/srv/www/repository'],
219 "ldap-uri" => [\$ldap_uri, ""],
220 "ldap-base" => [\$ldap_base, ""],
221 "ldap-admin-dn" => [\$ldap_admin_dn, ""],
222 "ldap-admin-password" => [\$ldap_admin_password, ""],
223 "gosa-unit-tag" => [\$gosa_unit_tag, ""],
224 "max-clients" => [\$max_clients, 10],
225 },
226 "GOsaPackages" => {
227 "ip" => [\$gosa_ip, "0.0.0.0"],
228 "port" => [\$gosa_port, "20082"],
229 "job-queue" => [\$job_queue_file_name, '/var/lib/gosa-si/jobs.db'],
230 "job-queue-loop-delay" => [\$job_queue_loop_delay, 3],
231 "messaging-db-loop-delay" => [\$messaging_db_loop_delay, 3],
232 "key" => [\$GosaPackages_key, "none"],
233 },
234 "SIPackages" => {
235 "key" => [\$SIPackages_key, "none"],
236 },
237 );
240 #=== FUNCTION ================================================================
241 # NAME: usage
242 # PARAMETERS: nothing
243 # RETURNS: nothing
244 # DESCRIPTION: print out usage text to STDERR
245 #===============================================================================
246 sub usage {
247 print STDERR << "EOF" ;
248 usage: $prg [-hvf] [-c config]
250 -h : this (help) message
251 -c <file> : config file
252 -f : foreground, process will not be forked to background
253 -v : be verbose (multiple to increase verbosity)
254 -no-bus : starts $prg without connection to bus
255 -no-arp : starts $prg without connection to arp module
257 EOF
258 print "\n" ;
259 }
262 #=== FUNCTION ================================================================
263 # NAME: read_configfile
264 # PARAMETERS: cfg_file - string -
265 # RETURNS: nothing
266 # DESCRIPTION: read cfg_file and set variables
267 #===============================================================================
268 sub read_configfile {
269 my $cfg;
270 if( defined( $cfg_file) && ( (-s $cfg_file) > 0 )) {
271 if( -r $cfg_file ) {
272 $cfg = Config::IniFiles->new( -file => $cfg_file );
273 } else {
274 print STDERR "Couldn't read config file!\n";
275 }
276 } else {
277 $cfg = Config::IniFiles->new() ;
278 }
279 foreach my $section (keys %cfg_defaults) {
280 foreach my $param (keys %{$cfg_defaults{ $section }}) {
281 my $pinfo = $cfg_defaults{ $section }{ $param };
282 ${@$pinfo[ 0 ]} = $cfg->val( $section, $param, @$pinfo[ 1 ] );
283 }
284 }
285 }
288 #=== FUNCTION ================================================================
289 # NAME: logging
290 # PARAMETERS: level - string - default 'info'
291 # msg - string -
292 # facility - string - default 'LOG_DAEMON'
293 # RETURNS: nothing
294 # DESCRIPTION: function for logging
295 #===============================================================================
296 sub daemon_log {
297 # log into log_file
298 my( $msg, $level ) = @_;
299 if(not defined $msg) { return }
300 if(not defined $level) { $level = 1 }
301 if(defined $log_file){
302 open(LOG_HANDLE, ">>$log_file");
303 if(not defined open( LOG_HANDLE, ">>$log_file" )) {
304 print STDERR "cannot open $log_file: $!";
305 return }
306 chomp($msg);
307 $msg =~s/\n//g; # no newlines are allowed in log messages, this is important for later log parsing
308 if($level <= $verbose){
309 my ($seconds, $minutes, $hours, $monthday, $month,
310 $year, $weekday, $yearday, $sommertime) = localtime(time);
311 $hours = $hours < 10 ? $hours = "0".$hours : $hours;
312 $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
313 $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
314 my @monthnames = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
315 $month = $monthnames[$month];
316 $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
317 $year+=1900;
318 my $name = $prg;
320 my $log_msg = "$month $monthday $hours:$minutes:$seconds $name $msg\n";
321 print LOG_HANDLE $log_msg;
322 if( $foreground ) {
323 print STDERR $log_msg;
324 }
325 }
326 close( LOG_HANDLE );
327 }
328 }
331 #=== FUNCTION ================================================================
332 # NAME: check_cmdline_param
333 # PARAMETERS: nothing
334 # RETURNS: nothing
335 # DESCRIPTION: validates commandline parameter
336 #===============================================================================
337 sub check_cmdline_param () {
338 my $err_config;
339 my $err_counter = 0;
340 if(not defined($cfg_file)) {
341 $cfg_file = "/etc/gosa-si/server.conf";
342 if(! -r $cfg_file) {
343 $err_config = "please specify a config file";
344 $err_counter += 1;
345 }
346 }
347 if( $err_counter > 0 ) {
348 &usage( "", 1 );
349 if( defined( $err_config)) { print STDERR "$err_config\n"}
350 print STDERR "\n";
351 exit( -1 );
352 }
353 }
356 #=== FUNCTION ================================================================
357 # NAME: check_pid
358 # PARAMETERS: nothing
359 # RETURNS: nothing
360 # DESCRIPTION: handels pid processing
361 #===============================================================================
362 sub check_pid {
363 $pid = -1;
364 # Check, if we are already running
365 if( open(LOCK_FILE, "<$pid_file") ) {
366 $pid = <LOCK_FILE>;
367 if( defined $pid ) {
368 chomp( $pid );
369 if( -f "/proc/$pid/stat" ) {
370 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
371 if( $stat ) {
372 daemon_log("ERROR: Already running",1);
373 close( LOCK_FILE );
374 exit -1;
375 }
376 }
377 }
378 close( LOCK_FILE );
379 unlink( $pid_file );
380 }
382 # create a syslog msg if it is not to possible to open PID file
383 if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
384 my($msg) = "Couldn't obtain lockfile '$pid_file' ";
385 if (open(LOCK_FILE, '<', $pid_file)
386 && ($pid = <LOCK_FILE>))
387 {
388 chomp($pid);
389 $msg .= "(PID $pid)\n";
390 } else {
391 $msg .= "(unable to read PID)\n";
392 }
393 if( ! ($foreground) ) {
394 openlog( $0, "cons,pid", "daemon" );
395 syslog( "warning", $msg );
396 closelog();
397 }
398 else {
399 print( STDERR " $msg " );
400 }
401 exit( -1 );
402 }
403 }
405 #=== FUNCTION ================================================================
406 # NAME: import_modules
407 # PARAMETERS: module_path - string - abs. path to the directory the modules
408 # are stored
409 # RETURNS: nothing
410 # DESCRIPTION: each file in module_path which ends with '.pm' and activation
411 # state is on is imported by "require 'file';"
412 #===============================================================================
413 sub import_modules {
414 daemon_log(" ", 1);
416 if (not -e $modules_path) {
417 daemon_log("ERROR: cannot find directory or directory is not readable: $modules_path", 1);
418 }
420 opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
421 while (defined (my $file = readdir (DIR))) {
422 if (not $file =~ /(\S*?).pm$/) {
423 next;
424 }
425 my $mod_name = $1;
427 if( $file =~ /ArpHandler.pm/ ) {
428 if( $no_arp > 0 ) {
429 next;
430 }
431 }
433 eval { require $file; };
434 if ($@) {
435 daemon_log("ERROR: gosa-si-server could not load module $file", 1);
436 daemon_log("$@", 5);
437 } else {
438 my $info = eval($mod_name.'::get_module_info()');
439 # Only load module if get_module_info() returns a non-null object
440 if( $info ) {
441 my ($input_address, $input_key, $input, $input_active, $input_type) = @{$info};
442 $known_modules->{$mod_name} = $info;
443 daemon_log("INFO: module $mod_name loaded", 5);
444 }
445 }
446 }
447 close (DIR);
448 }
451 #=== FUNCTION ================================================================
452 # NAME: sig_int_handler
453 # PARAMETERS: signal - string - signal arose from system
454 # RETURNS: noting
455 # DESCRIPTION: handels tasks to be done befor signal becomes active
456 #===============================================================================
457 sub sig_int_handler {
458 my ($signal) = @_;
460 # if (defined($ldap_handle)) {
461 # $ldap_handle->disconnect;
462 # }
463 # TODO alle verbliebenden ldap verbindungen aus allen heaps beenden
466 daemon_log("shutting down gosa-si-server", 1);
467 system("kill `ps -C gosa-si-server -o pid=`");
468 }
469 $SIG{INT} = \&sig_int_handler;
472 sub check_key_and_xml_validity {
473 my ($crypted_msg, $module_key, $session_id) = @_;
474 my $msg;
475 my $msg_hash;
476 my $error_string;
477 eval{
478 $msg = &decrypt_msg($crypted_msg, $module_key);
480 if ($msg =~ /<xml>/i){
481 $msg =~ s/\s+/ /g; # just for better daemon_log
482 daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 8);
483 $msg_hash = $xml->XMLin($msg, ForceArray=>1);
485 ##############
486 # check header
487 if( not exists $msg_hash->{'header'} ) { die "no header specified"; }
488 my $header_l = $msg_hash->{'header'};
489 if( 1 > @{$header_l} ) { die 'empty header tag'; }
490 if( 1 < @{$header_l} ) { die 'more than one header specified'; }
491 my $header = @{$header_l}[0];
492 if( 0 == length $header) { die 'empty string in header tag'; }
494 ##############
495 # check source
496 if( not exists $msg_hash->{'source'} ) { die "no source specified"; }
497 my $source_l = $msg_hash->{'source'};
498 if( 1 > @{$source_l} ) { die 'empty source tag'; }
499 if( 1 < @{$source_l} ) { die 'more than one source specified'; }
500 my $source = @{$source_l}[0];
501 if( 0 == length $source) { die 'source error'; }
503 ##############
504 # check target
505 if( not exists $msg_hash->{'target'} ) { die "no target specified"; }
506 my $target_l = $msg_hash->{'target'};
507 if( 1 > @{$target_l} ) { die 'empty target tag'; }
508 }
509 };
510 if($@) {
511 daemon_log("$session_id DEBUG: do not understand the message: $@", 7);
512 $msg = undef;
513 $msg_hash = undef;
514 }
516 return ($msg, $msg_hash);
517 }
520 sub check_outgoing_xml_validity {
521 my ($msg) = @_;
523 my $msg_hash;
524 eval{
525 $msg_hash = $xml->XMLin($msg, ForceArray=>1);
527 ##############
528 # check header
529 my $header_l = $msg_hash->{'header'};
530 if( 1 != @{$header_l} ) {
531 die 'no or more than one headers specified';
532 }
533 my $header = @{$header_l}[0];
534 if( 0 == length $header) {
535 die 'header has length 0';
536 }
538 ##############
539 # check source
540 my $source_l = $msg_hash->{'source'};
541 if( 1 != @{$source_l} ) {
542 die 'no or more than 1 sources specified';
543 }
544 my $source = @{$source_l}[0];
545 if( 0 == length $source) {
546 die 'source has length 0';
547 }
548 unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
549 $source =~ /^GOSA$/i ) {
550 die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
551 }
553 ##############
554 # check target
555 my $target_l = $msg_hash->{'target'};
556 if( 0 == @{$target_l} ) {
557 die "no targets specified";
558 }
559 foreach my $target (@$target_l) {
560 if( 0 == length $target) {
561 die "target has length 0";
562 }
563 unless( $target =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
564 $target =~ /^GOSA$/i ||
565 $target =~ /^\*$/ ||
566 $target =~ /KNOWN_SERVER/i ||
567 $target =~ /JOBDB/i ||
568 $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 ){
569 die "target '$target' is not a complete ip-address with port or a valid target name or a mac-address";
570 }
571 }
572 };
573 if($@) {
574 daemon_log("WARNING: outgoing msg is not gosa-si envelope conform", 5);
575 daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 8);
576 $msg_hash = undef;
577 }
579 return ($msg_hash);
580 }
583 sub input_from_known_server {
584 my ($input, $remote_ip, $session_id) = @_ ;
585 my ($msg, $msg_hash, $module);
587 my $sql_statement= "SELECT * FROM known_server";
588 my $query_res = $known_server_db->select_dbentry( $sql_statement );
590 while( my ($hit_num, $hit) = each %{ $query_res } ) {
591 my $host_name = $hit->{hostname};
592 if( not $host_name =~ "^$remote_ip") {
593 next;
594 }
595 my $host_key = $hit->{hostkey};
596 daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 7);
597 daemon_log("DEBUG: input_from_known_server: host_key: $host_key", 7);
599 # check if module can open msg envelope with module key
600 my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
601 if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
602 daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 7);
603 daemon_log("$@", 8);
604 next;
605 }
606 else {
607 $msg = $tmp_msg;
608 $msg_hash = $tmp_msg_hash;
609 $module = "SIPackages";
610 last;
611 }
612 }
614 if( (!$msg) || (!$msg_hash) || (!$module) ) {
615 daemon_log("$session_id DEBUG: Incoming message is not from a known server", 7);
616 }
618 return ($msg, $msg_hash, $module);
619 }
622 sub input_from_known_client {
623 my ($input, $remote_ip, $session_id) = @_ ;
624 my ($msg, $msg_hash, $module);
626 my $sql_statement= "SELECT * FROM known_clients";
627 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
628 while( my ($hit_num, $hit) = each %{ $query_res } ) {
629 my $host_name = $hit->{hostname};
630 if( not $host_name =~ /^$remote_ip:\d*$/) {
631 next;
632 }
633 my $host_key = $hit->{hostkey};
634 &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 7);
635 &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 7);
637 # check if module can open msg envelope with module key
638 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
640 if( (!$msg) || (!$msg_hash) ) {
641 &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 7);
642 &daemon_log("$@", 8);
643 next;
644 }
645 else {
646 $module = "SIPackages";
647 last;
648 }
649 }
651 if( (!$msg) || (!$msg_hash) || (!$module) ) {
652 &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 7);
653 }
655 return ($msg, $msg_hash, $module);
656 }
659 sub input_from_unknown_host {
660 no strict "refs";
661 my ($input, $session_id) = @_ ;
662 my ($msg, $msg_hash, $module);
663 my $error_string;
665 my %act_modules = %$known_modules;
667 while( my ($mod, $info) = each(%act_modules)) {
669 # check a key exists for this module
670 my $module_key = ${$mod."_key"};
671 if( not defined $module_key ) {
672 if( $mod eq 'ArpHandler' ) {
673 next;
674 }
675 daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
676 next;
677 }
678 daemon_log("$session_id DEBUG: $mod: $module_key", 7);
680 # check if module can open msg envelope with module key
681 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
682 if( (not defined $msg) || (not defined $msg_hash) ) {
683 next;
684 }
685 else {
686 $module = $mod;
687 last;
688 }
689 }
691 if( (!$msg) || (!$msg_hash) || (!$module)) {
692 daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 7);
693 }
695 return ($msg, $msg_hash, $module);
696 }
699 sub create_ciphering {
700 my ($passwd) = @_;
701 if((!defined($passwd)) || length($passwd)==0) {
702 $passwd = "";
703 }
704 $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
705 my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
706 my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
707 $my_cipher->set_iv($iv);
708 return $my_cipher;
709 }
712 sub encrypt_msg {
713 my ($msg, $key) = @_;
714 my $my_cipher = &create_ciphering($key);
715 my $len;
716 {
717 use bytes;
718 $len= 16-length($msg)%16;
719 }
720 $msg = "\0"x($len).$msg;
721 $msg = $my_cipher->encrypt($msg);
722 chomp($msg = &encode_base64($msg));
723 # there are no newlines allowed inside msg
724 $msg=~ s/\n//g;
725 return $msg;
726 }
729 sub decrypt_msg {
731 my ($msg, $key) = @_ ;
732 $msg = &decode_base64($msg);
733 my $my_cipher = &create_ciphering($key);
734 $msg = $my_cipher->decrypt($msg);
735 $msg =~ s/\0*//g;
736 return $msg;
737 }
740 sub get_encrypt_key {
741 my ($target) = @_ ;
742 my $encrypt_key;
743 my $error = 0;
745 # target can be in known_server
746 if( not defined $encrypt_key ) {
747 my $sql_statement= "SELECT * FROM known_server WHERE hostname='$target'";
748 my $query_res = $known_server_db->select_dbentry( $sql_statement );
749 while( my ($hit_num, $hit) = each %{ $query_res } ) {
750 my $host_name = $hit->{hostname};
751 if( $host_name ne $target ) {
752 next;
753 }
754 $encrypt_key = $hit->{hostkey};
755 last;
756 }
757 }
759 # target can be in known_client
760 if( not defined $encrypt_key ) {
761 my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$target'";
762 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
763 while( my ($hit_num, $hit) = each %{ $query_res } ) {
764 my $host_name = $hit->{hostname};
765 if( $host_name ne $target ) {
766 next;
767 }
768 $encrypt_key = $hit->{hostkey};
769 last;
770 }
771 }
773 return $encrypt_key;
774 }
777 #=== FUNCTION ================================================================
778 # NAME: open_socket
779 # PARAMETERS: PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
780 # [PeerPort] string necessary if port not appended by PeerAddr
781 # RETURNS: socket IO::Socket::INET
782 # DESCRIPTION: open a socket to PeerAddr
783 #===============================================================================
784 sub open_socket {
785 my ($PeerAddr, $PeerPort) = @_ ;
786 if(defined($PeerPort)){
787 $PeerAddr = $PeerAddr.":".$PeerPort;
788 }
789 my $socket;
790 $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
791 Porto => "tcp",
792 Type => SOCK_STREAM,
793 Timeout => 5,
794 );
795 if(not defined $socket) {
796 return;
797 }
798 # &daemon_log("DEBUG: open_socket: $PeerAddr", 7);
799 return $socket;
800 }
803 #=== FUNCTION ================================================================
804 # NAME: get_ip
805 # PARAMETERS: interface name (i.e. eth0)
806 # RETURNS: (ip address)
807 # DESCRIPTION: Uses ioctl to get ip address directly from system.
808 #===============================================================================
809 sub get_ip {
810 my $ifreq= shift;
811 my $result= "";
812 my $SIOCGIFADDR= 0x8915; # man 2 ioctl_list
813 my $proto= getprotobyname('ip');
815 socket SOCKET, PF_INET, SOCK_DGRAM, $proto
816 or die "socket: $!";
818 if(ioctl SOCKET, $SIOCGIFADDR, $ifreq) {
819 my ($if, $sin) = unpack 'a16 a16', $ifreq;
820 my ($port, $addr) = sockaddr_in $sin;
821 my $ip = inet_ntoa $addr;
823 if ($ip && length($ip) > 0) {
824 $result = $ip;
825 }
826 }
828 return $result;
829 }
832 sub get_local_ip_for_remote_ip {
833 my $remote_ip= shift;
834 my $result="0.0.0.0";
836 if($remote_ip =~ /^(\d\d?\d?\.){3}\d\d?\d?$/) {
837 if($remote_ip eq "127.0.0.1") {
838 $result = "127.0.0.1";
839 } else {
840 my $PROC_NET_ROUTE= ('/proc/net/route');
842 open(PROC_NET_ROUTE, "<$PROC_NET_ROUTE")
843 or die "Could not open $PROC_NET_ROUTE";
845 my @ifs = <PROC_NET_ROUTE>;
847 close(PROC_NET_ROUTE);
849 # Eat header line
850 shift @ifs;
851 chomp @ifs;
852 foreach my $line(@ifs) {
853 my ($Iface,$Destination,$Gateway,$Flags,$RefCnt,$Use,$Metric,$Mask,$MTU,$Window,$IRTT)=split(/\s/, $line);
854 my $destination;
855 my $mask;
856 my ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Destination);
857 $destination= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
858 ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Mask);
859 $mask= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
860 if(new NetAddr::IP($remote_ip)->within(new NetAddr::IP($destination, $mask))) {
861 # destination matches route, save mac and exit
862 $result= &get_ip($Iface);
863 last;
864 }
865 }
866 }
867 } else {
868 daemon_log("get_local_ip_for_remote_ip was called with a non-ip parameter: $remote_ip", 1);
869 }
870 return $result;
871 }
874 sub send_msg_to_target {
875 my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
876 my $error = 0;
877 my $header;
878 my $new_status;
879 my $act_status;
880 my ($sql_statement, $res);
882 if( $msg_header ) {
883 $header = "'$msg_header'-";
884 } else {
885 $header = "";
886 }
888 # Patch the source ip
889 if($msg =~ /<source>0\.0\.0\.0:\d*?<\/source>/) {
890 my $remote_ip = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
891 $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$remote_ip:$2<\/source>/s;
892 }
894 # encrypt xml msg
895 my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
897 # opensocket
898 my $socket = &open_socket($address);
899 if( !$socket ) {
900 daemon_log("$session_id ERROR: cannot send ".$header."msg to $address , host not reachable", 1);
901 $error++;
902 }
904 if( $error == 0 ) {
905 # send xml msg
906 print $socket $crypted_msg."\n";
908 daemon_log("$session_id INFO: send ".$header."msg to $address", 5);
909 #daemon_log("DEBUG: message:\n$msg", 9);
911 }
913 # close socket in any case
914 if( $socket ) {
915 close $socket;
916 }
918 if( $error > 0 ) { $new_status = "down"; }
919 else { $new_status = $msg_header; }
922 # known_clients
923 $sql_statement = "SELECT * FROM known_clients WHERE hostname='$address'";
924 $res = $known_clients_db->select_dbentry($sql_statement);
925 if( keys(%$res) > 0) {
926 $act_status = $res->{1}->{'status'};
927 if( $act_status eq "down" ) {
928 $sql_statement = "DELETE FROM known_clients WHERE hostname='$address'";
929 $res = $known_clients_db->del_dbentry($sql_statement);
930 daemon_log("$session_id WARNING: failed 2x to send msg to host '$address', delete host from known_clients", 3);
931 } else {
932 $sql_statement = "UPDATE known_clients SET status='$new_status' WHERE hostname='$address'";
933 $res = $known_clients_db->update_dbentry($sql_statement);
934 if($new_status eq "down"){
935 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
936 } else {
937 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
938 }
939 }
940 }
942 # known_server
943 $sql_statement = "SELECT * FROM known_server WHERE hostname='$address'";
944 $res = $known_server_db->select_dbentry($sql_statement);
945 if( keys(%$res) > 0 ) {
946 $act_status = $res->{1}->{'status'};
947 if( $act_status eq "down" ) {
948 $sql_statement = "DELETE FROM known_server WHERE hostname='$address'";
949 $res = $known_server_db->del_dbentry($sql_statement);
950 daemon_log("$session_id WARNING: failed 2x to a send msg to host '$address', delete host from known_server", 3);
951 }
952 else {
953 $sql_statement = "UPDATE known_server SET status='$new_status' WHERE hostname='$address'";
954 $res = $known_server_db->update_dbentry($sql_statement);
955 if($new_status eq "down"){
956 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
957 }
958 else {
959 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
960 }
961 }
962 }
963 return $error;
964 }
967 sub update_jobdb_status_for_send_msgs {
968 my ($answer, $error) = @_;
969 if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
970 my $jobdb_id = $1;
972 # sending msg faild
973 if( $error ) {
974 if (not $answer =~ /<header>trigger_action_reinstall<\/header>/) {
975 my $sql_statement = "UPDATE $job_queue_tn ".
976 "SET status='error', result='can not deliver msg, please consult log file' ".
977 "WHERE id=$jobdb_id";
978 my $res = $job_db->update_dbentry($sql_statement);
979 }
981 # sending msg was successful
982 } else {
983 my $sql_statement = "UPDATE $job_queue_tn ".
984 "SET status='done' ".
985 "WHERE id=$jobdb_id AND status='processed'";
986 my $res = $job_db->update_dbentry($sql_statement);
987 }
988 }
989 }
991 sub _start {
992 my ($kernel) = $_[KERNEL];
993 &trigger_db_loop($kernel);
994 $global_kernel = $kernel;
995 $kernel->yield('create_fai_server_db', $fai_server_tn );
996 $kernel->yield('create_fai_release_db', $fai_release_tn );
997 $kernel->sig(USR1 => "sig_handler");
998 $kernel->sig(USR2 => "create_packages_list_db");
999 }
1001 sub sig_handler {
1002 my ($kernel, $signal) = @_[KERNEL, ARG0] ;
1003 daemon_log("0 INFO got signal '$signal'", 1);
1004 $kernel->sig_handled();
1005 return;
1006 }
1009 sub msg_to_decrypt {
1010 my ($session, $heap) = @_[SESSION, HEAP];
1011 my $session_id = $session->ID;
1012 my ($msg, $msg_hash, $module);
1013 my $error = 0;
1015 # hole neue msg aus @msgs_to_decrypt
1016 my $next_msg = shift @msgs_to_decrypt;
1018 # entschlüssle sie
1020 # msg is from a new client or gosa
1021 ($msg, $msg_hash, $module) = &input_from_unknown_host($next_msg, $session_id);
1022 # msg is from a gosa-si-server or gosa-si-bus
1023 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1024 ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $heap->{'remote_ip'}, $session_id);
1025 }
1026 # msg is from a gosa-si-client
1027 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1028 ($msg, $msg_hash, $module) = &input_from_known_client($next_msg, $heap->{'remote_ip'}, $session_id);
1029 }
1030 # an error occurred
1031 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1032 # if an incoming msg could not be decrypted (maybe a wrong key), send client a ping. If the client
1033 # could not understand a msg from its server the client cause a re-registering process
1034 daemon_log("$session_id INFO cannot understand incoming msg, send 'ping'-msg to all host with ip '".$heap->{remote_ip}."' to cause a re-registering of the client if necessary", 5);
1035 my $sql_statement = "SELECT * FROM $main::known_clients_tn WHERE (hostname LIKE '".$heap->{'remote_ip'}."%')";
1036 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1037 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1038 my $host_name = $hit->{'hostname'};
1039 my $host_key = $hit->{'hostkey'};
1040 my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source> <target>$host_name</target></xml>";
1041 my $error = &send_msg_to_target($ping_msg, $host_name, $host_key, "gosa_ping", $session_id);
1042 &update_jobdb_status_for_send_msgs($ping_msg, $error);
1043 }
1044 $error++;
1045 }
1047 # add message to incoming_db
1048 if( $error == 0) {
1049 my $header = @{$msg_hash->{'header'}}[0];
1050 my $target = @{$msg_hash->{'target'}}[0];
1051 my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1052 primkey=>[],
1053 headertag=>$header,
1054 targettag=>$target,
1055 xmlmessage=>$msg,
1056 timestamp=>&get_time,
1057 module=>$module,
1058 } );
1059 if ($res != 0) {
1060 # TODO ist das mit $! so ok???
1061 #&daemon_log("$session_id ERROR: cannot add message to incoming.db: $!", 1);
1062 }
1063 }
1065 }
1068 sub next_task {
1069 my ($session, $heap) = @_[SESSION, HEAP];
1070 # if (keys( %{ $heap->{task} } ) < $max_children ) {
1071 my $task = POE::Wheel::Run->new(
1072 Program => sub { process_task($session, $heap) },
1073 StdioFilter => POE::Filter::Reference->new(),
1074 StdoutEvent => "task_result",
1075 StderrEvent => "task_debug",
1076 CloseEvent => "task_done",
1077 );
1079 $heap->{task}->{ $task->ID } = $task;
1080 # }
1081 }
1083 sub handle_task_result {
1084 my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
1085 my $client_answer = $result->{'answer'};
1086 if( $client_answer =~ s/session_id=(\d+)$// ) {
1087 my $session_id = $1;
1088 if( defined $session_id ) {
1089 my $session_reference = $kernel->ID_id_to_session($session_id);
1090 if( defined $session_reference ) {
1091 $heap = $session_reference->get_heap();
1092 }
1093 }
1095 if(exists $heap->{'client'}) {
1096 $heap->{'client'}->put($client_answer);
1097 }
1098 }
1099 $kernel->sig(CHLD => "child_reap");
1100 }
1102 sub handle_task_debug {
1103 my $result = $_[ARG0];
1104 print STDERR "$result\n";
1105 }
1107 sub handle_task_done {
1108 my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1109 delete $heap->{task}->{$task_id};
1110 }
1112 sub process_task {
1113 no strict "refs";
1114 my ($session, $heap, $input) = @_;
1115 my $session_id = $session->ID;
1116 my $error = 0;
1117 my $answer_l;
1118 my ($answer_header, @answer_target_l, $answer_source);
1119 my $client_answer = "";
1121 daemon_log("", 5);
1122 daemon_log("$session_id INFO: Incoming msg with session ID $session_id from '".$heap->{'remote_ip'}."'", 5);
1123 #daemon_log("$session_id DEBUG: Incoming msg:\n$input", 9);
1125 ##################################################
1126 # fetch first unprocessed message from incoming_db
1127 # sometimes the program is faster than sqlite, so wait until informations are present at db
1128 my $id_sql;
1129 my $id_res;
1130 my $message_id;
1131 # TODO : das hier ist sehr sehr unschön
1132 # to be tested: speed enhancement with usleep 100000???
1133 while (1) {
1134 $id_sql = "SELECT min(id) FROM $incoming_tn WHERE (NOT headertag LIKE 'answer%')";
1135 $id_res = $incoming_db->exec_statement($id_sql);
1136 $message_id = @{@$id_res[0]}[0];
1137 if (defined $message_id) { last }
1138 }
1140 # fetch new message from incoming_db
1141 my $sql = "SELECT * FROM $incoming_tn WHERE id=$message_id";
1142 my $res = $incoming_db->exec_statement($sql);
1144 # prepare all variables needed to process message
1145 my $msg = @{@$res[0]}[3];
1146 my $incoming_id = @{@$res[0]}[0];
1147 my $module = @{@$res[0]}[4];
1148 my $header = @{@$res[0]}[2];
1149 my $msg_hash = $xml->XMLin($msg, ForceArray=>1);
1151 # messages which are an answer to a still running process should not be processed here
1152 if ($header =~ /^answer_(\d+)/) {
1153 return;
1154 }
1156 # delete message from db
1157 my $delete_sql = "DELETE FROM $incoming_tn WHERE id=$incoming_id";
1158 my $delete_res = $incoming_db->exec_statement($delete_sql);
1160 ######################
1161 # process incoming msg
1162 if( $error == 0) {
1163 daemon_log("$session_id INFO: Incoming msg with header '".@{$msg_hash->{'header'}}[0].
1164 "' from '".$heap->{'remote_ip'}."'", 5);
1165 daemon_log("$session_id DEBUG: Processing module ".$module, 7);
1166 $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1168 if ( 0 < @{$answer_l} ) {
1169 my $answer_str = join("\n", @{$answer_l});
1170 daemon_log("$session_id DEBUG: $module: Got answer from module: \n".$answer_str,8);
1171 } else {
1172 daemon_log("$session_id DEBUG: $module: Got no answer from module!" ,8);
1173 }
1175 }
1176 if( !$answer_l ) { $error++ };
1178 ########
1179 # answer
1180 if( $error == 0 ) {
1182 foreach my $answer ( @{$answer_l} ) {
1183 # check outgoing msg to xml validity
1184 my $answer_hash = &check_outgoing_xml_validity($answer);
1185 if( not defined $answer_hash ) {
1186 next;
1187 }
1189 $answer_header = @{$answer_hash->{'header'}}[0];
1190 @answer_target_l = @{$answer_hash->{'target'}};
1191 $answer_source = @{$answer_hash->{'source'}}[0];
1193 # deliver msg to all targets
1194 foreach my $answer_target ( @answer_target_l ) {
1196 # targets of msg are all gosa-si-clients in known_clients_db
1197 if( $answer_target eq "*" ) {
1198 # answer is for all clients
1199 my $sql_statement= "SELECT * FROM known_clients";
1200 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1201 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1202 my $host_name = $hit->{hostname};
1203 my $host_key = $hit->{hostkey};
1204 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1205 &update_jobdb_status_for_send_msgs($answer, $error);
1206 }
1207 }
1209 # targets of msg are all gosa-si-server in known_server_db
1210 elsif( $answer_target eq "KNOWN_SERVER" ) {
1211 # answer is for all server in known_server
1212 my $sql_statement= "SELECT * FROM known_server";
1213 my $query_res = $known_server_db->select_dbentry( $sql_statement );
1214 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1215 my $host_name = $hit->{hostname};
1216 my $host_key = $hit->{hostkey};
1217 $answer =~ s/KNOWN_SERVER/$host_name/g;
1218 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1219 &update_jobdb_status_for_send_msgs($answer, $error);
1220 }
1221 }
1223 # target of msg is GOsa
1224 elsif( $answer_target eq "GOSA" ) {
1225 my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1226 my $add_on = "";
1227 if( defined $session_id ) {
1228 $add_on = ".session_id=$session_id";
1229 }
1230 # answer is for GOSA and has to returned to connected client
1231 my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1232 $client_answer = $gosa_answer.$add_on;
1233 }
1235 # target of msg is job queue at this host
1236 elsif( $answer_target eq "JOBDB") {
1237 $answer =~ /<header>(\S+)<\/header>/;
1238 my $header;
1239 if( defined $1 ) { $header = $1; }
1240 my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1241 &update_jobdb_status_for_send_msgs($answer, $error);
1242 }
1244 # target of msg is a mac address
1245 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 ) {
1246 daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients", 5);
1247 my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1248 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1249 my $found_ip_flag = 0;
1250 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1251 my $host_name = $hit->{hostname};
1252 my $host_key = $hit->{hostkey};
1253 $answer =~ s/$answer_target/$host_name/g;
1254 daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1255 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1256 &update_jobdb_status_for_send_msgs($answer, $error);
1257 $found_ip_flag++ ;
1258 }
1259 if( $found_ip_flag == 0) {
1260 daemon_log("$session_id WARNING: no host found in known_clients with mac address '$answer_target'", 3);
1261 if( $bus_activ eq "true" ) {
1262 daemon_log("$session_id INFO: try to forward msg '$answer_header' to bus '$bus_address'", 5);
1263 my $sql_statement = "SELECT * FROM known_server WHERE hostname='$bus_address'";
1264 my $query_res = $known_server_db->select_dbentry( $sql_statement );
1265 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1266 my $bus_address = $hit->{hostname};
1267 my $bus_key = $hit->{hostkey};
1268 my $error = &send_msg_to_target($answer, $bus_address, $bus_key, $answer_header, $session_id);
1269 &update_jobdb_status_for_send_msgs($answer, $error);
1270 last;
1271 }
1272 }
1274 }
1276 # answer is for one specific host
1277 } else {
1278 # get encrypt_key
1279 my $encrypt_key = &get_encrypt_key($answer_target);
1280 if( not defined $encrypt_key ) {
1281 # unknown target, forward msg to bus
1282 daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1283 if( $bus_activ eq "true" ) {
1284 daemon_log("$session_id INFO: try to forward msg '$answer_header' to bus '$bus_address'", 5);
1285 my $sql_statement = "SELECT * FROM known_server WHERE hostname='$bus_address'";
1286 my $query_res = $known_server_db->select_dbentry( $sql_statement );
1287 my $res_length = keys( %{$query_res} );
1288 if( $res_length == 0 ){
1289 daemon_log("$session_id WARNING: send '$answer_header' to '$bus_address' failed, ".
1290 "no bus found in known_server", 3);
1291 }
1292 else {
1293 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1294 my $bus_key = $hit->{hostkey};
1295 my $error = &send_msg_to_target($answer, $bus_address, $bus_key, $answer_header,$session_id );
1296 &update_jobdb_status_for_send_msgs($answer, $error);
1297 }
1298 }
1299 }
1300 next;
1301 }
1302 my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1303 &update_jobdb_status_for_send_msgs($answer, $error);
1304 }
1305 }
1306 }
1307 }
1309 my $filter = POE::Filter::Reference->new();
1310 my %result = (
1311 status => "seems ok to me",
1312 answer => $client_answer,
1313 );
1315 my $output = $filter->put( [ \%result ] );
1316 print @$output;
1319 }
1322 sub trigger_db_loop {
1323 my ($kernel) = @_ ;
1324 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1325 $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay);
1326 $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1327 $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1328 $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1329 }
1332 sub watch_for_done_jobs {
1333 my ($kernel,$heap) = @_[KERNEL, HEAP];
1335 my $sql_statement = "SELECT * FROM ".$job_queue_tn.
1336 " WHERE status='done'";
1337 my $res = $job_db->select_dbentry( $sql_statement );
1339 while( my ($id, $hit) = each %{$res} ) {
1340 my $jobdb_id = $hit->{id};
1341 my $sql_statement = "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1342 my $res = $job_db->del_dbentry($sql_statement);
1343 }
1345 $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1346 }
1349 sub watch_for_new_jobs {
1350 if($watch_for_new_jobs_in_progress == 0) {
1351 $watch_for_new_jobs_in_progress = 1;
1352 my ($kernel,$heap) = @_[KERNEL, HEAP];
1354 # check gosa job queue for jobs with executable timestamp
1355 my $timestamp = &get_time();
1356 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE status='waiting' AND (CAST (timestamp AS INTEGER)) < $timestamp ORDER BY timestamp";
1357 my $res = $job_db->exec_statement( $sql_statement );
1359 # Merge all new jobs that would do the same actions
1360 my @drops;
1361 my $hits;
1362 foreach my $hit (reverse @{$res} ) {
1363 my $macaddress= lc @{$hit}[8];
1364 my $headertag= @{$hit}[5];
1365 if(
1366 defined($hits->{$macaddress}) &&
1367 defined($hits->{$macaddress}->{$headertag}) &&
1368 defined($hits->{$macaddress}->{$headertag}[0])
1369 ) {
1370 push @drops, "DELETE FROM $job_queue_tn WHERE id = $hits->{$macaddress}->{$headertag}[0]";
1371 }
1372 $hits->{$macaddress}->{$headertag}= $hit;
1373 }
1375 # Delete new jobs with a matching job in state 'processing'
1376 foreach my $macaddress (keys %{$hits}) {
1377 foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1378 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1379 if(defined($jobdb_id)) {
1380 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND headertag='$jobdb_headertag' AND status='processing'";
1381 my $res = $job_db->exec_statement( $sql_statement );
1382 foreach my $hit (@{$res}) {
1383 push @drops, "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1384 }
1385 } else {
1386 daemon_log("J ERROR: Job without id exists for macaddress $macaddress!", 1);
1387 }
1388 }
1389 }
1391 # Commit deletion
1392 $job_db->exec_statementlist(\@drops);
1394 # Look for new jobs that could be executed
1395 foreach my $macaddress (keys %{$hits}) {
1397 # Look if there is an executing job
1398 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='processing'";
1399 my $res = $job_db->exec_statement( $sql_statement );
1401 # Skip new jobs for host if there is a processing job
1402 if(defined($res) and defined @{$res}[0]) {
1403 next;
1404 }
1406 foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1407 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1408 if(defined($jobdb_id)) {
1409 my $job_msg = @{$hits->{$macaddress}->{$jobdb_headertag}}[7];
1411 daemon_log("J DEBUG: its time to execute $job_msg", 7);
1412 my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1413 my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1415 # expect macaddress is unique!!!!!!
1416 my $target = $res_hash->{1}->{hostname};
1418 # change header
1419 $job_msg =~ s/<header>job_/<header>gosa_/;
1421 # add sqlite_id
1422 $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1424 $job_msg =~ /<header>(\S+)<\/header>/;
1425 my $header = $1 ;
1426 my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");
1428 # update status in job queue to 'processing'
1429 $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
1430 my $res = $job_db->update_dbentry($sql_statement);
1431 # TODO: abfangen ob alles in ordnung ist oder nicht, wenn nicht error schmeißen
1433 # We don't want parallel processing
1434 last;
1435 }
1436 }
1437 }
1439 $watch_for_new_jobs_in_progress = 0;
1440 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1441 }
1442 }
1445 sub watch_for_new_messages {
1446 my ($kernel,$heap) = @_[KERNEL, HEAP];
1447 my @coll_user_msg; # collection list of outgoing messages
1449 # check messaging_db for new incoming messages with executable timestamp
1450 my $timestamp = &get_time();
1451 my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS INTEGER))<$timestamp AND flag='n' AND direction='in' )";
1452 my $res = $messaging_db->exec_statement( $sql_statement );
1453 foreach my $hit (@{$res}) {
1455 # create outgoing messages
1456 my $message_to = @{$hit}[3];
1457 # translate message_to to plain login name
1458 my @message_to_l = split(/,/, $message_to);
1459 my %receiver_h;
1460 foreach my $receiver (@message_to_l) {
1461 if ($receiver =~ /^u_([\s\S]*)$/) {
1462 $receiver_h{$1} = 0;
1463 } elsif ($receiver =~ /^g_([\s\S]*)$/) {
1464 my $group_name = $1;
1465 # fetch all group members from ldap and add them to receiver hash
1466 my $ldap_handle = &get_ldap_handle($session_id);
1467 if (defined $ldap_handle) {
1468 my $mesg = $ldap_handle->search(
1469 base => $ldap_base,
1470 scope => 'sub',
1471 attrs => ['memberUid'],
1472 filter => "cn=$group_name";
1473 );
1474 if ($mesg->count) {
1475 my @entries = $mesg->entries;
1476 foreach my $entry (@entries) {
1477 my @receivers= $entry->get_value("memberUid");
1478 foreach my $receiver (@receivers) {
1479 $receiver_h{$1} = 0;
1480 }
1481 }
1482 }
1483 # translating errors ?
1484 if ($mesg->code) {
1485 daemon_log("$session_id ERROR: unable to translate group '$group_name' to user list for message delivery: $mesg->error", 1);
1486 }
1487 # ldap handle error ?
1488 } else {
1489 daemon_log("$session_id ERROR: unable to translate group '$group_name' to user list for message delivery: no ldap handle available", 1);
1490 }
1491 } else {
1492 my $sbjct = &encode_base64(@{$hit}[1]);
1493 my $msg = &encode_base64(@{$hit}[7]);
1494 &daemon_log("M WARNING: unknown receiver '$receiver' for a user-message '$sbjct - $msg'", 3);
1495 }
1496 }
1497 my @receiver_l = keys(%receiver_h);
1499 my $message_id = @{$hit}[0];
1501 #add each outgoing msg to messaging_db
1502 my $receiver;
1503 foreach $receiver (@receiver_l) {
1504 my $sql_statement = "INSERT INTO $messaging_tn (id, subject, message_from, message_to, flag, direction, delivery_time, message, timestamp) ".
1505 "VALUES ('".
1506 $message_id."', '". # id
1507 @{$hit}[1]."', '". # subject
1508 @{$hit}[2]."', '". # message_from
1509 $receiver."', '". # message_to
1510 "none"."', '". # flag
1511 "out"."', '". # direction
1512 @{$hit}[6]."', '". # delivery_time
1513 @{$hit}[7]."', '". # message
1514 $timestamp."'". # timestamp
1515 ")";
1516 &daemon_log("M DEBUG: $sql_statement", 1);
1517 my $res = $messaging_db->exec_statement($sql_statement);
1518 &daemon_log("M INFO: message '".@{$hit}[0]."' is prepared for delivery to receiver '$receiver'", 5);
1519 }
1521 # set incoming message to flag d=deliverd
1522 $sql_statement = "UPDATE $messaging_tn SET flag='p' WHERE id='$message_id'";
1523 &daemon_log("M DEBUG: $sql_statement", 7);
1524 $res = $messaging_db->update_dbentry($sql_statement);
1525 &daemon_log("M INFO: message '$message_id' is set to flag 'p' (processed)", 5);
1526 }
1528 $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1529 return;
1530 }
1532 sub watch_for_delivery_messages {
1533 my ($kernel, $heap) = @_[KERNEL, HEAP];
1535 # select outgoing messages
1536 my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( flag='p' AND direction='out' )";
1537 #&daemon_log("0 DEBUG: $sql", 7);
1538 my $res = $messaging_db->exec_statement( $sql_statement );
1540 # build out msg for each usr
1541 foreach my $hit (@{$res}) {
1542 my $receiver = @{$hit}[3];
1543 my $msg_id = @{$hit}[0];
1544 my $subject = @{$hit}[1];
1545 my $message = @{$hit}[7];
1547 # resolve usr -> host where usr is logged in
1548 my $sql = "SELECT * FROM $login_users_tn WHERE (user='$receiver')";
1549 #&daemon_log("0 DEBUG: $sql", 7);
1550 my $res = $login_users_db->exec_statement($sql);
1552 # reciver is logged in nowhere
1553 if (not ref(@$res[0]) eq "ARRAY") { next; }
1555 my $send_succeed = 0;
1556 foreach my $hit (@$res) {
1557 my $receiver_host = @$hit[0];
1558 &daemon_log("M DEBUG: user '$receiver' is logged in at host '$receiver_host'", 7);
1560 # fetch key to encrypt msg propperly for usr/host
1561 my $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$receiver_host')";
1562 &daemon_log("0 DEBUG: $sql", 7);
1563 my $res = $known_clients_db->exec_statement($sql);
1565 # host is already down
1566 if (not ref(@$res[0]) eq "ARRAY") { next; }
1568 # host is on
1569 my $receiver_key = @{@{$res}[0]}[2];
1570 my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
1571 my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data );
1572 my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0);
1573 if ($error == 0 ) {
1574 $send_succeed++ ;
1575 }
1576 }
1578 if ($send_succeed) {
1579 # set outgoing msg at db to deliverd
1580 my $sql = "UPDATE $messaging_tn SET flag='d' WHERE (id='$msg_id' AND direction='out' AND message_to='$receiver')";
1581 &daemon_log("0 DEBUG: $sql", 7);
1582 my $res = $messaging_db->exec_statement($sql);
1583 }
1584 }
1586 $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1587 return;
1588 }
1591 sub watch_for_done_messages {
1592 my ($kernel,$heap) = @_[KERNEL, HEAP];
1594 my $sql = "SELECT * FROM $messaging_tn WHERE (flag='p' AND direction='in')";
1595 #&daemon_log("0 DEBUG: $sql", 7);
1596 my $res = $messaging_db->exec_statement($sql);
1598 foreach my $hit (@{$res}) {
1599 my $msg_id = @{$hit}[0];
1601 my $sql = "SELECT * FROM $messaging_tn WHERE (id='$msg_id' AND direction='out' AND (NOT flag='s'))";
1602 #&daemon_log("0 DEBUG: $sql", 7);
1603 my $res = $messaging_db->exec_statement($sql);
1605 # not all usr msgs have been seen till now
1606 if ( ref(@$res[0]) eq "ARRAY") { next; }
1608 $sql = "DELETE FROM $messaging_tn WHERE (id='$msg_id')";
1609 #&daemon_log("0 DEBUG: $sql", 7);
1610 $res = $messaging_db->exec_statement($sql);
1612 }
1614 $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1615 return;
1616 }
1619 sub get_ldap_handle {
1620 my ($session_id) = @_;
1621 my $heap;
1622 my $ldap_handle;
1624 if (not defined $session_id ) { $session_id = 0 };
1625 if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
1627 if ($session_id == 0) {
1628 daemon_log("$session_id DEBUG: get_ldap_handle invoked without a session_id, create a new ldap_handle", 7);
1629 $ldap_handle = Net::LDAP->new( $ldap_uri );
1630 $ldap_handle->bind($ldap_admin_dn, password => $ldap_admin_password);
1632 } else {
1633 my $session_reference = $global_kernel->ID_id_to_session($session_id);
1634 if( defined $session_reference ) {
1635 $heap = $session_reference->get_heap();
1636 }
1638 if (not defined $heap) {
1639 daemon_log("$session_id DEBUG: cannot get heap for session_id '$session_id'", 7);
1640 return;
1641 }
1643 # TODO: This "if" is nonsense, because it doesn't prove that the
1644 # used handle is still valid - or if we've to reconnect...
1645 #if (not exists $heap->{ldap_handle}) {
1646 $ldap_handle = Net::LDAP->new( $ldap_uri );
1647 $ldap_handle->bind($ldap_admin_dn, password => $ldap_admin_password);
1648 $heap->{ldap_handle} = $ldap_handle;
1649 #}
1650 }
1651 return $ldap_handle;
1652 }
1655 sub change_fai_state {
1656 my ($st, $targets, $session_id) = @_;
1657 $session_id = 0 if not defined $session_id;
1658 # Set FAI state to localboot
1659 my %mapActions= (
1660 reboot => '',
1661 update => 'softupdate',
1662 localboot => 'localboot',
1663 reinstall => 'install',
1664 rescan => '',
1665 wake => '',
1666 memcheck => 'memcheck',
1667 sysinfo => 'sysinfo',
1668 install => 'install',
1669 );
1671 # Return if this is unknown
1672 if (!exists $mapActions{ $st }){
1673 daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1);
1674 return;
1675 }
1677 my $state= $mapActions{ $st };
1679 my $ldap_handle = &get_ldap_handle($session_id);
1680 if( defined($ldap_handle) ) {
1682 # Build search filter for hosts
1683 my $search= "(&(objectClass=GOhard)";
1684 foreach (@{$targets}){
1685 $search.= "(macAddress=$_)";
1686 }
1687 $search.= ")";
1689 # If there's any host inside of the search string, procress them
1690 if (!($search =~ /macAddress/)){
1691 daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);
1692 return;
1693 }
1695 # Perform search for Unit Tag
1696 my $mesg = $ldap_handle->search(
1697 base => $ldap_base,
1698 scope => 'sub',
1699 attrs => ['dn', 'FAIstate', 'objectClass'],
1700 filter => "$search"
1701 );
1703 if ($mesg->count) {
1704 my @entries = $mesg->entries;
1705 foreach my $entry (@entries) {
1706 # Only modify entry if it is not set to '$state'
1707 if ($entry->get_value("FAIstate") ne "$state"){
1708 daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
1709 my $result;
1710 my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
1711 if (exists $tmp{'FAIobject'}){
1712 if ($state eq ''){
1713 $result= $ldap_handle->modify($entry->dn, changes => [
1714 delete => [ FAIstate => [] ] ]);
1715 } else {
1716 $result= $ldap_handle->modify($entry->dn, changes => [
1717 replace => [ FAIstate => $state ] ]);
1718 }
1719 } elsif ($state ne ''){
1720 $result= $ldap_handle->modify($entry->dn, changes => [
1721 add => [ objectClass => 'FAIobject' ],
1722 add => [ FAIstate => $state ] ]);
1723 }
1725 # Errors?
1726 if ($result->code){
1727 daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
1728 }
1729 } else {
1730 daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 7);
1731 }
1732 }
1733 }
1734 # if no ldap handle defined
1735 } else {
1736 daemon_log("$session_id ERROR: no LDAP handle defined for update FAIstate", 1);
1737 }
1739 }
1742 sub change_goto_state {
1743 my ($st, $targets, $session_id) = @_;
1744 $session_id = 0 if not defined $session_id;
1746 # Switch on or off?
1747 my $state= $st eq 'active' ? 'active': 'locked';
1749 my $ldap_handle = &get_ldap_handle($session_id);
1750 if( defined($ldap_handle) ) {
1752 # Build search filter for hosts
1753 my $search= "(&(objectClass=GOhard)";
1754 foreach (@{$targets}){
1755 $search.= "(macAddress=$_)";
1756 }
1757 $search.= ")";
1759 # If there's any host inside of the search string, procress them
1760 if (!($search =~ /macAddress/)){
1761 return;
1762 }
1764 # Perform search for Unit Tag
1765 my $mesg = $ldap_handle->search(
1766 base => $ldap_base,
1767 scope => 'sub',
1768 attrs => ['dn', 'gotoMode'],
1769 filter => "$search"
1770 );
1772 if ($mesg->count) {
1773 my @entries = $mesg->entries;
1774 foreach my $entry (@entries) {
1776 # Only modify entry if it is not set to '$state'
1777 if ($entry->get_value("gotoMode") ne $state){
1779 daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
1780 my $result;
1781 $result= $ldap_handle->modify($entry->dn, changes => [
1782 replace => [ gotoMode => $state ] ]);
1784 # Errors?
1785 if ($result->code){
1786 &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
1787 }
1789 }
1790 }
1791 }
1793 }
1794 }
1797 sub run_create_fai_server_db {
1798 my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
1799 my $session_id = $session->ID;
1800 my $task = POE::Wheel::Run->new(
1801 Program => sub { &create_fai_server_db($table_name,$kernel, undef, $session_id) },
1802 StdoutEvent => "session_run_result",
1803 StderrEvent => "session_run_debug",
1804 CloseEvent => "session_run_done",
1805 );
1807 $heap->{task}->{ $task->ID } = $task;
1808 return;
1809 }
1812 sub create_fai_server_db {
1813 my ($table_name, $kernel, $dont_create_packages_list, $session_id) = @_;
1814 my $result;
1816 if (not defined $session_id) { $session_id = 0; }
1817 my $ldap_handle = &get_ldap_handle();
1818 if(defined($ldap_handle)) {
1819 daemon_log("$session_id INFO: create_fai_server_db: start", 5);
1820 my $mesg= $ldap_handle->search(
1821 base => $ldap_base,
1822 scope => 'sub',
1823 attrs => ['FAIrepository', 'gosaUnitTag'],
1824 filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
1825 );
1826 if($mesg->{'resultCode'} == 0 &&
1827 $mesg->count != 0) {
1828 foreach my $entry (@{$mesg->{entries}}) {
1829 if($entry->exists('FAIrepository')) {
1830 # Add an entry for each Repository configured for server
1831 foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
1832 my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
1833 my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
1834 $result= $fai_server_db->add_dbentry( {
1835 table => $table_name,
1836 primkey => ['server', 'release', 'tag'],
1837 server => $tmp_url,
1838 release => $tmp_release,
1839 sections => $tmp_sections,
1840 tag => (length($tmp_tag)>0)?$tmp_tag:"",
1841 } );
1842 }
1843 }
1844 }
1845 }
1846 daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
1848 # TODO: Find a way to post the 'create_packages_list_db' event
1849 if(not defined($dont_create_packages_list)) {
1850 &create_packages_list_db(undef, undef, $session_id);
1851 }
1852 }
1854 $ldap_handle->disconnect;
1855 return $result;
1856 }
1859 sub run_create_fai_release_db {
1860 my ($session, $heap, $table_name) = @_[SESSION, HEAP, ARG0];
1861 my $session_id = $session->ID;
1862 my $task = POE::Wheel::Run->new(
1863 Program => sub { &create_fai_release_db($table_name, $session_id) },
1864 StdoutEvent => "session_run_result",
1865 StderrEvent => "session_run_debug",
1866 CloseEvent => "session_run_done",
1867 );
1869 $heap->{task}->{ $task->ID } = $task;
1870 return;
1871 }
1874 sub create_fai_release_db {
1875 my ($table_name, $session_id) = @_;
1876 my $result;
1878 # used for logging
1879 if (not defined $session_id) { $session_id = 0; }
1881 my $ldap_handle = &get_ldap_handle();
1882 if(defined($ldap_handle)) {
1883 daemon_log("$session_id INFO: create_fai_release_db: start",5);
1884 my $mesg= $ldap_handle->search(
1885 base => $ldap_base,
1886 scope => 'sub',
1887 attrs => [],
1888 filter => "(&(objectClass=organizationalUnit)(ou=fai))",
1889 );
1890 if($mesg->{'resultCode'} == 0 &&
1891 $mesg->count != 0) {
1892 # Walk through all possible FAI container ou's
1893 my @sql_list;
1894 my $timestamp= &get_time();
1895 foreach my $ou (@{$mesg->{entries}}) {
1896 my $tmp_classes= resolve_fai_classes($ou->dn, $ldap_handle, $session_id);
1897 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
1898 my @tmp_array=get_fai_release_entries($tmp_classes);
1899 if(@tmp_array) {
1900 foreach my $entry (@tmp_array) {
1901 if(defined($entry) && ref($entry) eq 'HASH') {
1902 my $sql=
1903 "INSERT INTO $table_name "
1904 ."(timestamp, release, class, type, state) VALUES ("
1905 .$timestamp.","
1906 ."'".$entry->{'release'}."',"
1907 ."'".$entry->{'class'}."',"
1908 ."'".$entry->{'type'}."',"
1909 ."'".$entry->{'state'}."')";
1910 push @sql_list, $sql;
1911 }
1912 }
1913 }
1914 }
1915 }
1917 daemon_log("$session_id DEBUG: Inserting ".scalar @sql_list." entries to DB",8);
1918 if(@sql_list) {
1919 unshift @sql_list, "VACUUM";
1920 unshift @sql_list, "DELETE FROM $table_name";
1921 $fai_release_db->exec_statementlist(\@sql_list);
1922 }
1923 daemon_log("$session_id DEBUG: Done with inserting",7);
1924 }
1925 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
1926 }
1927 $ldap_handle->disconnect;
1928 return $result;
1929 }
1931 sub get_fai_types {
1932 my $tmp_classes = shift || return undef;
1933 my @result;
1935 foreach my $type(keys %{$tmp_classes}) {
1936 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
1937 my $entry = {
1938 type => $type,
1939 state => $tmp_classes->{$type}[0],
1940 };
1941 push @result, $entry;
1942 }
1943 }
1945 return @result;
1946 }
1948 sub get_fai_state {
1949 my $result = "";
1950 my $tmp_classes = shift || return $result;
1952 foreach my $type(keys %{$tmp_classes}) {
1953 if(defined($tmp_classes->{$type}[0])) {
1954 $result = $tmp_classes->{$type}[0];
1956 # State is equal for all types in class
1957 last;
1958 }
1959 }
1961 return $result;
1962 }
1964 sub resolve_fai_classes {
1965 my ($fai_base, $ldap_handle, $session_id) = @_;
1966 if (not defined $session_id) { $session_id = 0; }
1967 my $result;
1968 my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
1969 my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
1970 my $fai_classes;
1972 daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base",7);
1973 my $mesg= $ldap_handle->search(
1974 base => $fai_base,
1975 scope => 'sub',
1976 attrs => ['cn','objectClass','FAIstate'],
1977 filter => $fai_filter,
1978 );
1979 daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries",7);
1981 if($mesg->{'resultCode'} == 0 &&
1982 $mesg->count != 0) {
1983 foreach my $entry (@{$mesg->{entries}}) {
1984 if($entry->exists('cn')) {
1985 my $tmp_dn= $entry->dn();
1987 # Skip classname and ou dn parts for class
1988 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?),$fai_base$/;
1990 # Skip classes without releases
1991 if((!defined($tmp_release)) || length($tmp_release)==0) {
1992 next;
1993 }
1995 my $tmp_cn= $entry->get_value('cn');
1996 my $tmp_state= $entry->get_value('FAIstate');
1998 my $tmp_type;
1999 # Get FAI type
2000 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
2001 if(grep $_ eq $oclass, @possible_fai_classes) {
2002 $tmp_type= $oclass;
2003 last;
2004 }
2005 }
2007 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2008 # A Subrelease
2009 my @sub_releases = split(/,/, $tmp_release);
2011 # Walk through subreleases and build hash tree
2012 my $hash;
2013 while(my $tmp_sub_release = pop @sub_releases) {
2014 $hash .= "\{'$tmp_sub_release'\}->";
2015 }
2016 eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
2017 } else {
2018 # A branch, no subrelease
2019 push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
2020 }
2021 } elsif (!$entry->exists('cn')) {
2022 my $tmp_dn= $entry->dn();
2023 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?),$fai_base$/;
2025 # Skip classes without releases
2026 if((!defined($tmp_release)) || length($tmp_release)==0) {
2027 next;
2028 }
2030 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2031 # A Subrelease
2032 my @sub_releases= split(/,/, $tmp_release);
2034 # Walk through subreleases and build hash tree
2035 my $hash;
2036 while(my $tmp_sub_release = pop @sub_releases) {
2037 $hash .= "\{'$tmp_sub_release'\}->";
2038 }
2039 # Remove the last two characters
2040 chop($hash);
2041 chop($hash);
2043 eval('$fai_classes->'.$hash.'= {}');
2044 } else {
2045 # A branch, no subrelease
2046 if(!exists($fai_classes->{$tmp_release})) {
2047 $fai_classes->{$tmp_release} = {};
2048 }
2049 }
2050 }
2051 }
2053 # The hash is complete, now we can honor the copy-on-write based missing entries
2054 foreach my $release (keys %$fai_classes) {
2055 $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
2056 }
2057 }
2058 return $result;
2059 }
2061 sub apply_fai_inheritance {
2062 my $fai_classes = shift || return {};
2063 my $tmp_classes;
2065 # Get the classes from the branch
2066 foreach my $class (keys %{$fai_classes}) {
2067 # Skip subreleases
2068 if($class =~ /^ou=.*$/) {
2069 next;
2070 } else {
2071 $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
2072 }
2073 }
2075 # Apply to each subrelease
2076 foreach my $subrelease (keys %{$fai_classes}) {
2077 if($subrelease =~ /ou=/) {
2078 foreach my $tmp_class (keys %{$tmp_classes}) {
2079 if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
2080 $fai_classes->{$subrelease}->{$tmp_class} =
2081 deep_copy($tmp_classes->{$tmp_class});
2082 } else {
2083 foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
2084 if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
2085 $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
2086 deep_copy($tmp_classes->{$tmp_class}->{$type});
2087 }
2088 }
2089 }
2090 }
2091 }
2092 }
2094 # Find subreleases in deeper levels
2095 foreach my $subrelease (keys %{$fai_classes}) {
2096 if($subrelease =~ /ou=/) {
2097 foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
2098 if($subsubrelease =~ /ou=/) {
2099 apply_fai_inheritance($fai_classes->{$subrelease});
2100 }
2101 }
2102 }
2103 }
2105 return $fai_classes;
2106 }
2108 sub get_fai_release_entries {
2109 my $tmp_classes = shift || return;
2110 my $parent = shift || "";
2111 my @result = shift || ();
2113 foreach my $entry (keys %{$tmp_classes}) {
2114 if(defined($entry)) {
2115 if($entry =~ /^ou=.*$/) {
2116 my $release_name = $entry;
2117 $release_name =~ s/ou=//g;
2118 if(length($parent)>0) {
2119 $release_name = $parent."/".$release_name;
2120 }
2121 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2122 foreach my $bufentry(@bufentries) {
2123 push @result, $bufentry;
2124 }
2125 } else {
2126 my @types = get_fai_types($tmp_classes->{$entry});
2127 foreach my $type (@types) {
2128 push @result,
2129 {
2130 'class' => $entry,
2131 'type' => $type->{'type'},
2132 'release' => $parent,
2133 'state' => $type->{'state'},
2134 };
2135 }
2136 }
2137 }
2138 }
2140 return @result;
2141 }
2143 sub deep_copy {
2144 my $this = shift;
2145 if (not ref $this) {
2146 $this;
2147 } elsif (ref $this eq "ARRAY") {
2148 [map deep_copy($_), @$this];
2149 } elsif (ref $this eq "HASH") {
2150 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2151 } else { die "what type is $_?" }
2152 }
2155 sub session_run_result {
2156 my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];
2157 $kernel->sig(CHLD => "child_reap");
2158 }
2160 sub session_run_debug {
2161 my $result = $_[ARG0];
2162 print STDERR "$result\n";
2163 }
2165 sub session_run_done {
2166 my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2167 delete $heap->{task}->{$task_id};
2168 }
2171 sub create_sources_list {
2172 my $session_id = shift;
2173 my $ldap_handle = &main::get_ldap_handle;
2174 my $result="/tmp/gosa_si_tmp_sources_list";
2176 # Remove old file
2177 if(stat($result)) {
2178 unlink($result);
2179 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7);
2180 }
2182 my $fh;
2183 open($fh, ">$result");
2184 if (not defined $fh) {
2185 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7);
2186 return undef;
2187 }
2188 if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2189 my $mesg=$ldap_handle->search(
2190 base => $main::ldap_server_dn,
2191 scope => 'base',
2192 attrs => 'FAIrepository',
2193 filter => 'objectClass=FAIrepositoryServer'
2194 );
2195 if($mesg->count) {
2196 foreach my $entry(@{$mesg->{'entries'}}) {
2197 foreach my $value(@{$entry->get_value('FAIrepository', asref => 1)}) {
2198 my ($server, $tag, $release, $sections)= split /\|/, $value;
2199 my $line = "deb $server $release";
2200 $sections =~ s/,/ /g;
2201 $line.= " $sections";
2202 print $fh $line."\n";
2203 }
2204 }
2205 }
2206 } else {
2207 if (defined $main::ldap_server_dn){
2208 &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1);
2209 } else {
2210 &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2211 }
2212 }
2213 close($fh);
2215 return $result;
2216 }
2219 sub run_create_packages_list_db {
2220 my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2221 my $session_id = $session->ID;
2223 my $task = POE::Wheel::Run->new(
2224 Priority => +20,
2225 Program => sub {&create_packages_list_db(undef, undef, $session_id)},
2226 StdoutEvent => "session_run_result",
2227 StderrEvent => "session_run_debug",
2228 CloseEvent => "session_run_done",
2229 );
2230 $heap->{task}->{ $task->ID } = $task;
2231 }
2234 sub create_packages_list_db {
2235 my ($ldap_handle, $sources_file, $session_id) = @_;
2237 # it should not be possible to trigger a recreation of packages_list_db
2238 # while packages_list_db is under construction, so set flag packages_list_under_construction
2239 # which is tested befor recreation can be started
2240 if (-r $packages_list_under_construction) {
2241 daemon_log("$session_id WARNING: packages_list_db is right now under construction, please wait until this process is finished", 3);
2242 return;
2243 } else {
2244 daemon_log("$session_id INFO: create_packages_list_db: start", 5);
2245 # set packages_list_under_construction to true
2246 system("touch $packages_list_under_construction");
2247 @packages_list_statements=();
2248 }
2250 if (not defined $session_id) { $session_id = 0; }
2251 if (not defined $ldap_handle) {
2252 $ldap_handle= &get_ldap_handle();
2254 if (not defined $ldap_handle) {
2255 daemon_log("$session_id ERROR: no ldap_handle available to create_packages_list_db", 1);
2256 unlink($packages_list_under_construction);
2257 return;
2258 }
2259 }
2260 if (not defined $sources_file) {
2261 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5);
2262 $sources_file = &create_sources_list($session_id);
2263 }
2265 if (not defined $sources_file) {
2266 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1);
2267 unlink($packages_list_under_construction);
2268 return;
2269 }
2271 my $line;
2273 open(CONFIG, "<$sources_file") or do {
2274 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2275 unlink($packages_list_under_construction);
2276 return;
2277 };
2279 # Read lines
2280 while ($line = <CONFIG>){
2281 # Unify
2282 chop($line);
2283 $line =~ s/^\s+//;
2284 $line =~ s/^\s+/ /;
2286 # Strip comments
2287 $line =~ s/#.*$//g;
2289 # Skip empty lines
2290 if ($line =~ /^\s*$/){
2291 next;
2292 }
2294 # Interpret deb line
2295 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2296 my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2297 my $section;
2298 foreach $section (split(' ', $sections)){
2299 &parse_package_info( $baseurl, $dist, $section, $session_id );
2300 }
2301 }
2302 }
2304 close (CONFIG);
2306 find(\&cleanup_and_extract, keys( %repo_dirs ));
2307 &main::strip_packages_list_statements();
2308 unshift @packages_list_statements, "VACUUM";
2309 $packages_list_db->exec_statementlist(\@packages_list_statements);
2310 unlink($packages_list_under_construction);
2311 daemon_log("$session_id INFO: create_packages_list_db: finished", 5);
2312 return;
2313 }
2315 # This function should do some intensive task to minimize the db-traffic
2316 sub strip_packages_list_statements {
2317 my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2318 my @new_statement_list=();
2319 my $hash;
2320 my $insert_hash;
2321 my $update_hash;
2322 my $delete_hash;
2323 my $local_timestamp=get_time();
2325 foreach my $existing_entry (@existing_entries) {
2326 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
2327 }
2329 foreach my $statement (@packages_list_statements) {
2330 if($statement =~ /^INSERT/i) {
2331 # Assign the values from the insert statement
2332 my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~
2333 /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
2334 if(exists($hash->{$distribution}->{$package}->{$version})) {
2335 # If section or description has changed, update the DB
2336 if(
2337 (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or
2338 (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
2339 ) {
2340 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
2341 }
2342 } else {
2343 # Insert a non-existing entry to db
2344 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2345 }
2346 } elsif ($statement =~ /^UPDATE/i) {
2347 my ($template,$package,$version) = ($1,$2,$3) if $statement =~
2348 /^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;
2349 foreach my $distribution (keys %{$hash}) {
2350 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
2351 # update the insertion hash to execute only one query per package (insert instead insert+update)
2352 @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
2353 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
2354 if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
2355 my $section;
2356 my $description;
2357 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
2358 length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
2359 $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
2360 }
2361 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2362 $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
2363 }
2364 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2365 }
2366 }
2367 }
2368 }
2369 }
2371 # TODO: Check for orphaned entries
2373 # unroll the insert_hash
2374 foreach my $distribution (keys %{$insert_hash}) {
2375 foreach my $package (keys %{$insert_hash->{$distribution}}) {
2376 foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
2377 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
2378 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
2379 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
2380 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
2381 ."'$local_timestamp')";
2382 }
2383 }
2384 }
2386 # unroll the update hash
2387 foreach my $distribution (keys %{$update_hash}) {
2388 foreach my $package (keys %{$update_hash->{$distribution}}) {
2389 foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
2390 my $set = "";
2391 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
2392 $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
2393 }
2394 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2395 $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
2396 }
2397 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
2398 $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
2399 }
2400 if(defined($set) and length($set) > 0) {
2401 $set .= "timestamp = '$local_timestamp'";
2402 } else {
2403 next;
2404 }
2405 push @new_statement_list,
2406 "UPDATE $main::packages_list_tn SET $set WHERE"
2407 ." distribution = '$distribution'"
2408 ." AND package = '$package'"
2409 ." AND version = '$version'";
2410 }
2411 }
2412 }
2414 @packages_list_statements = @new_statement_list;
2415 }
2418 sub parse_package_info {
2419 my ($baseurl, $dist, $section, $session_id)= @_;
2420 my ($package);
2421 if (not defined $session_id) { $session_id = 0; }
2422 my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
2423 $repo_dirs{ "${repo_path}/pool" } = 1;
2425 foreach $package ("Packages.gz"){
2426 daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
2427 get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
2428 parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
2429 }
2431 }
2434 sub get_package {
2435 my ($url, $dest, $session_id)= @_;
2436 if (not defined $session_id) { $session_id = 0; }
2438 my $tpath = dirname($dest);
2439 -d "$tpath" || mkpath "$tpath";
2441 # This is ugly, but I've no time to take a look at "how it works in perl"
2442 if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
2443 system("gunzip -cd '$dest' > '$dest.in'");
2444 daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 5);
2445 unlink($dest);
2446 daemon_log("$session_id DEBUG: delete file '$dest'", 5);
2447 } else {
2448 daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' failed!", 1);
2449 }
2450 return 0;
2451 }
2454 sub parse_package {
2455 my ($path, $dist, $srv_path, $session_id)= @_;
2456 if (not defined $session_id) { $session_id = 0;}
2457 my ($package, $version, $section, $description);
2458 my $PACKAGES;
2459 my $timestamp = &get_time();
2461 if(not stat("$path.in")) {
2462 daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
2463 return;
2464 }
2466 open($PACKAGES, "<$path.in");
2467 if(not defined($PACKAGES)) {
2468 daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1);
2469 return;
2470 }
2472 # Read lines
2473 while (<$PACKAGES>){
2474 my $line = $_;
2475 # Unify
2476 chop($line);
2478 # Use empty lines as a trigger
2479 if ($line =~ /^\s*$/){
2480 my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
2481 push(@packages_list_statements, $sql);
2482 $package = "none";
2483 $version = "none";
2484 $section = "none";
2485 $description = "none";
2486 next;
2487 }
2489 # Trigger for package name
2490 if ($line =~ /^Package:\s/){
2491 ($package)= ($line =~ /^Package: (.*)$/);
2492 next;
2493 }
2495 # Trigger for version
2496 if ($line =~ /^Version:\s/){
2497 ($version)= ($line =~ /^Version: (.*)$/);
2498 next;
2499 }
2501 # Trigger for description
2502 if ($line =~ /^Description:\s/){
2503 ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
2504 next;
2505 }
2507 # Trigger for section
2508 if ($line =~ /^Section:\s/){
2509 ($section)= ($line =~ /^Section: (.*)$/);
2510 next;
2511 }
2513 # Trigger for filename
2514 if ($line =~ /^Filename:\s/){
2515 my ($filename) = ($line =~ /^Filename: (.*)$/);
2516 store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
2517 next;
2518 }
2519 }
2521 close( $PACKAGES );
2522 unlink( "$path.in" );
2523 &main::daemon_log("$session_id DEBUG: unlink '$path.in'", 1);
2524 }
2527 sub store_fileinfo {
2528 my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
2530 my %fileinfo = (
2531 'package' => $package,
2532 'dist' => $dist,
2533 'version' => $vers,
2534 );
2536 $repo_files{ "${srvdir}/$file" } = \%fileinfo;
2537 }
2540 sub cleanup_and_extract {
2541 my $fileinfo = $repo_files{ $File::Find::name };
2543 if( defined $fileinfo ) {
2545 my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
2546 my $sql;
2547 my $package = $fileinfo->{ 'package' };
2548 my $newver = $fileinfo->{ 'version' };
2550 mkpath($dir);
2551 system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
2553 if( -f "$dir/DEBIAN/templates" ) {
2555 daemon_log("DEBUG: Found debconf templates in '$package' - $newver", 5);
2557 my $tmpl= "";
2558 {
2559 local $/=undef;
2560 open FILE, "$dir/DEBIAN/templates";
2561 $tmpl = &encode_base64(<FILE>);
2562 close FILE;
2563 }
2564 rmtree("$dir/DEBIAN/templates");
2566 $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
2567 push @packages_list_statements, $sql;
2568 }
2569 }
2571 return;
2572 }
2575 #==== MAIN = main ==============================================================
2576 # parse commandline options
2577 Getopt::Long::Configure( "bundling" );
2578 GetOptions("h|help" => \&usage,
2579 "c|config=s" => \$cfg_file,
2580 "f|foreground" => \$foreground,
2581 "v|verbose+" => \$verbose,
2582 "no-bus+" => \$no_bus,
2583 "no-arp+" => \$no_arp,
2584 );
2586 # read and set config parameters
2587 &check_cmdline_param ;
2588 &read_configfile;
2589 &check_pid;
2591 $SIG{CHLD} = 'IGNORE';
2593 # forward error messages to logfile
2594 if( ! $foreground ) {
2595 open( STDIN, '+>/dev/null' );
2596 open( STDOUT, '+>&STDIN' );
2597 open( STDERR, '+>&STDIN' );
2598 }
2600 # Just fork, if we are not in foreground mode
2601 if( ! $foreground ) {
2602 chdir '/' or die "Can't chdir to /: $!";
2603 $pid = fork;
2604 setsid or die "Can't start a new session: $!";
2605 umask 0;
2606 } else {
2607 $pid = $$;
2608 }
2610 # Do something useful - put our PID into the pid_file
2611 if( 0 != $pid ) {
2612 open( LOCK_FILE, ">$pid_file" );
2613 print LOCK_FILE "$pid\n";
2614 close( LOCK_FILE );
2615 if( !$foreground ) {
2616 exit( 0 )
2617 };
2618 }
2620 # parse head url and revision from svn
2621 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
2622 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
2623 $server_headURL = defined $1 ? $1 : 'unknown' ;
2624 $server_revision = defined $2 ? $2 : 'unknown' ;
2625 if ($server_headURL =~ /\/tag\// ||
2626 $server_headURL =~ /\/branches\// ) {
2627 $server_status = "stable";
2628 } else {
2629 $server_status = "developmental" ;
2630 }
2633 daemon_log(" ", 1);
2634 daemon_log("$0 started!", 1);
2635 daemon_log("status: $server_status", 1);
2636 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1);
2638 if ($no_bus > 0) {
2639 $bus_activ = "false"
2640 }
2642 # connect to incoming_db
2643 unlink($incoming_file_name);
2644 $incoming_db = GOSA::DBsqlite->new($incoming_file_name);
2645 $incoming_db->create_table($incoming_tn, \@incoming_col_names);
2647 # connect to gosa-si job queue
2648 $job_db = GOSA::DBsqlite->new($job_queue_file_name);
2649 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
2651 # connect to known_clients_db
2652 $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
2653 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
2655 # connect to known_server_db
2656 $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
2657 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
2659 # connect to login_usr_db
2660 $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
2661 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
2663 # connect to fai_server_db and fai_release_db
2664 unlink($fai_server_file_name);
2665 $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
2666 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
2668 unlink($fai_release_file_name);
2669 $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
2670 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
2672 # connect to packages_list_db
2673 #unlink($packages_list_file_name);
2674 unlink($packages_list_under_construction);
2675 $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
2676 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
2678 # connect to messaging_db
2679 $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
2680 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
2683 # create xml object used for en/decrypting
2684 $xml = new XML::Simple();
2686 # create socket for incoming xml messages
2688 POE::Component::Server::TCP->new(
2689 Port => $server_port,
2690 ClientInput => sub {
2691 my ($kernel, $input) = @_[KERNEL, ARG0];
2692 push(@tasks, $input);
2693 push(@msgs_to_decrypt, $input);
2694 $kernel->yield("next_task");
2695 $kernel->yield("msg_to_decrypt");
2696 },
2697 InlineStates => {
2698 next_task => \&next_task,
2699 msg_to_decrypt => \&msg_to_decrypt,
2700 task_result => \&handle_task_result,
2701 task_done => \&handle_task_done,
2702 task_debug => \&handle_task_debug,
2703 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!" },
2704 }
2705 );
2707 daemon_log("start socket for incoming xml messages at port '$server_port' ", 1);
2709 # create session for repeatedly checking the job queue for jobs
2710 POE::Session->create(
2711 inline_states => {
2712 _start => \&_start,
2713 sig_handler => \&sig_handler,
2714 watch_for_new_messages => \&watch_for_new_messages,
2715 watch_for_delivery_messages => \&watch_for_delivery_messages,
2716 watch_for_done_messages => \&watch_for_done_messages,
2717 watch_for_new_jobs => \&watch_for_new_jobs,
2718 watch_for_done_jobs => \&watch_for_done_jobs,
2719 create_packages_list_db => \&run_create_packages_list_db,
2720 create_fai_server_db => \&run_create_fai_server_db,
2721 create_fai_release_db => \&run_create_fai_release_db,
2722 session_run_result => \&session_run_result,
2723 session_run_debug => \&session_run_debug,
2724 session_run_done => \&session_run_done,
2725 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!" },
2726 }
2727 );
2730 # import all modules
2731 &import_modules;
2733 # check wether all modules are gosa-si valid passwd check
2735 POE::Kernel->run();
2736 exit;