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 while (1) {
1133 $id_sql = "SELECT min(id) FROM $incoming_tn WHERE (NOT headertag LIKE 'answer%')";
1134 $id_res = $incoming_db->exec_statement($id_sql);
1135 $message_id = @{@$id_res[0]}[0];
1136 if (defined $message_id) { last }
1137 }
1139 # fetch new message from incoming_db
1140 my $sql = "SELECT * FROM $incoming_tn WHERE id=$message_id";
1141 my $res = $incoming_db->exec_statement($sql);
1143 # prepare all variables needed to process message
1144 my $msg = @{@$res[0]}[3];
1145 my $incoming_id = @{@$res[0]}[0];
1146 my $module = @{@$res[0]}[4];
1147 my $header = @{@$res[0]}[2];
1148 my $msg_hash = $xml->XMLin($msg, ForceArray=>1);
1150 # messages which are an answer to a still running process should not be processed here
1151 if ($header =~ /^answer_(\d+)/) {
1152 return;
1153 }
1155 # delete message from db
1156 my $delete_sql = "DELETE FROM $incoming_tn WHERE id=$incoming_id";
1157 my $delete_res = $incoming_db->exec_statement($delete_sql);
1159 ######################
1160 # process incoming msg
1161 if( $error == 0) {
1162 daemon_log("$session_id INFO: Incoming msg with header '".@{$msg_hash->{'header'}}[0].
1163 "' from '".$heap->{'remote_ip'}."'", 5);
1164 daemon_log("$session_id DEBUG: Processing module ".$module, 7);
1165 $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1167 if ( 0 < @{$answer_l} ) {
1168 my $answer_str = join("\n", @{$answer_l});
1169 daemon_log("$session_id DEBUG: $module: Got answer from module: \n".$answer_str,8);
1170 } else {
1171 daemon_log("$session_id DEBUG: $module: Got no answer from module!" ,8);
1172 }
1174 }
1175 if( !$answer_l ) { $error++ };
1177 ########
1178 # answer
1179 if( $error == 0 ) {
1181 foreach my $answer ( @{$answer_l} ) {
1182 # check outgoing msg to xml validity
1183 my $answer_hash = &check_outgoing_xml_validity($answer);
1184 if( not defined $answer_hash ) {
1185 next;
1186 }
1188 $answer_header = @{$answer_hash->{'header'}}[0];
1189 @answer_target_l = @{$answer_hash->{'target'}};
1190 $answer_source = @{$answer_hash->{'source'}}[0];
1192 # deliver msg to all targets
1193 foreach my $answer_target ( @answer_target_l ) {
1195 # targets of msg are all gosa-si-clients in known_clients_db
1196 if( $answer_target eq "*" ) {
1197 # answer is for all clients
1198 my $sql_statement= "SELECT * FROM known_clients";
1199 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1200 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1201 my $host_name = $hit->{hostname};
1202 my $host_key = $hit->{hostkey};
1203 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1204 &update_jobdb_status_for_send_msgs($answer, $error);
1205 }
1206 }
1208 # targets of msg are all gosa-si-server in known_server_db
1209 elsif( $answer_target eq "KNOWN_SERVER" ) {
1210 # answer is for all server in known_server
1211 my $sql_statement= "SELECT * FROM known_server";
1212 my $query_res = $known_server_db->select_dbentry( $sql_statement );
1213 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1214 my $host_name = $hit->{hostname};
1215 my $host_key = $hit->{hostkey};
1216 $answer =~ s/KNOWN_SERVER/$host_name/g;
1217 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1218 &update_jobdb_status_for_send_msgs($answer, $error);
1219 }
1220 }
1222 # target of msg is GOsa
1223 elsif( $answer_target eq "GOSA" ) {
1224 my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1225 my $add_on = "";
1226 if( defined $session_id ) {
1227 $add_on = ".session_id=$session_id";
1228 }
1229 # answer is for GOSA and has to returned to connected client
1230 my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1231 $client_answer = $gosa_answer.$add_on;
1232 }
1234 # target of msg is job queue at this host
1235 elsif( $answer_target eq "JOBDB") {
1236 $answer =~ /<header>(\S+)<\/header>/;
1237 my $header;
1238 if( defined $1 ) { $header = $1; }
1239 my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1240 &update_jobdb_status_for_send_msgs($answer, $error);
1241 }
1243 # target of msg is a mac address
1244 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 ) {
1245 daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients", 5);
1246 my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1247 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1248 my $found_ip_flag = 0;
1249 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1250 my $host_name = $hit->{hostname};
1251 my $host_key = $hit->{hostkey};
1252 $answer =~ s/$answer_target/$host_name/g;
1253 daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1254 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1255 &update_jobdb_status_for_send_msgs($answer, $error);
1256 $found_ip_flag++ ;
1257 }
1258 if( $found_ip_flag == 0) {
1259 daemon_log("$session_id WARNING: no host found in known_clients with mac address '$answer_target'", 3);
1260 if( $bus_activ eq "true" ) {
1261 daemon_log("$session_id INFO: try to forward msg '$answer_header' to bus '$bus_address'", 5);
1262 my $sql_statement = "SELECT * FROM known_server WHERE hostname='$bus_address'";
1263 my $query_res = $known_server_db->select_dbentry( $sql_statement );
1264 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1265 my $bus_address = $hit->{hostname};
1266 my $bus_key = $hit->{hostkey};
1267 my $error = &send_msg_to_target($answer, $bus_address, $bus_key, $answer_header, $session_id);
1268 &update_jobdb_status_for_send_msgs($answer, $error);
1269 last;
1270 }
1271 }
1273 }
1275 # answer is for one specific host
1276 } else {
1277 # get encrypt_key
1278 my $encrypt_key = &get_encrypt_key($answer_target);
1279 if( not defined $encrypt_key ) {
1280 # unknown target, forward msg to bus
1281 daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1282 if( $bus_activ eq "true" ) {
1283 daemon_log("$session_id INFO: try to forward msg '$answer_header' to bus '$bus_address'", 5);
1284 my $sql_statement = "SELECT * FROM known_server WHERE hostname='$bus_address'";
1285 my $query_res = $known_server_db->select_dbentry( $sql_statement );
1286 my $res_length = keys( %{$query_res} );
1287 if( $res_length == 0 ){
1288 daemon_log("$session_id WARNING: send '$answer_header' to '$bus_address' failed, ".
1289 "no bus found in known_server", 3);
1290 }
1291 else {
1292 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1293 my $bus_key = $hit->{hostkey};
1294 my $error = &send_msg_to_target($answer, $bus_address, $bus_key, $answer_header,$session_id );
1295 &update_jobdb_status_for_send_msgs($answer, $error);
1296 }
1297 }
1298 }
1299 next;
1300 }
1301 my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1302 &update_jobdb_status_for_send_msgs($answer, $error);
1303 }
1304 }
1305 }
1306 }
1308 my $filter = POE::Filter::Reference->new();
1309 my %result = (
1310 status => "seems ok to me",
1311 answer => $client_answer,
1312 );
1314 my $output = $filter->put( [ \%result ] );
1315 print @$output;
1318 }
1321 sub trigger_db_loop {
1322 my ($kernel) = @_ ;
1323 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1324 $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay);
1325 $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1326 $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1327 $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1328 }
1331 sub watch_for_done_jobs {
1332 my ($kernel,$heap) = @_[KERNEL, HEAP];
1334 my $sql_statement = "SELECT * FROM ".$job_queue_tn.
1335 " WHERE status='done'";
1336 my $res = $job_db->select_dbentry( $sql_statement );
1338 while( my ($id, $hit) = each %{$res} ) {
1339 my $jobdb_id = $hit->{id};
1340 my $sql_statement = "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1341 my $res = $job_db->del_dbentry($sql_statement);
1342 }
1344 $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1345 }
1348 sub watch_for_new_jobs {
1349 if($watch_for_new_jobs_in_progress == 0) {
1350 $watch_for_new_jobs_in_progress = 1;
1351 my ($kernel,$heap) = @_[KERNEL, HEAP];
1353 # check gosa job queue for jobs with executable timestamp
1354 my $timestamp = &get_time();
1355 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE status='waiting' AND (CAST (timestamp AS INTEGER)) < $timestamp ORDER BY timestamp";
1356 my $res = $job_db->exec_statement( $sql_statement );
1358 # Merge all new jobs that would do the same actions
1359 my @drops;
1360 my $hits;
1361 foreach my $hit (reverse @{$res} ) {
1362 my $macaddress= lc @{$hit}[8];
1363 my $headertag= @{$hit}[5];
1364 if(
1365 defined($hits->{$macaddress}) &&
1366 defined($hits->{$macaddress}->{$headertag}) &&
1367 defined($hits->{$macaddress}->{$headertag}[0])
1368 ) {
1369 push @drops, "DELETE FROM $job_queue_tn WHERE id = $hits->{$macaddress}->{$headertag}[0]";
1370 }
1371 $hits->{$macaddress}->{$headertag}= $hit;
1372 }
1374 # Delete new jobs with a matching job in state 'processing'
1375 foreach my $macaddress (keys %{$hits}) {
1376 foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1377 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1378 if(defined($jobdb_id)) {
1379 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND headertag='$jobdb_headertag' AND status='processing'";
1380 my $res = $job_db->exec_statement( $sql_statement );
1381 foreach my $hit (@{$res}) {
1382 push @drops, "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1383 }
1384 } else {
1385 daemon_log("J ERROR: Job without id exists for macaddress $macaddress!", 1);
1386 }
1387 }
1388 }
1390 # Commit deletion
1391 $job_db->exec_statementlist(\@drops);
1393 # Look for new jobs that could be executed
1394 foreach my $macaddress (keys %{$hits}) {
1396 # Look if there is an executing job
1397 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='processing'";
1398 my $res = $job_db->exec_statement( $sql_statement );
1400 # Skip new jobs for host if there is a processing job
1401 if(defined($res) and defined @{$res}[0]) {
1402 next;
1403 }
1405 foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1406 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1407 if(defined($jobdb_id)) {
1408 my $job_msg = @{$hits->{$macaddress}->{$jobdb_headertag}}[7];
1410 daemon_log("J DEBUG: its time to execute $job_msg", 7);
1411 my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1412 my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1414 # expect macaddress is unique!!!!!!
1415 my $target = $res_hash->{1}->{hostname};
1417 # change header
1418 $job_msg =~ s/<header>job_/<header>gosa_/;
1420 # add sqlite_id
1421 $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1423 $job_msg =~ /<header>(\S+)<\/header>/;
1424 my $header = $1 ;
1425 my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");
1427 # update status in job queue to 'processing'
1428 $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
1429 my $res = $job_db->update_dbentry($sql_statement);
1430 # TODO: abfangen ob alles in ordnung ist oder nicht, wenn nicht error schmeißen
1432 # We don't want parallel processing
1433 last;
1434 }
1435 }
1436 }
1438 $watch_for_new_jobs_in_progress = 0;
1439 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1440 }
1441 }
1444 sub watch_for_new_messages {
1445 my ($kernel,$heap) = @_[KERNEL, HEAP];
1446 my @coll_user_msg; # collection list of outgoing messages
1448 # check messaging_db for new incoming messages with executable timestamp
1449 my $timestamp = &get_time();
1450 my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS INTEGER))<$timestamp AND flag='n' AND direction='in' )";
1451 my $res = $messaging_db->exec_statement( $sql_statement );
1452 foreach my $hit (@{$res}) {
1454 # create outgoing messages
1455 my $message_to = @{$hit}[3];
1456 # translate message_to to plain login name
1457 my @message_to_l = split(/,/, $message_to);
1458 my %receiver_h;
1459 foreach my $receiver (@message_to_l) {
1460 if ($receiver =~ /^u_([\s\S]*)$/) {
1461 $receiver_h{$1} = 0;
1462 } elsif ($receiver =~ /^g_([\s\S]*)$/) {
1463 my $group_name = $1;
1464 # fetch all group members from ldap and add them to receiver hash
1465 my $ldap_handle = &get_ldap_handle($session_id);
1466 if (defined $ldap_handle) {
1467 my $mesg = $ldap_handle->search(
1468 base => $ldap_base,
1469 scope => 'sub',
1470 attrs => ['memberUid'],
1471 filter => "cn=$group_name";
1472 );
1473 if ($mesg->count) {
1474 my @entries = $mesg->entries;
1475 foreach my $entry (@entries) {
1476 my @receivers= $entry->get_value("memberUid");
1477 foreach my $receiver (@receivers) {
1478 $receiver_h{$1} = 0;
1479 }
1480 }
1481 }
1482 # translating errors ?
1483 if ($mesg->code) {
1484 daemon_log("$session_id ERROR: unable to translate group '$group_name' to user list for message delivery: $mesg->error", 1);
1485 }
1486 # ldap handle error ?
1487 } else {
1488 daemon_log("$session_id ERROR: unable to translate group '$group_name' to user list for message delivery: no ldap handle available", 1);
1489 }
1490 } else {
1491 my $sbjct = &encode_base64(@{$hit}[1]);
1492 my $msg = &encode_base64(@{$hit}[7]);
1493 &daemon_log("M WARNING: unknown receiver '$receiver' for a user-message '$sbjct - $msg'", 3);
1494 }
1495 }
1496 my @receiver_l = keys(%receiver_h);
1498 my $message_id = @{$hit}[0];
1500 #add each outgoing msg to messaging_db
1501 my $receiver;
1502 foreach $receiver (@receiver_l) {
1503 my $sql_statement = "INSERT INTO $messaging_tn (id, subject, message_from, message_to, flag, direction, delivery_time, message, timestamp) ".
1504 "VALUES ('".
1505 $message_id."', '". # id
1506 @{$hit}[1]."', '". # subject
1507 @{$hit}[2]."', '". # message_from
1508 $receiver."', '". # message_to
1509 "none"."', '". # flag
1510 "out"."', '". # direction
1511 @{$hit}[6]."', '". # delivery_time
1512 @{$hit}[7]."', '". # message
1513 $timestamp."'". # timestamp
1514 ")";
1515 &daemon_log("M DEBUG: $sql_statement", 1);
1516 my $res = $messaging_db->exec_statement($sql_statement);
1517 &daemon_log("M INFO: message '".@{$hit}[0]."' is prepared for delivery to receiver '$receiver'", 5);
1518 }
1520 # set incoming message to flag d=deliverd
1521 $sql_statement = "UPDATE $messaging_tn SET flag='p' WHERE id='$message_id'";
1522 &daemon_log("M DEBUG: $sql_statement", 7);
1523 $res = $messaging_db->update_dbentry($sql_statement);
1524 &daemon_log("M INFO: message '$message_id' is set to flag 'p' (processed)", 5);
1525 }
1527 $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1528 return;
1529 }
1531 sub watch_for_delivery_messages {
1532 my ($kernel, $heap) = @_[KERNEL, HEAP];
1534 # select outgoing messages
1535 my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( flag='p' AND direction='out' )";
1536 #&daemon_log("0 DEBUG: $sql", 7);
1537 my $res = $messaging_db->exec_statement( $sql_statement );
1539 # build out msg for each usr
1540 foreach my $hit (@{$res}) {
1541 my $receiver = @{$hit}[3];
1542 my $msg_id = @{$hit}[0];
1543 my $subject = @{$hit}[1];
1544 my $message = @{$hit}[7];
1546 # resolve usr -> host where usr is logged in
1547 my $sql = "SELECT * FROM $login_users_tn WHERE (user='$receiver')";
1548 #&daemon_log("0 DEBUG: $sql", 7);
1549 my $res = $login_users_db->exec_statement($sql);
1551 # reciver is logged in nowhere
1552 if (not ref(@$res[0]) eq "ARRAY") { next; }
1554 my $send_succeed = 0;
1555 foreach my $hit (@$res) {
1556 my $receiver_host = @$hit[0];
1557 &daemon_log("M DEBUG: user '$receiver' is logged in at host '$receiver_host'", 7);
1559 # fetch key to encrypt msg propperly for usr/host
1560 my $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$receiver_host')";
1561 &daemon_log("0 DEBUG: $sql", 7);
1562 my $res = $known_clients_db->exec_statement($sql);
1564 # host is already down
1565 if (not ref(@$res[0]) eq "ARRAY") { next; }
1567 # host is on
1568 my $receiver_key = @{@{$res}[0]}[2];
1569 my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
1570 my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data );
1571 my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0);
1572 if ($error == 0 ) {
1573 $send_succeed++ ;
1574 }
1575 }
1577 if ($send_succeed) {
1578 # set outgoing msg at db to deliverd
1579 my $sql = "UPDATE $messaging_tn SET flag='d' WHERE (id='$msg_id' AND direction='out' AND message_to='$receiver')";
1580 &daemon_log("0 DEBUG: $sql", 7);
1581 my $res = $messaging_db->exec_statement($sql);
1582 }
1583 }
1585 $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1586 return;
1587 }
1590 sub watch_for_done_messages {
1591 my ($kernel,$heap) = @_[KERNEL, HEAP];
1593 my $sql = "SELECT * FROM $messaging_tn WHERE (flag='p' AND direction='in')";
1594 #&daemon_log("0 DEBUG: $sql", 7);
1595 my $res = $messaging_db->exec_statement($sql);
1597 foreach my $hit (@{$res}) {
1598 my $msg_id = @{$hit}[0];
1600 my $sql = "SELECT * FROM $messaging_tn WHERE (id='$msg_id' AND direction='out' AND (NOT flag='s'))";
1601 #&daemon_log("0 DEBUG: $sql", 7);
1602 my $res = $messaging_db->exec_statement($sql);
1604 # not all usr msgs have been seen till now
1605 if ( ref(@$res[0]) eq "ARRAY") { next; }
1607 $sql = "DELETE FROM $messaging_tn WHERE (id='$msg_id')";
1608 #&daemon_log("0 DEBUG: $sql", 7);
1609 $res = $messaging_db->exec_statement($sql);
1611 }
1613 $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1614 return;
1615 }
1618 sub get_ldap_handle {
1619 my ($session_id) = @_;
1620 my $heap;
1621 my $ldap_handle;
1623 if (not defined $session_id ) { $session_id = 0 };
1624 if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
1626 if ($session_id == 0) {
1627 daemon_log("$session_id DEBUG: get_ldap_handle invoked without a session_id, create a new ldap_handle", 7);
1628 $ldap_handle = Net::LDAP->new( $ldap_uri );
1629 $ldap_handle->bind($ldap_admin_dn, password => $ldap_admin_password);
1631 } else {
1632 my $session_reference = $global_kernel->ID_id_to_session($session_id);
1633 if( defined $session_reference ) {
1634 $heap = $session_reference->get_heap();
1635 }
1637 if (not defined $heap) {
1638 daemon_log("$session_id DEBUG: cannot get heap for session_id '$session_id'", 7);
1639 return;
1640 }
1642 # TODO: This "if" is nonsense, because it doesn't prove that the
1643 # used handle is still valid - or if we've to reconnect...
1644 #if (not exists $heap->{ldap_handle}) {
1645 $ldap_handle = Net::LDAP->new( $ldap_uri );
1646 $ldap_handle->bind($ldap_admin_dn, password => $ldap_admin_password);
1647 $heap->{ldap_handle} = $ldap_handle;
1648 #}
1649 }
1650 return $ldap_handle;
1651 }
1654 sub change_fai_state {
1655 my ($st, $targets, $session_id) = @_;
1656 $session_id = 0 if not defined $session_id;
1657 # Set FAI state to localboot
1658 my %mapActions= (
1659 reboot => '',
1660 update => 'softupdate',
1661 localboot => 'localboot',
1662 reinstall => 'install',
1663 rescan => '',
1664 wake => '',
1665 memcheck => 'memcheck',
1666 sysinfo => 'sysinfo',
1667 install => 'install',
1668 );
1670 # Return if this is unknown
1671 if (!exists $mapActions{ $st }){
1672 daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1);
1673 return;
1674 }
1676 my $state= $mapActions{ $st };
1678 my $ldap_handle = &get_ldap_handle($session_id);
1679 if( defined($ldap_handle) ) {
1681 # Build search filter for hosts
1682 my $search= "(&(objectClass=GOhard)";
1683 foreach (@{$targets}){
1684 $search.= "(macAddress=$_)";
1685 }
1686 $search.= ")";
1688 # If there's any host inside of the search string, procress them
1689 if (!($search =~ /macAddress/)){
1690 daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);
1691 return;
1692 }
1694 # Perform search for Unit Tag
1695 my $mesg = $ldap_handle->search(
1696 base => $ldap_base,
1697 scope => 'sub',
1698 attrs => ['dn', 'FAIstate', 'objectClass'],
1699 filter => "$search"
1700 );
1702 if ($mesg->count) {
1703 my @entries = $mesg->entries;
1704 foreach my $entry (@entries) {
1705 # Only modify entry if it is not set to '$state'
1706 if ($entry->get_value("FAIstate") ne "$state"){
1707 daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
1708 my $result;
1709 my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
1710 if (exists $tmp{'FAIobject'}){
1711 if ($state eq ''){
1712 $result= $ldap_handle->modify($entry->dn, changes => [
1713 delete => [ FAIstate => [] ] ]);
1714 } else {
1715 $result= $ldap_handle->modify($entry->dn, changes => [
1716 replace => [ FAIstate => $state ] ]);
1717 }
1718 } elsif ($state ne ''){
1719 $result= $ldap_handle->modify($entry->dn, changes => [
1720 add => [ objectClass => 'FAIobject' ],
1721 add => [ FAIstate => $state ] ]);
1722 }
1724 # Errors?
1725 if ($result->code){
1726 daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
1727 }
1728 } else {
1729 daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 7);
1730 }
1731 }
1732 }
1733 # if no ldap handle defined
1734 } else {
1735 daemon_log("$session_id ERROR: no LDAP handle defined for update FAIstate", 1);
1736 }
1738 }
1741 sub change_goto_state {
1742 my ($st, $targets, $session_id) = @_;
1743 $session_id = 0 if not defined $session_id;
1745 # Switch on or off?
1746 my $state= $st eq 'active' ? 'active': 'locked';
1748 my $ldap_handle = &get_ldap_handle($session_id);
1749 if( defined($ldap_handle) ) {
1751 # Build search filter for hosts
1752 my $search= "(&(objectClass=GOhard)";
1753 foreach (@{$targets}){
1754 $search.= "(macAddress=$_)";
1755 }
1756 $search.= ")";
1758 # If there's any host inside of the search string, procress them
1759 if (!($search =~ /macAddress/)){
1760 return;
1761 }
1763 # Perform search for Unit Tag
1764 my $mesg = $ldap_handle->search(
1765 base => $ldap_base,
1766 scope => 'sub',
1767 attrs => ['dn', 'gotoMode'],
1768 filter => "$search"
1769 );
1771 if ($mesg->count) {
1772 my @entries = $mesg->entries;
1773 foreach my $entry (@entries) {
1775 # Only modify entry if it is not set to '$state'
1776 if ($entry->get_value("gotoMode") ne $state){
1778 daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
1779 my $result;
1780 $result= $ldap_handle->modify($entry->dn, changes => [
1781 replace => [ gotoMode => $state ] ]);
1783 # Errors?
1784 if ($result->code){
1785 &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
1786 }
1788 }
1789 }
1790 }
1792 }
1793 }
1796 sub run_create_fai_server_db {
1797 my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
1798 my $session_id = $session->ID;
1799 my $task = POE::Wheel::Run->new(
1800 Program => sub { &create_fai_server_db($table_name,$kernel, undef, $session_id) },
1801 StdoutEvent => "session_run_result",
1802 StderrEvent => "session_run_debug",
1803 CloseEvent => "session_run_done",
1804 );
1806 $heap->{task}->{ $task->ID } = $task;
1807 return;
1808 }
1811 sub create_fai_server_db {
1812 my ($table_name, $kernel, $dont_create_packages_list, $session_id) = @_;
1813 my $result;
1815 if (not defined $session_id) { $session_id = 0; }
1816 my $ldap_handle = &get_ldap_handle();
1817 if(defined($ldap_handle)) {
1818 daemon_log("$session_id INFO: create_fai_server_db: start", 5);
1819 my $mesg= $ldap_handle->search(
1820 base => $ldap_base,
1821 scope => 'sub',
1822 attrs => ['FAIrepository', 'gosaUnitTag'],
1823 filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
1824 );
1825 if($mesg->{'resultCode'} == 0 &&
1826 $mesg->count != 0) {
1827 foreach my $entry (@{$mesg->{entries}}) {
1828 if($entry->exists('FAIrepository')) {
1829 # Add an entry for each Repository configured for server
1830 foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
1831 my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
1832 my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
1833 $result= $fai_server_db->add_dbentry( {
1834 table => $table_name,
1835 primkey => ['server', 'release', 'tag'],
1836 server => $tmp_url,
1837 release => $tmp_release,
1838 sections => $tmp_sections,
1839 tag => (length($tmp_tag)>0)?$tmp_tag:"",
1840 } );
1841 }
1842 }
1843 }
1844 }
1845 daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
1847 # TODO: Find a way to post the 'create_packages_list_db' event
1848 if(not defined($dont_create_packages_list)) {
1849 &create_packages_list_db(undef, undef, $session_id);
1850 }
1851 }
1853 $ldap_handle->disconnect;
1854 return $result;
1855 }
1858 sub run_create_fai_release_db {
1859 my ($session, $heap, $table_name) = @_[SESSION, HEAP, ARG0];
1860 my $session_id = $session->ID;
1861 my $task = POE::Wheel::Run->new(
1862 Program => sub { &create_fai_release_db($table_name, $session_id) },
1863 StdoutEvent => "session_run_result",
1864 StderrEvent => "session_run_debug",
1865 CloseEvent => "session_run_done",
1866 );
1868 $heap->{task}->{ $task->ID } = $task;
1869 return;
1870 }
1873 sub create_fai_release_db {
1874 my ($table_name, $session_id) = @_;
1875 my $result;
1877 # used for logging
1878 if (not defined $session_id) { $session_id = 0; }
1880 my $ldap_handle = &get_ldap_handle();
1881 if(defined($ldap_handle)) {
1882 daemon_log("$session_id INFO: create_fai_release_db: start",5);
1883 my $mesg= $ldap_handle->search(
1884 base => $ldap_base,
1885 scope => 'sub',
1886 attrs => [],
1887 filter => "(&(objectClass=organizationalUnit)(ou=fai))",
1888 );
1889 if($mesg->{'resultCode'} == 0 &&
1890 $mesg->count != 0) {
1891 # Walk through all possible FAI container ou's
1892 my @sql_list;
1893 my $timestamp= &get_time();
1894 foreach my $ou (@{$mesg->{entries}}) {
1895 my $tmp_classes= resolve_fai_classes($ou->dn, $ldap_handle, $session_id);
1896 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
1897 my @tmp_array=get_fai_release_entries($tmp_classes);
1898 if(@tmp_array) {
1899 foreach my $entry (@tmp_array) {
1900 if(defined($entry) && ref($entry) eq 'HASH') {
1901 my $sql=
1902 "INSERT INTO $table_name "
1903 ."(timestamp, release, class, type, state) VALUES ("
1904 .$timestamp.","
1905 ."'".$entry->{'release'}."',"
1906 ."'".$entry->{'class'}."',"
1907 ."'".$entry->{'type'}."',"
1908 ."'".$entry->{'state'}."')";
1909 push @sql_list, $sql;
1910 }
1911 }
1912 }
1913 }
1914 }
1916 daemon_log("$session_id DEBUG: Inserting ".scalar @sql_list." entries to DB",8);
1917 if(@sql_list) {
1918 unshift @sql_list, "VACUUM";
1919 unshift @sql_list, "DELETE FROM $table_name";
1920 $fai_release_db->exec_statementlist(\@sql_list);
1921 }
1922 daemon_log("$session_id DEBUG: Done with inserting",7);
1923 }
1924 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
1925 }
1926 $ldap_handle->disconnect;
1927 return $result;
1928 }
1930 sub get_fai_types {
1931 my $tmp_classes = shift || return undef;
1932 my @result;
1934 foreach my $type(keys %{$tmp_classes}) {
1935 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
1936 my $entry = {
1937 type => $type,
1938 state => $tmp_classes->{$type}[0],
1939 };
1940 push @result, $entry;
1941 }
1942 }
1944 return @result;
1945 }
1947 sub get_fai_state {
1948 my $result = "";
1949 my $tmp_classes = shift || return $result;
1951 foreach my $type(keys %{$tmp_classes}) {
1952 if(defined($tmp_classes->{$type}[0])) {
1953 $result = $tmp_classes->{$type}[0];
1955 # State is equal for all types in class
1956 last;
1957 }
1958 }
1960 return $result;
1961 }
1963 sub resolve_fai_classes {
1964 my ($fai_base, $ldap_handle, $session_id) = @_;
1965 if (not defined $session_id) { $session_id = 0; }
1966 my $result;
1967 my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
1968 my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
1969 my $fai_classes;
1971 daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base",7);
1972 my $mesg= $ldap_handle->search(
1973 base => $fai_base,
1974 scope => 'sub',
1975 attrs => ['cn','objectClass','FAIstate'],
1976 filter => $fai_filter,
1977 );
1978 daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries",7);
1980 if($mesg->{'resultCode'} == 0 &&
1981 $mesg->count != 0) {
1982 foreach my $entry (@{$mesg->{entries}}) {
1983 if($entry->exists('cn')) {
1984 my $tmp_dn= $entry->dn();
1986 # Skip classname and ou dn parts for class
1987 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?),$fai_base$/;
1989 # Skip classes without releases
1990 if((!defined($tmp_release)) || length($tmp_release)==0) {
1991 next;
1992 }
1994 my $tmp_cn= $entry->get_value('cn');
1995 my $tmp_state= $entry->get_value('FAIstate');
1997 my $tmp_type;
1998 # Get FAI type
1999 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
2000 if(grep $_ eq $oclass, @possible_fai_classes) {
2001 $tmp_type= $oclass;
2002 last;
2003 }
2004 }
2006 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2007 # A Subrelease
2008 my @sub_releases = split(/,/, $tmp_release);
2010 # Walk through subreleases and build hash tree
2011 my $hash;
2012 while(my $tmp_sub_release = pop @sub_releases) {
2013 $hash .= "\{'$tmp_sub_release'\}->";
2014 }
2015 eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
2016 } else {
2017 # A branch, no subrelease
2018 push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
2019 }
2020 } elsif (!$entry->exists('cn')) {
2021 my $tmp_dn= $entry->dn();
2022 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?),$fai_base$/;
2024 # Skip classes without releases
2025 if((!defined($tmp_release)) || length($tmp_release)==0) {
2026 next;
2027 }
2029 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2030 # A Subrelease
2031 my @sub_releases= split(/,/, $tmp_release);
2033 # Walk through subreleases and build hash tree
2034 my $hash;
2035 while(my $tmp_sub_release = pop @sub_releases) {
2036 $hash .= "\{'$tmp_sub_release'\}->";
2037 }
2038 # Remove the last two characters
2039 chop($hash);
2040 chop($hash);
2042 eval('$fai_classes->'.$hash.'= {}');
2043 } else {
2044 # A branch, no subrelease
2045 if(!exists($fai_classes->{$tmp_release})) {
2046 $fai_classes->{$tmp_release} = {};
2047 }
2048 }
2049 }
2050 }
2052 # The hash is complete, now we can honor the copy-on-write based missing entries
2053 foreach my $release (keys %$fai_classes) {
2054 $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
2055 }
2056 }
2057 return $result;
2058 }
2060 sub apply_fai_inheritance {
2061 my $fai_classes = shift || return {};
2062 my $tmp_classes;
2064 # Get the classes from the branch
2065 foreach my $class (keys %{$fai_classes}) {
2066 # Skip subreleases
2067 if($class =~ /^ou=.*$/) {
2068 next;
2069 } else {
2070 $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
2071 }
2072 }
2074 # Apply to each subrelease
2075 foreach my $subrelease (keys %{$fai_classes}) {
2076 if($subrelease =~ /ou=/) {
2077 foreach my $tmp_class (keys %{$tmp_classes}) {
2078 if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
2079 $fai_classes->{$subrelease}->{$tmp_class} =
2080 deep_copy($tmp_classes->{$tmp_class});
2081 } else {
2082 foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
2083 if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
2084 $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
2085 deep_copy($tmp_classes->{$tmp_class}->{$type});
2086 }
2087 }
2088 }
2089 }
2090 }
2091 }
2093 # Find subreleases in deeper levels
2094 foreach my $subrelease (keys %{$fai_classes}) {
2095 if($subrelease =~ /ou=/) {
2096 foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
2097 if($subsubrelease =~ /ou=/) {
2098 apply_fai_inheritance($fai_classes->{$subrelease});
2099 }
2100 }
2101 }
2102 }
2104 return $fai_classes;
2105 }
2107 sub get_fai_release_entries {
2108 my $tmp_classes = shift || return;
2109 my $parent = shift || "";
2110 my @result = shift || ();
2112 foreach my $entry (keys %{$tmp_classes}) {
2113 if(defined($entry)) {
2114 if($entry =~ /^ou=.*$/) {
2115 my $release_name = $entry;
2116 $release_name =~ s/ou=//g;
2117 if(length($parent)>0) {
2118 $release_name = $parent."/".$release_name;
2119 }
2120 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2121 foreach my $bufentry(@bufentries) {
2122 push @result, $bufentry;
2123 }
2124 } else {
2125 my @types = get_fai_types($tmp_classes->{$entry});
2126 foreach my $type (@types) {
2127 push @result,
2128 {
2129 'class' => $entry,
2130 'type' => $type->{'type'},
2131 'release' => $parent,
2132 'state' => $type->{'state'},
2133 };
2134 }
2135 }
2136 }
2137 }
2139 return @result;
2140 }
2142 sub deep_copy {
2143 my $this = shift;
2144 if (not ref $this) {
2145 $this;
2146 } elsif (ref $this eq "ARRAY") {
2147 [map deep_copy($_), @$this];
2148 } elsif (ref $this eq "HASH") {
2149 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2150 } else { die "what type is $_?" }
2151 }
2154 sub session_run_result {
2155 my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];
2156 $kernel->sig(CHLD => "child_reap");
2157 }
2159 sub session_run_debug {
2160 my $result = $_[ARG0];
2161 print STDERR "$result\n";
2162 }
2164 sub session_run_done {
2165 my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2166 delete $heap->{task}->{$task_id};
2167 }
2170 sub create_sources_list {
2171 my $session_id = shift;
2172 my $ldap_handle = &main::get_ldap_handle;
2173 my $result="/tmp/gosa_si_tmp_sources_list";
2175 # Remove old file
2176 if(stat($result)) {
2177 unlink($result);
2178 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7);
2179 }
2181 my $fh;
2182 open($fh, ">$result");
2183 if (not defined $fh) {
2184 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7);
2185 return undef;
2186 }
2187 if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2188 my $mesg=$ldap_handle->search(
2189 base => $main::ldap_server_dn,
2190 scope => 'base',
2191 attrs => 'FAIrepository',
2192 filter => 'objectClass=FAIrepositoryServer'
2193 );
2194 if($mesg->count) {
2195 foreach my $entry(@{$mesg->{'entries'}}) {
2196 foreach my $value(@{$entry->get_value('FAIrepository', asref => 1)}) {
2197 my ($server, $tag, $release, $sections)= split /\|/, $value;
2198 my $line = "deb $server $release";
2199 $sections =~ s/,/ /g;
2200 $line.= " $sections";
2201 print $fh $line."\n";
2202 }
2203 }
2204 }
2205 } else {
2206 if (defined $main::ldap_server_dn){
2207 &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1);
2208 } else {
2209 &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2210 }
2211 }
2212 close($fh);
2214 return $result;
2215 }
2218 sub run_create_packages_list_db {
2219 my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2220 my $session_id = $session->ID;
2222 my $task = POE::Wheel::Run->new(
2223 Priority => +20,
2224 Program => sub {&create_packages_list_db(undef, undef, $session_id)},
2225 StdoutEvent => "session_run_result",
2226 StderrEvent => "session_run_debug",
2227 CloseEvent => "session_run_done",
2228 );
2229 $heap->{task}->{ $task->ID } = $task;
2230 }
2233 sub create_packages_list_db {
2234 my ($ldap_handle, $sources_file, $session_id) = @_;
2236 # it should not be possible to trigger a recreation of packages_list_db
2237 # while packages_list_db is under construction, so set flag packages_list_under_construction
2238 # which is tested befor recreation can be started
2239 if (-r $packages_list_under_construction) {
2240 daemon_log("$session_id WARNING: packages_list_db is right now under construction, please wait until this process is finished", 3);
2241 return;
2242 } else {
2243 daemon_log("$session_id INFO: create_packages_list_db: start", 5);
2244 # set packages_list_under_construction to true
2245 system("touch $packages_list_under_construction");
2246 @packages_list_statements=();
2247 }
2249 if (not defined $session_id) { $session_id = 0; }
2250 if (not defined $ldap_handle) {
2251 $ldap_handle= &get_ldap_handle();
2253 if (not defined $ldap_handle) {
2254 daemon_log("$session_id ERROR: no ldap_handle available to create_packages_list_db", 1);
2255 unlink($packages_list_under_construction);
2256 return;
2257 }
2258 }
2259 if (not defined $sources_file) {
2260 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5);
2261 $sources_file = &create_sources_list($session_id);
2262 }
2264 if (not defined $sources_file) {
2265 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1);
2266 unlink($packages_list_under_construction);
2267 return;
2268 }
2270 my $line;
2272 open(CONFIG, "<$sources_file") or do {
2273 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2274 unlink($packages_list_under_construction);
2275 return;
2276 };
2278 # Read lines
2279 while ($line = <CONFIG>){
2280 # Unify
2281 chop($line);
2282 $line =~ s/^\s+//;
2283 $line =~ s/^\s+/ /;
2285 # Strip comments
2286 $line =~ s/#.*$//g;
2288 # Skip empty lines
2289 if ($line =~ /^\s*$/){
2290 next;
2291 }
2293 # Interpret deb line
2294 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2295 my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2296 my $section;
2297 foreach $section (split(' ', $sections)){
2298 &parse_package_info( $baseurl, $dist, $section, $session_id );
2299 }
2300 }
2301 }
2303 close (CONFIG);
2305 find(\&cleanup_and_extract, keys( %repo_dirs ));
2306 &main::strip_packages_list_statements();
2307 unshift @packages_list_statements, "VACUUM";
2308 $packages_list_db->exec_statementlist(\@packages_list_statements);
2309 unlink($packages_list_under_construction);
2310 daemon_log("$session_id INFO: create_packages_list_db: finished", 5);
2311 return;
2312 }
2314 # This function should do some intensive task to minimize the db-traffic
2315 sub strip_packages_list_statements {
2316 my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2317 my @new_statement_list=();
2318 my $hash;
2319 my $insert_hash;
2320 my $update_hash;
2321 my $delete_hash;
2322 my $local_timestamp=get_time();
2324 foreach my $existing_entry (@existing_entries) {
2325 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
2326 }
2328 foreach my $statement (@packages_list_statements) {
2329 if($statement =~ /^INSERT/i) {
2330 # Assign the values from the insert statement
2331 my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~
2332 /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
2333 if(exists($hash->{$distribution}->{$package}->{$version})) {
2334 # If section or description has changed, update the DB
2335 if(
2336 (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or
2337 (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
2338 ) {
2339 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
2340 }
2341 } else {
2342 # Insert a non-existing entry to db
2343 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2344 }
2345 } elsif ($statement =~ /^UPDATE/i) {
2346 my ($template,$package,$version) = ($1,$2,$3) if $statement =~
2347 /^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;
2348 foreach my $distribution (keys %{$hash}) {
2349 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
2350 # update the insertion hash to execute only one query per package (insert instead insert+update)
2351 @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
2352 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
2353 if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
2354 my $section;
2355 my $description;
2356 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
2357 length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
2358 $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
2359 }
2360 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2361 $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
2362 }
2363 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2364 }
2365 }
2366 }
2367 }
2368 }
2370 # TODO: Check for orphaned entries
2372 # unroll the insert_hash
2373 foreach my $distribution (keys %{$insert_hash}) {
2374 foreach my $package (keys %{$insert_hash->{$distribution}}) {
2375 foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
2376 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
2377 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
2378 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
2379 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
2380 ."'$local_timestamp')";
2381 }
2382 }
2383 }
2385 # unroll the update hash
2386 foreach my $distribution (keys %{$update_hash}) {
2387 foreach my $package (keys %{$update_hash->{$distribution}}) {
2388 foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
2389 my $set = "";
2390 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
2391 $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
2392 }
2393 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2394 $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
2395 }
2396 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
2397 $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
2398 }
2399 if(defined($set) and length($set) > 0) {
2400 $set .= "timestamp = '$local_timestamp'";
2401 } else {
2402 next;
2403 }
2404 push @new_statement_list,
2405 "UPDATE $main::packages_list_tn SET $set WHERE"
2406 ." distribution = '$distribution'"
2407 ." AND package = '$package'"
2408 ." AND version = '$version'";
2409 }
2410 }
2411 }
2413 @packages_list_statements = @new_statement_list;
2414 }
2417 sub parse_package_info {
2418 my ($baseurl, $dist, $section, $session_id)= @_;
2419 my ($package);
2420 if (not defined $session_id) { $session_id = 0; }
2421 my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
2422 $repo_dirs{ "${repo_path}/pool" } = 1;
2424 foreach $package ("Packages.gz"){
2425 daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
2426 get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
2427 parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
2428 }
2430 }
2433 sub get_package {
2434 my ($url, $dest, $session_id)= @_;
2435 if (not defined $session_id) { $session_id = 0; }
2437 my $tpath = dirname($dest);
2438 -d "$tpath" || mkpath "$tpath";
2440 # This is ugly, but I've no time to take a look at "how it works in perl"
2441 if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
2442 system("gunzip -cd '$dest' > '$dest.in'");
2443 daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 5);
2444 unlink($dest);
2445 daemon_log("$session_id DEBUG: delete file '$dest'", 5);
2446 } else {
2447 daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' failed!", 1);
2448 }
2449 return 0;
2450 }
2453 sub parse_package {
2454 my ($path, $dist, $srv_path, $session_id)= @_;
2455 if (not defined $session_id) { $session_id = 0;}
2456 my ($package, $version, $section, $description);
2457 my $PACKAGES;
2458 my $timestamp = &get_time();
2460 if(not stat("$path.in")) {
2461 daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
2462 return;
2463 }
2465 open($PACKAGES, "<$path.in");
2466 if(not defined($PACKAGES)) {
2467 daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1);
2468 return;
2469 }
2471 # Read lines
2472 while (<$PACKAGES>){
2473 my $line = $_;
2474 # Unify
2475 chop($line);
2477 # Use empty lines as a trigger
2478 if ($line =~ /^\s*$/){
2479 my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
2480 push(@packages_list_statements, $sql);
2481 $package = "none";
2482 $version = "none";
2483 $section = "none";
2484 $description = "none";
2485 next;
2486 }
2488 # Trigger for package name
2489 if ($line =~ /^Package:\s/){
2490 ($package)= ($line =~ /^Package: (.*)$/);
2491 next;
2492 }
2494 # Trigger for version
2495 if ($line =~ /^Version:\s/){
2496 ($version)= ($line =~ /^Version: (.*)$/);
2497 next;
2498 }
2500 # Trigger for description
2501 if ($line =~ /^Description:\s/){
2502 ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
2503 next;
2504 }
2506 # Trigger for section
2507 if ($line =~ /^Section:\s/){
2508 ($section)= ($line =~ /^Section: (.*)$/);
2509 next;
2510 }
2512 # Trigger for filename
2513 if ($line =~ /^Filename:\s/){
2514 my ($filename) = ($line =~ /^Filename: (.*)$/);
2515 store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
2516 next;
2517 }
2518 }
2520 close( $PACKAGES );
2521 unlink( "$path.in" );
2522 &main::daemon_log("$session_id DEBUG: unlink '$path.in'", 1);
2523 }
2526 sub store_fileinfo {
2527 my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
2529 my %fileinfo = (
2530 'package' => $package,
2531 'dist' => $dist,
2532 'version' => $vers,
2533 );
2535 $repo_files{ "${srvdir}/$file" } = \%fileinfo;
2536 }
2539 sub cleanup_and_extract {
2540 my $fileinfo = $repo_files{ $File::Find::name };
2542 if( defined $fileinfo ) {
2544 my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
2545 my $sql;
2546 my $package = $fileinfo->{ 'package' };
2547 my $newver = $fileinfo->{ 'version' };
2549 mkpath($dir);
2550 system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
2552 if( -f "$dir/DEBIAN/templates" ) {
2554 daemon_log("DEBUG: Found debconf templates in '$package' - $newver", 5);
2556 my $tmpl= "";
2557 {
2558 local $/=undef;
2559 open FILE, "$dir/DEBIAN/templates";
2560 $tmpl = &encode_base64(<FILE>);
2561 close FILE;
2562 }
2563 rmtree("$dir/DEBIAN/templates");
2565 $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
2566 push @packages_list_statements, $sql;
2567 }
2568 }
2570 return;
2571 }
2574 #==== MAIN = main ==============================================================
2575 # parse commandline options
2576 Getopt::Long::Configure( "bundling" );
2577 GetOptions("h|help" => \&usage,
2578 "c|config=s" => \$cfg_file,
2579 "f|foreground" => \$foreground,
2580 "v|verbose+" => \$verbose,
2581 "no-bus+" => \$no_bus,
2582 "no-arp+" => \$no_arp,
2583 );
2585 # read and set config parameters
2586 &check_cmdline_param ;
2587 &read_configfile;
2588 &check_pid;
2590 $SIG{CHLD} = 'IGNORE';
2592 # forward error messages to logfile
2593 if( ! $foreground ) {
2594 open( STDIN, '+>/dev/null' );
2595 open( STDOUT, '+>&STDIN' );
2596 open( STDERR, '+>&STDIN' );
2597 }
2599 # Just fork, if we are not in foreground mode
2600 if( ! $foreground ) {
2601 chdir '/' or die "Can't chdir to /: $!";
2602 $pid = fork;
2603 setsid or die "Can't start a new session: $!";
2604 umask 0;
2605 } else {
2606 $pid = $$;
2607 }
2609 # Do something useful - put our PID into the pid_file
2610 if( 0 != $pid ) {
2611 open( LOCK_FILE, ">$pid_file" );
2612 print LOCK_FILE "$pid\n";
2613 close( LOCK_FILE );
2614 if( !$foreground ) {
2615 exit( 0 )
2616 };
2617 }
2619 # parse head url and revision from svn
2620 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
2621 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
2622 $server_headURL = defined $1 ? $1 : 'unknown' ;
2623 $server_revision = defined $2 ? $2 : 'unknown' ;
2624 if ($server_headURL =~ /\/tag\// ||
2625 $server_headURL =~ /\/branches\// ) {
2626 $server_status = "stable";
2627 } else {
2628 $server_status = "developmental" ;
2629 }
2632 daemon_log(" ", 1);
2633 daemon_log("$0 started!", 1);
2634 daemon_log("status: $server_status", 1);
2635 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1);
2637 if ($no_bus > 0) {
2638 $bus_activ = "false"
2639 }
2641 # connect to incoming_db
2642 unlink($incoming_file_name);
2643 $incoming_db = GOSA::DBsqlite->new($incoming_file_name);
2644 $incoming_db->create_table($incoming_tn, \@incoming_col_names);
2646 # connect to gosa-si job queue
2647 $job_db = GOSA::DBsqlite->new($job_queue_file_name);
2648 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
2650 # connect to known_clients_db
2651 $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
2652 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
2654 # connect to known_server_db
2655 $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
2656 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
2658 # connect to login_usr_db
2659 $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
2660 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
2662 # connect to fai_server_db and fai_release_db
2663 unlink($fai_server_file_name);
2664 $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
2665 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
2667 unlink($fai_release_file_name);
2668 $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
2669 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
2671 # connect to packages_list_db
2672 #unlink($packages_list_file_name);
2673 unlink($packages_list_under_construction);
2674 $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
2675 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
2677 # connect to messaging_db
2678 $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
2679 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
2682 # create xml object used for en/decrypting
2683 $xml = new XML::Simple();
2685 # create socket for incoming xml messages
2687 POE::Component::Server::TCP->new(
2688 Port => $server_port,
2689 ClientInput => sub {
2690 my ($kernel, $input) = @_[KERNEL, ARG0];
2691 push(@tasks, $input);
2692 push(@msgs_to_decrypt, $input);
2693 $kernel->yield("next_task");
2694 $kernel->yield("msg_to_decrypt");
2695 },
2696 InlineStates => {
2697 next_task => \&next_task,
2698 msg_to_decrypt => \&msg_to_decrypt,
2699 task_result => \&handle_task_result,
2700 task_done => \&handle_task_done,
2701 task_debug => \&handle_task_debug,
2702 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!" },
2703 }
2704 );
2706 daemon_log("start socket for incoming xml messages at port '$server_port' ", 1);
2708 # create session for repeatedly checking the job queue for jobs
2709 POE::Session->create(
2710 inline_states => {
2711 _start => \&_start,
2712 sig_handler => \&sig_handler,
2713 watch_for_new_messages => \&watch_for_new_messages,
2714 watch_for_delivery_messages => \&watch_for_delivery_messages,
2715 watch_for_done_messages => \&watch_for_done_messages,
2716 watch_for_new_jobs => \&watch_for_new_jobs,
2717 watch_for_done_jobs => \&watch_for_done_jobs,
2718 create_packages_list_db => \&run_create_packages_list_db,
2719 create_fai_server_db => \&run_create_fai_server_db,
2720 create_fai_release_db => \&run_create_fai_release_db,
2721 session_run_result => \&session_run_result,
2722 session_run_debug => \&session_run_debug,
2723 session_run_done => \&session_run_done,
2724 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!" },
2725 }
2726 );
2729 # import all modules
2730 &import_modules;
2732 # check wether all modules are gosa-si valid passwd check
2734 POE::Kernel->run();
2735 exit;