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 "xmlmessage DEFAULT 'none'",
126 "module DEFAULT 'none'",
127 );
129 # holds all gosa jobs
130 our $job_db;
131 our $job_queue_tn = 'jobs';
132 my $job_queue_file_name;
133 my @job_queue_col_names = ("id INTEGER PRIMARY KEY",
134 "timestamp DEFAULT 'none'",
135 "status DEFAULT 'none'",
136 "result DEFAULT 'none'",
137 "progress DEFAULT 'none'",
138 "headertag DEFAULT 'none'",
139 "targettag DEFAULT 'none'",
140 "xmlmessage DEFAULT 'none'",
141 "macaddress DEFAULT 'none'",
142 "plainname DEFAULT 'none'",
143 );
145 # holds all other gosa-sd as well as the gosa-sd-bus
146 our $known_server_db;
147 our $known_server_tn = "known_server";
148 my $known_server_file_name;
149 my @known_server_col_names = ("hostname", "status", "hostkey", "timestamp");
151 # holds all registrated clients
152 our $known_clients_db;
153 our $known_clients_tn = "known_clients";
154 my $known_clients_file_name;
155 my @known_clients_col_names = ("hostname", "status", "hostkey", "timestamp", "macaddress", "events");
157 # holds all logged in user at each client
158 our $login_users_db;
159 our $login_users_tn = "login_users";
160 my $login_users_file_name;
161 my @login_users_col_names = ("client", "user", "timestamp");
163 # holds all fai server, the debian release and tag
164 our $fai_server_db;
165 our $fai_server_tn = "fai_server";
166 my $fai_server_file_name;
167 our @fai_server_col_names = ("timestamp", "server", "release", "sections", "tag");
169 our $fai_release_db;
170 our $fai_release_tn = "fai_release";
171 my $fai_release_file_name;
172 our @fai_release_col_names = ("timestamp", "release", "class", "type", "state");
174 # holds all packages available from different repositories
175 our $packages_list_db;
176 our $packages_list_tn = "packages_list";
177 my $packages_list_file_name;
178 our @packages_list_col_names = ("distribution", "package", "version", "section", "description", "template", "timestamp");
179 my $outdir = "/tmp/packages_list_db";
180 my $arch = "i386";
182 # holds all messages which should be delivered to a user
183 our $messaging_db;
184 our $messaging_tn = "messaging";
185 our @messaging_col_names = ("id INTEGER", "subject", "message_from", "message_to",
186 "flag", "direction", "delivery_time", "message", "timestamp" );
187 my $messaging_file_name;
189 # path to directory to store client install log files
190 our $client_fai_log_dir = "/var/log/fai";
192 # queue which stores taskes until one of the $max_children children are ready to process the task
193 my @tasks = qw();
194 my @msgs_to_decrypt = qw();
195 my $max_children = 2;
198 %cfg_defaults = (
199 "general" => {
200 "log-file" => [\$log_file, "/var/run/".$prg.".log"],
201 "pid-file" => [\$pid_file, "/var/run/".$prg.".pid"],
202 },
203 "bus" => {
204 "activ" => [\$bus_activ, "true"],
205 },
206 "server" => {
207 "port" => [\$server_port, "20081"],
208 "known-clients" => [\$known_clients_file_name, '/var/lib/gosa-si/clients.db' ],
209 "known-servers" => [\$known_server_file_name, '/var/lib/gosa-si/servers.db'],
210 "incoming" => [\$incoming_file_name, '/var/lib/gosa-si/incoming.db'],
211 "login-users" => [\$login_users_file_name, '/var/lib/gosa-si/users.db'],
212 "fai-server" => [\$fai_server_file_name, '/var/lib/gosa-si/fai_server.db'],
213 "fai-release" => [\$fai_release_file_name, '/var/lib/gosa-si/fai_release.db'],
214 "packages-list" => [\$packages_list_file_name, '/var/lib/gosa-si/packages.db'],
215 "messaging" => [\$messaging_file_name, '/var/lib/gosa-si/messaging.db'],
216 "source-list" => [\$sources_list, '/etc/apt/sources.list'],
217 "repo-path" => [\$repo_path, '/srv/www/repository'],
218 "ldap-uri" => [\$ldap_uri, ""],
219 "ldap-base" => [\$ldap_base, ""],
220 "ldap-admin-dn" => [\$ldap_admin_dn, ""],
221 "ldap-admin-password" => [\$ldap_admin_password, ""],
222 "gosa-unit-tag" => [\$gosa_unit_tag, ""],
223 "max-clients" => [\$max_clients, 10],
224 },
225 "GOsaPackages" => {
226 "ip" => [\$gosa_ip, "0.0.0.0"],
227 "port" => [\$gosa_port, "20082"],
228 "job-queue" => [\$job_queue_file_name, '/var/lib/gosa-si/jobs.db'],
229 "job-queue-loop-delay" => [\$job_queue_loop_delay, 3],
230 "messaging-db-loop-delay" => [\$messaging_db_loop_delay, 3],
231 "key" => [\$GosaPackages_key, "none"],
232 },
233 "SIPackages" => {
234 "key" => [\$SIPackages_key, "none"],
235 },
236 );
239 #=== FUNCTION ================================================================
240 # NAME: usage
241 # PARAMETERS: nothing
242 # RETURNS: nothing
243 # DESCRIPTION: print out usage text to STDERR
244 #===============================================================================
245 sub usage {
246 print STDERR << "EOF" ;
247 usage: $prg [-hvf] [-c config]
249 -h : this (help) message
250 -c <file> : config file
251 -f : foreground, process will not be forked to background
252 -v : be verbose (multiple to increase verbosity)
253 -no-bus : starts $prg without connection to bus
254 -no-arp : starts $prg without connection to arp module
256 EOF
257 print "\n" ;
258 }
261 #=== FUNCTION ================================================================
262 # NAME: read_configfile
263 # PARAMETERS: cfg_file - string -
264 # RETURNS: nothing
265 # DESCRIPTION: read cfg_file and set variables
266 #===============================================================================
267 sub read_configfile {
268 my $cfg;
269 if( defined( $cfg_file) && ( (-s $cfg_file) > 0 )) {
270 if( -r $cfg_file ) {
271 $cfg = Config::IniFiles->new( -file => $cfg_file );
272 } else {
273 print STDERR "Couldn't read config file!\n";
274 }
275 } else {
276 $cfg = Config::IniFiles->new() ;
277 }
278 foreach my $section (keys %cfg_defaults) {
279 foreach my $param (keys %{$cfg_defaults{ $section }}) {
280 my $pinfo = $cfg_defaults{ $section }{ $param };
281 ${@$pinfo[ 0 ]} = $cfg->val( $section, $param, @$pinfo[ 1 ] );
282 }
283 }
284 }
287 #=== FUNCTION ================================================================
288 # NAME: logging
289 # PARAMETERS: level - string - default 'info'
290 # msg - string -
291 # facility - string - default 'LOG_DAEMON'
292 # RETURNS: nothing
293 # DESCRIPTION: function for logging
294 #===============================================================================
295 sub daemon_log {
296 # log into log_file
297 my( $msg, $level ) = @_;
298 if(not defined $msg) { return }
299 if(not defined $level) { $level = 1 }
300 if(defined $log_file){
301 open(LOG_HANDLE, ">>$log_file");
302 if(not defined open( LOG_HANDLE, ">>$log_file" )) {
303 print STDERR "cannot open $log_file: $!";
304 return }
305 chomp($msg);
306 $msg =~s/\n//g; # no newlines are allowed in log messages, this is important for later log parsing
307 if($level <= $verbose){
308 my ($seconds, $minutes, $hours, $monthday, $month,
309 $year, $weekday, $yearday, $sommertime) = localtime(time);
310 $hours = $hours < 10 ? $hours = "0".$hours : $hours;
311 $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
312 $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
313 my @monthnames = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
314 $month = $monthnames[$month];
315 $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
316 $year+=1900;
317 my $name = $prg;
319 my $log_msg = "$month $monthday $hours:$minutes:$seconds $name $msg\n";
320 print LOG_HANDLE $log_msg;
321 if( $foreground ) {
322 print STDERR $log_msg;
323 }
324 }
325 close( LOG_HANDLE );
326 }
327 }
330 #=== FUNCTION ================================================================
331 # NAME: check_cmdline_param
332 # PARAMETERS: nothing
333 # RETURNS: nothing
334 # DESCRIPTION: validates commandline parameter
335 #===============================================================================
336 sub check_cmdline_param () {
337 my $err_config;
338 my $err_counter = 0;
339 if(not defined($cfg_file)) {
340 $cfg_file = "/etc/gosa-si/server.conf";
341 if(! -r $cfg_file) {
342 $err_config = "please specify a config file";
343 $err_counter += 1;
344 }
345 }
346 if( $err_counter > 0 ) {
347 &usage( "", 1 );
348 if( defined( $err_config)) { print STDERR "$err_config\n"}
349 print STDERR "\n";
350 exit( -1 );
351 }
352 }
355 #=== FUNCTION ================================================================
356 # NAME: check_pid
357 # PARAMETERS: nothing
358 # RETURNS: nothing
359 # DESCRIPTION: handels pid processing
360 #===============================================================================
361 sub check_pid {
362 $pid = -1;
363 # Check, if we are already running
364 if( open(LOCK_FILE, "<$pid_file") ) {
365 $pid = <LOCK_FILE>;
366 if( defined $pid ) {
367 chomp( $pid );
368 if( -f "/proc/$pid/stat" ) {
369 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
370 if( $stat ) {
371 daemon_log("ERROR: Already running",1);
372 close( LOCK_FILE );
373 exit -1;
374 }
375 }
376 }
377 close( LOCK_FILE );
378 unlink( $pid_file );
379 }
381 # create a syslog msg if it is not to possible to open PID file
382 if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
383 my($msg) = "Couldn't obtain lockfile '$pid_file' ";
384 if (open(LOCK_FILE, '<', $pid_file)
385 && ($pid = <LOCK_FILE>))
386 {
387 chomp($pid);
388 $msg .= "(PID $pid)\n";
389 } else {
390 $msg .= "(unable to read PID)\n";
391 }
392 if( ! ($foreground) ) {
393 openlog( $0, "cons,pid", "daemon" );
394 syslog( "warning", $msg );
395 closelog();
396 }
397 else {
398 print( STDERR " $msg " );
399 }
400 exit( -1 );
401 }
402 }
404 #=== FUNCTION ================================================================
405 # NAME: import_modules
406 # PARAMETERS: module_path - string - abs. path to the directory the modules
407 # are stored
408 # RETURNS: nothing
409 # DESCRIPTION: each file in module_path which ends with '.pm' and activation
410 # state is on is imported by "require 'file';"
411 #===============================================================================
412 sub import_modules {
413 daemon_log(" ", 1);
415 if (not -e $modules_path) {
416 daemon_log("ERROR: cannot find directory or directory is not readable: $modules_path", 1);
417 }
419 opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
420 while (defined (my $file = readdir (DIR))) {
421 if (not $file =~ /(\S*?).pm$/) {
422 next;
423 }
424 my $mod_name = $1;
426 if( $file =~ /ArpHandler.pm/ ) {
427 if( $no_arp > 0 ) {
428 next;
429 }
430 }
432 eval { require $file; };
433 if ($@) {
434 daemon_log("ERROR: gosa-si-server could not load module $file", 1);
435 daemon_log("$@", 5);
436 } else {
437 my $info = eval($mod_name.'::get_module_info()');
438 # Only load module if get_module_info() returns a non-null object
439 if( $info ) {
440 my ($input_address, $input_key, $input, $input_active, $input_type) = @{$info};
441 $known_modules->{$mod_name} = $info;
442 daemon_log("INFO: module $mod_name loaded", 5);
443 }
444 }
445 }
446 close (DIR);
447 }
450 #=== FUNCTION ================================================================
451 # NAME: sig_int_handler
452 # PARAMETERS: signal - string - signal arose from system
453 # RETURNS: noting
454 # DESCRIPTION: handels tasks to be done befor signal becomes active
455 #===============================================================================
456 sub sig_int_handler {
457 my ($signal) = @_;
459 # if (defined($ldap_handle)) {
460 # $ldap_handle->disconnect;
461 # }
462 # TODO alle verbliebenden ldap verbindungen aus allen heaps beenden
465 daemon_log("shutting down gosa-si-server", 1);
466 system("kill `ps -C gosa-si-server -o pid=`");
467 }
468 $SIG{INT} = \&sig_int_handler;
471 sub check_key_and_xml_validity {
472 my ($crypted_msg, $module_key, $session_id) = @_;
473 my $msg;
474 my $msg_hash;
475 my $error_string;
476 eval{
477 $msg = &decrypt_msg($crypted_msg, $module_key);
479 if ($msg =~ /<xml>/i){
480 $msg =~ s/\s+/ /g; # just for better daemon_log
481 daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 8);
482 $msg_hash = $xml->XMLin($msg, ForceArray=>1);
484 ##############
485 # check header
486 if( not exists $msg_hash->{'header'} ) { die "no header specified"; }
487 my $header_l = $msg_hash->{'header'};
488 if( 1 > @{$header_l} ) { die 'empty header tag'; }
489 if( 1 < @{$header_l} ) { die 'more than one header specified'; }
490 my $header = @{$header_l}[0];
491 if( 0 == length $header) { die 'empty string in header tag'; }
493 ##############
494 # check source
495 if( not exists $msg_hash->{'source'} ) { die "no source specified"; }
496 my $source_l = $msg_hash->{'source'};
497 if( 1 > @{$source_l} ) { die 'empty source tag'; }
498 if( 1 < @{$source_l} ) { die 'more than one source specified'; }
499 my $source = @{$source_l}[0];
500 if( 0 == length $source) { die 'source error'; }
502 ##############
503 # check target
504 if( not exists $msg_hash->{'target'} ) { die "no target specified"; }
505 my $target_l = $msg_hash->{'target'};
506 if( 1 > @{$target_l} ) { die 'empty target tag'; }
507 }
508 };
509 if($@) {
510 daemon_log("$session_id DEBUG: do not understand the message: $@", 7);
511 $msg = undef;
512 $msg_hash = undef;
513 }
515 return ($msg, $msg_hash);
516 }
519 sub check_outgoing_xml_validity {
520 my ($msg) = @_;
522 my $msg_hash;
523 eval{
524 $msg_hash = $xml->XMLin($msg, ForceArray=>1);
526 ##############
527 # check header
528 my $header_l = $msg_hash->{'header'};
529 if( 1 != @{$header_l} ) {
530 die 'no or more than one headers specified';
531 }
532 my $header = @{$header_l}[0];
533 if( 0 == length $header) {
534 die 'header has length 0';
535 }
537 ##############
538 # check source
539 my $source_l = $msg_hash->{'source'};
540 if( 1 != @{$source_l} ) {
541 die 'no or more than 1 sources specified';
542 }
543 my $source = @{$source_l}[0];
544 if( 0 == length $source) {
545 die 'source has length 0';
546 }
547 unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
548 $source =~ /^GOSA$/i ) {
549 die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
550 }
552 ##############
553 # check target
554 my $target_l = $msg_hash->{'target'};
555 if( 0 == @{$target_l} ) {
556 die "no targets specified";
557 }
558 foreach my $target (@$target_l) {
559 if( 0 == length $target) {
560 die "target has length 0";
561 }
562 unless( $target =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
563 $target =~ /^GOSA$/i ||
564 $target =~ /^\*$/ ||
565 $target =~ /KNOWN_SERVER/i ||
566 $target =~ /JOBDB/i ||
567 $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 ){
568 die "target '$target' is not a complete ip-address with port or a valid target name or a mac-address";
569 }
570 }
571 };
572 if($@) {
573 daemon_log("WARNING: outgoing msg is not gosa-si envelope conform", 5);
574 daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 8);
575 $msg_hash = undef;
576 }
578 return ($msg_hash);
579 }
582 sub input_from_known_server {
583 my ($input, $remote_ip, $session_id) = @_ ;
584 my ($msg, $msg_hash, $module);
586 my $sql_statement= "SELECT * FROM known_server";
587 my $query_res = $known_server_db->select_dbentry( $sql_statement );
589 while( my ($hit_num, $hit) = each %{ $query_res } ) {
590 my $host_name = $hit->{hostname};
591 if( not $host_name =~ "^$remote_ip") {
592 next;
593 }
594 my $host_key = $hit->{hostkey};
595 daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 7);
596 daemon_log("DEBUG: input_from_known_server: host_key: $host_key", 7);
598 # check if module can open msg envelope with module key
599 my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
600 if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
601 daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 7);
602 daemon_log("$@", 8);
603 next;
604 }
605 else {
606 $msg = $tmp_msg;
607 $msg_hash = $tmp_msg_hash;
608 $module = "SIPackages";
609 last;
610 }
611 }
613 if( (!$msg) || (!$msg_hash) || (!$module) ) {
614 daemon_log("$session_id DEBUG: Incoming message is not from a known server", 7);
615 }
617 return ($msg, $msg_hash, $module);
618 }
621 sub input_from_known_client {
622 my ($input, $remote_ip, $session_id) = @_ ;
623 my ($msg, $msg_hash, $module);
625 my $sql_statement= "SELECT * FROM known_clients";
626 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
627 while( my ($hit_num, $hit) = each %{ $query_res } ) {
628 my $host_name = $hit->{hostname};
629 if( not $host_name =~ /^$remote_ip:\d*$/) {
630 next;
631 }
632 my $host_key = $hit->{hostkey};
633 &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 7);
634 &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 7);
636 # check if module can open msg envelope with module key
637 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
639 if( (!$msg) || (!$msg_hash) ) {
640 &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 7);
641 &daemon_log("$@", 8);
642 next;
643 }
644 else {
645 $module = "SIPackages";
646 last;
647 }
648 }
650 if( (!$msg) || (!$msg_hash) || (!$module) ) {
651 &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 7);
652 }
654 return ($msg, $msg_hash, $module);
655 }
658 sub input_from_unknown_host {
659 no strict "refs";
660 my ($input, $session_id) = @_ ;
661 my ($msg, $msg_hash, $module);
662 my $error_string;
664 my %act_modules = %$known_modules;
666 while( my ($mod, $info) = each(%act_modules)) {
668 # check a key exists for this module
669 my $module_key = ${$mod."_key"};
670 if( not defined $module_key ) {
671 if( $mod eq 'ArpHandler' ) {
672 next;
673 }
674 daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
675 next;
676 }
677 daemon_log("$session_id DEBUG: $mod: $module_key", 7);
679 # check if module can open msg envelope with module key
680 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
681 if( (not defined $msg) || (not defined $msg_hash) ) {
682 next;
683 }
684 else {
685 $module = $mod;
686 last;
687 }
688 }
690 if( (!$msg) || (!$msg_hash) || (!$module)) {
691 daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 7);
692 }
694 return ($msg, $msg_hash, $module);
695 }
698 sub create_ciphering {
699 my ($passwd) = @_;
700 if((!defined($passwd)) || length($passwd)==0) {
701 $passwd = "";
702 }
703 $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
704 my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
705 my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
706 $my_cipher->set_iv($iv);
707 return $my_cipher;
708 }
711 sub encrypt_msg {
712 my ($msg, $key) = @_;
713 my $my_cipher = &create_ciphering($key);
714 my $len;
715 {
716 use bytes;
717 $len= 16-length($msg)%16;
718 }
719 $msg = "\0"x($len).$msg;
720 $msg = $my_cipher->encrypt($msg);
721 chomp($msg = &encode_base64($msg));
722 # there are no newlines allowed inside msg
723 $msg=~ s/\n//g;
724 return $msg;
725 }
728 sub decrypt_msg {
730 my ($msg, $key) = @_ ;
731 $msg = &decode_base64($msg);
732 my $my_cipher = &create_ciphering($key);
733 $msg = $my_cipher->decrypt($msg);
734 $msg =~ s/\0*//g;
735 return $msg;
736 }
739 sub get_encrypt_key {
740 my ($target) = @_ ;
741 my $encrypt_key;
742 my $error = 0;
744 # target can be in known_server
745 if( not defined $encrypt_key ) {
746 my $sql_statement= "SELECT * FROM known_server WHERE hostname='$target'";
747 my $query_res = $known_server_db->select_dbentry( $sql_statement );
748 while( my ($hit_num, $hit) = each %{ $query_res } ) {
749 my $host_name = $hit->{hostname};
750 if( $host_name ne $target ) {
751 next;
752 }
753 $encrypt_key = $hit->{hostkey};
754 last;
755 }
756 }
758 # target can be in known_client
759 if( not defined $encrypt_key ) {
760 my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$target'";
761 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
762 while( my ($hit_num, $hit) = each %{ $query_res } ) {
763 my $host_name = $hit->{hostname};
764 if( $host_name ne $target ) {
765 next;
766 }
767 $encrypt_key = $hit->{hostkey};
768 last;
769 }
770 }
772 return $encrypt_key;
773 }
776 #=== FUNCTION ================================================================
777 # NAME: open_socket
778 # PARAMETERS: PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
779 # [PeerPort] string necessary if port not appended by PeerAddr
780 # RETURNS: socket IO::Socket::INET
781 # DESCRIPTION: open a socket to PeerAddr
782 #===============================================================================
783 sub open_socket {
784 my ($PeerAddr, $PeerPort) = @_ ;
785 if(defined($PeerPort)){
786 $PeerAddr = $PeerAddr.":".$PeerPort;
787 }
788 my $socket;
789 $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
790 Porto => "tcp",
791 Type => SOCK_STREAM,
792 Timeout => 5,
793 );
794 if(not defined $socket) {
795 return;
796 }
797 # &daemon_log("DEBUG: open_socket: $PeerAddr", 7);
798 return $socket;
799 }
802 #=== FUNCTION ================================================================
803 # NAME: get_ip
804 # PARAMETERS: interface name (i.e. eth0)
805 # RETURNS: (ip address)
806 # DESCRIPTION: Uses ioctl to get ip address directly from system.
807 #===============================================================================
808 sub get_ip {
809 my $ifreq= shift;
810 my $result= "";
811 my $SIOCGIFADDR= 0x8915; # man 2 ioctl_list
812 my $proto= getprotobyname('ip');
814 socket SOCKET, PF_INET, SOCK_DGRAM, $proto
815 or die "socket: $!";
817 if(ioctl SOCKET, $SIOCGIFADDR, $ifreq) {
818 my ($if, $sin) = unpack 'a16 a16', $ifreq;
819 my ($port, $addr) = sockaddr_in $sin;
820 my $ip = inet_ntoa $addr;
822 if ($ip && length($ip) > 0) {
823 $result = $ip;
824 }
825 }
827 return $result;
828 }
831 sub get_local_ip_for_remote_ip {
832 my $remote_ip= shift;
833 my $result="0.0.0.0";
835 if($remote_ip =~ /^(\d\d?\d?\.){3}\d\d?\d?$/) {
836 if($remote_ip eq "127.0.0.1") {
837 $result = "127.0.0.1";
838 } else {
839 my $PROC_NET_ROUTE= ('/proc/net/route');
841 open(PROC_NET_ROUTE, "<$PROC_NET_ROUTE")
842 or die "Could not open $PROC_NET_ROUTE";
844 my @ifs = <PROC_NET_ROUTE>;
846 close(PROC_NET_ROUTE);
848 # Eat header line
849 shift @ifs;
850 chomp @ifs;
851 foreach my $line(@ifs) {
852 my ($Iface,$Destination,$Gateway,$Flags,$RefCnt,$Use,$Metric,$Mask,$MTU,$Window,$IRTT)=split(/\s/, $line);
853 my $destination;
854 my $mask;
855 my ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Destination);
856 $destination= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
857 ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Mask);
858 $mask= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
859 if(new NetAddr::IP($remote_ip)->within(new NetAddr::IP($destination, $mask))) {
860 # destination matches route, save mac and exit
861 $result= &get_ip($Iface);
862 last;
863 }
864 }
865 }
866 } else {
867 daemon_log("get_local_ip_for_remote_ip was called with a non-ip parameter: $remote_ip", 1);
868 }
869 return $result;
870 }
873 sub send_msg_to_target {
874 my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
875 my $error = 0;
876 my $header;
877 my $new_status;
878 my $act_status;
879 my ($sql_statement, $res);
881 if( $msg_header ) {
882 $header = "'$msg_header'-";
883 } else {
884 $header = "";
885 }
887 # Patch the source ip
888 if($msg =~ /<source>0\.0\.0\.0:\d*?<\/source>/) {
889 my $remote_ip = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
890 $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$remote_ip:$2<\/source>/s;
891 }
893 # encrypt xml msg
894 my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
896 # opensocket
897 my $socket = &open_socket($address);
898 if( !$socket ) {
899 daemon_log("$session_id ERROR: cannot send ".$header."msg to $address , host not reachable", 1);
900 $error++;
901 }
903 if( $error == 0 ) {
904 # send xml msg
905 print $socket $crypted_msg."\n";
907 daemon_log("$session_id INFO: send ".$header."msg to $address", 5);
908 #daemon_log("DEBUG: message:\n$msg", 9);
910 }
912 # close socket in any case
913 if( $socket ) {
914 close $socket;
915 }
917 if( $error > 0 ) { $new_status = "down"; }
918 else { $new_status = $msg_header; }
921 # known_clients
922 $sql_statement = "SELECT * FROM known_clients WHERE hostname='$address'";
923 $res = $known_clients_db->select_dbentry($sql_statement);
924 if( keys(%$res) > 0) {
925 $act_status = $res->{1}->{'status'};
926 if( $act_status eq "down" ) {
927 $sql_statement = "DELETE FROM known_clients WHERE hostname='$address'";
928 $res = $known_clients_db->del_dbentry($sql_statement);
929 daemon_log("$session_id WARNING: failed 2x to send msg to host '$address', delete host from known_clients", 3);
930 } else {
931 $sql_statement = "UPDATE known_clients SET status='$new_status' WHERE hostname='$address'";
932 $res = $known_clients_db->update_dbentry($sql_statement);
933 if($new_status eq "down"){
934 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
935 } else {
936 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
937 }
938 }
939 }
941 # known_server
942 $sql_statement = "SELECT * FROM known_server WHERE hostname='$address'";
943 $res = $known_server_db->select_dbentry($sql_statement);
944 if( keys(%$res) > 0 ) {
945 $act_status = $res->{1}->{'status'};
946 if( $act_status eq "down" ) {
947 $sql_statement = "DELETE FROM known_server WHERE hostname='$address'";
948 $res = $known_server_db->del_dbentry($sql_statement);
949 daemon_log("$session_id WARNING: failed 2x to a send msg to host '$address', delete host from known_server", 3);
950 }
951 else {
952 $sql_statement = "UPDATE known_server SET status='$new_status' WHERE hostname='$address'";
953 $res = $known_server_db->update_dbentry($sql_statement);
954 if($new_status eq "down"){
955 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
956 }
957 else {
958 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
959 }
960 }
961 }
962 return $error;
963 }
966 sub update_jobdb_status_for_send_msgs {
967 my ($answer, $error) = @_;
968 if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
969 my $jobdb_id = $1;
971 # sending msg faild
972 if( $error ) {
973 if (not $answer =~ /<header>trigger_action_reinstall<\/header>/) {
974 my $sql_statement = "UPDATE $job_queue_tn ".
975 "SET status='error', result='can not deliver msg, please consult log file' ".
976 "WHERE id=$jobdb_id";
977 my $res = $job_db->update_dbentry($sql_statement);
978 }
980 # sending msg was successful
981 } else {
982 my $sql_statement = "UPDATE $job_queue_tn ".
983 "SET status='done' ".
984 "WHERE id=$jobdb_id AND status='processed'";
985 my $res = $job_db->update_dbentry($sql_statement);
986 }
987 }
988 }
990 sub _start {
991 my ($kernel) = $_[KERNEL];
992 &trigger_db_loop($kernel);
993 $global_kernel = $kernel;
994 $kernel->yield('create_fai_server_db', $fai_server_tn );
995 $kernel->yield('create_fai_release_db', $fai_release_tn );
996 $kernel->sig(USR1 => "sig_handler");
997 $kernel->sig(USR2 => "create_packages_list_db");
998 }
1000 sub sig_handler {
1001 my ($kernel, $signal) = @_[KERNEL, ARG0] ;
1002 daemon_log("0 INFO got signal '$signal'", 1);
1003 $kernel->sig_handled();
1004 return;
1005 }
1008 sub msg_to_decrypt {
1009 my ($session, $heap) = @_[SESSION, HEAP];
1010 my $session_id = $session->ID;
1011 my ($msg, $msg_hash, $module);
1012 my $error = 0;
1014 # hole neue msg aus @msgs_to_decrypt
1015 my $next_msg = shift @msgs_to_decrypt;
1017 # entschlüssle sie
1019 # msg is from a new client or gosa
1020 ($msg, $msg_hash, $module) = &input_from_unknown_host($next_msg, $session_id);
1021 # msg is from a gosa-si-server or gosa-si-bus
1022 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1023 ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $heap->{'remote_ip'}, $session_id);
1024 }
1025 # msg is from a gosa-si-client
1026 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1027 ($msg, $msg_hash, $module) = &input_from_known_client($next_msg, $heap->{'remote_ip'}, $session_id);
1028 }
1029 # an error occurred
1030 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1031 # if an incoming msg could not be decrypted (maybe a wrong key), send client a ping. If the client
1032 # could not understand a msg from its server the client cause a re-registering process
1033 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);
1034 my $sql_statement = "SELECT * FROM $main::known_clients_tn WHERE (hostname LIKE '".$heap->{'remote_ip'}."%')";
1035 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1036 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1037 my $host_name = $hit->{'hostname'};
1038 my $host_key = $hit->{'hostkey'};
1039 my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source> <target>$host_name</target></xml>";
1040 my $error = &send_msg_to_target($ping_msg, $host_name, $host_key, "gosa_ping", $session_id);
1041 &update_jobdb_status_for_send_msgs($ping_msg, $error);
1042 }
1043 $error++;
1044 }
1046 # schiebe sie in incoming_db
1047 if( $error == 0) {
1048 my $header = @{$msg_hash->{'header'}}[0];
1049 my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1050 primkey=>[],
1051 headertag=>$header,
1052 xmlmessage=>$msg,
1053 timestamp=>&get_time,
1054 module=>$module,
1055 } );
1056 if ($res != 0) {
1057 &daemon_log();
1058 }
1059 }
1061 }
1064 sub next_task {
1065 my ($session, $heap) = @_[SESSION, HEAP];
1066 # if (keys( %{ $heap->{task} } ) < $max_children ) {
1067 my $task = POE::Wheel::Run->new(
1068 Program => sub { process_task($session, $heap) },
1069 StdioFilter => POE::Filter::Reference->new(),
1070 StdoutEvent => "task_result",
1071 StderrEvent => "task_debug",
1072 CloseEvent => "task_done",
1073 );
1075 $heap->{task}->{ $task->ID } = $task;
1076 # }
1077 }
1079 sub handle_task_result {
1080 my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
1081 my $client_answer = $result->{'answer'};
1082 if( $client_answer =~ s/session_id=(\d+)$// ) {
1083 my $session_id = $1;
1084 if( defined $session_id ) {
1085 my $session_reference = $kernel->ID_id_to_session($session_id);
1086 if( defined $session_reference ) {
1087 $heap = $session_reference->get_heap();
1088 }
1089 }
1091 if(exists $heap->{'client'}) {
1092 $heap->{'client'}->put($client_answer);
1093 }
1094 }
1095 $kernel->sig(CHLD => "child_reap");
1096 }
1098 sub handle_task_debug {
1099 my $result = $_[ARG0];
1100 print STDERR "$result\n";
1101 }
1103 sub handle_task_done {
1104 my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1105 delete $heap->{task}->{$task_id};
1106 }
1108 sub process_task {
1109 no strict "refs";
1110 my ($session, $heap, $input) = @_;
1111 my $session_id = $session->ID;
1112 my $error = 0;
1113 my $answer_l;
1114 my ($answer_header, @answer_target_l, $answer_source);
1115 my $client_answer = "";
1117 daemon_log("", 5);
1118 daemon_log("$session_id INFO: Incoming msg with session ID $session_id from '".$heap->{'remote_ip'}."'", 5);
1119 #daemon_log("$session_id DEBUG: Incoming msg:\n$input", 9);
1121 # ####################
1122 # # check incoming msg
1123 # # msg is from a new client or gosa
1124 # ($msg, $msg_hash, $module) = &input_from_unknown_host($input, $session_id);
1125 # # msg is from a gosa-si-server or gosa-si-bus
1126 # if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1127 # ($msg, $msg_hash, $module) = &input_from_known_server($input, $heap->{'remote_ip'}, $session_id);
1128 # }
1129 # # msg is from a gosa-si-client
1130 # if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1131 # ($msg, $msg_hash, $module) = &input_from_known_client($input, $heap->{'remote_ip'}, $session_id);
1132 # }
1133 # # an error occurred
1134 # if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1135 # # if an incoming msg could not be decrypted (maybe a wrong key), send client a ping. If the client
1136 # # could not understand a msg from its server the client cause a re-registering process
1137 # 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);
1138 # my $sql_statement = "SELECT * FROM $main::known_clients_tn WHERE (hostname LIKE '".$heap->{'remote_ip'}."%')";
1139 # my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1140 # while( my ($hit_num, $hit) = each %{ $query_res } ) {
1141 # my $host_name = $hit->{'hostname'};
1142 # my $host_key = $hit->{'hostkey'};
1143 # my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source> <target>$host_name</target></xml>";
1144 # my $error = &send_msg_to_target($ping_msg, $host_name, $host_key, "gosa_ping", $session_id);
1145 # &update_jobdb_status_for_send_msgs($ping_msg, $error);
1146 # }
1147 # $error++;
1148 # }
1151 # sometimes the program is faster than sqlite, so wait until informations are present at db
1152 my $id_sql;
1153 my $id_res;
1154 my $message_id;
1155 while (1) {
1156 $id_sql = "SELECT min(id) FROM $incoming_tn WHERE (NOT headertag LIKE 'answer%')";
1157 $id_res = $incoming_db->exec_statement($id_sql);
1158 $message_id = @{@$id_res[0]}[0];
1159 if (defined $message_id) { last }
1160 }
1162 print STDERR "min(id): $message_id\n";
1165 # fetch new message from incoming_db
1166 my $sql = "SELECT * FROM $incoming_tn WHERE id=$message_id";
1167 my $res = $incoming_db->exec_statement($sql);
1169 # prepare all variables needed to process message
1170 my $msg = @{@$res[0]}[3];
1171 my $incoming_id = @{@$res[0]}[0];
1172 my $module = @{@$res[0]}[4];
1173 my $header = @{@$res[0]}[2];
1174 my $msg_hash = $xml->XMLin($msg, ForceArray=>1);
1176 # messages which are an answer to a still running process should not be processed here
1177 if ($header =~ /^answer_(\d+)/) {
1178 return;
1179 }
1181 # delete message from db
1182 my $delete_sql = "DELETE FROM $incoming_tn WHERE id=$incoming_id";
1183 my $delete_res = $incoming_db->exec_statement($delete_sql);
1185 ######################
1186 # process incoming msg
1187 if( $error == 0) {
1188 daemon_log("$session_id INFO: Incoming msg with header '".@{$msg_hash->{'header'}}[0].
1189 "' from '".$heap->{'remote_ip'}."'", 5);
1190 daemon_log("$session_id DEBUG: Processing module ".$module, 7);
1191 $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1193 if ( 0 < @{$answer_l} ) {
1194 my $answer_str = join("\n", @{$answer_l});
1195 daemon_log("$session_id DEBUG: $module: Got answer from module: \n".$answer_str,8);
1196 } else {
1197 daemon_log("$session_id DEBUG: $module: Got no answer from module!" ,8);
1198 }
1200 }
1201 if( !$answer_l ) { $error++ };
1203 ########
1204 # answer
1205 if( $error == 0 ) {
1207 foreach my $answer ( @{$answer_l} ) {
1208 # for each answer in answer list
1210 # check outgoing msg to xml validity
1211 my $answer_hash = &check_outgoing_xml_validity($answer);
1212 if( not defined $answer_hash ) {
1213 next;
1214 }
1216 $answer_header = @{$answer_hash->{'header'}}[0];
1217 @answer_target_l = @{$answer_hash->{'target'}};
1218 $answer_source = @{$answer_hash->{'source'}}[0];
1220 # deliver msg to all targets
1221 foreach my $answer_target ( @answer_target_l ) {
1223 # targets of msg are all gosa-si-clients in known_clients_db
1224 if( $answer_target eq "*" ) {
1225 # answer is for all clients
1226 my $sql_statement= "SELECT * FROM known_clients";
1227 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1228 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1229 my $host_name = $hit->{hostname};
1230 my $host_key = $hit->{hostkey};
1231 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1232 &update_jobdb_status_for_send_msgs($answer, $error);
1233 }
1234 }
1236 # targets of msg are all gosa-si-server in known_server_db
1237 elsif( $answer_target eq "KNOWN_SERVER" ) {
1238 # answer is for all server in known_server
1239 my $sql_statement= "SELECT * FROM known_server";
1240 my $query_res = $known_server_db->select_dbentry( $sql_statement );
1241 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1242 my $host_name = $hit->{hostname};
1243 my $host_key = $hit->{hostkey};
1244 $answer =~ s/KNOWN_SERVER/$host_name/g;
1245 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1246 &update_jobdb_status_for_send_msgs($answer, $error);
1247 }
1248 }
1250 # target of msg is GOsa
1251 elsif( $answer_target eq "GOSA" ) {
1252 my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1253 my $add_on = "";
1254 if( defined $session_id ) {
1255 $add_on = ".session_id=$session_id";
1256 }
1257 # answer is for GOSA and has to returned to connected client
1258 my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1259 $client_answer = $gosa_answer.$add_on;
1260 }
1262 # target of msg is job queue at this host
1263 elsif( $answer_target eq "JOBDB") {
1264 $answer =~ /<header>(\S+)<\/header>/;
1265 my $header;
1266 if( defined $1 ) { $header = $1; }
1267 my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1268 &update_jobdb_status_for_send_msgs($answer, $error);
1269 }
1271 # target of msg is a mac address
1272 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 ) {
1273 daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients", 5);
1274 my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1275 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1276 my $found_ip_flag = 0;
1277 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1278 my $host_name = $hit->{hostname};
1279 my $host_key = $hit->{hostkey};
1280 $answer =~ s/$answer_target/$host_name/g;
1281 daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1282 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1283 &update_jobdb_status_for_send_msgs($answer, $error);
1284 $found_ip_flag++ ;
1285 }
1286 if( $found_ip_flag == 0) {
1287 daemon_log("$session_id WARNING: no host found in known_clients with mac address '$answer_target'", 3);
1288 if( $bus_activ eq "true" ) {
1289 daemon_log("$session_id INFO: try to forward msg '$answer_header' to bus '$bus_address'", 5);
1290 my $sql_statement = "SELECT * FROM known_server WHERE hostname='$bus_address'";
1291 my $query_res = $known_server_db->select_dbentry( $sql_statement );
1292 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1293 my $bus_address = $hit->{hostname};
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 last;
1298 }
1299 }
1301 }
1303 # answer is for one specific host
1304 } else {
1305 # get encrypt_key
1306 my $encrypt_key = &get_encrypt_key($answer_target);
1307 if( not defined $encrypt_key ) {
1308 # unknown target, forward msg to bus
1309 daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1310 if( $bus_activ eq "true" ) {
1311 daemon_log("$session_id INFO: try to forward msg '$answer_header' to bus '$bus_address'", 5);
1312 my $sql_statement = "SELECT * FROM known_server WHERE hostname='$bus_address'";
1313 my $query_res = $known_server_db->select_dbentry( $sql_statement );
1314 my $res_length = keys( %{$query_res} );
1315 if( $res_length == 0 ){
1316 daemon_log("$session_id WARNING: send '$answer_header' to '$bus_address' failed, ".
1317 "no bus found in known_server", 3);
1318 }
1319 else {
1320 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1321 my $bus_key = $hit->{hostkey};
1322 my $error = &send_msg_to_target($answer, $bus_address, $bus_key, $answer_header,$session_id );
1323 &update_jobdb_status_for_send_msgs($answer, $error);
1324 }
1325 }
1326 }
1327 next;
1328 }
1329 my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1330 &update_jobdb_status_for_send_msgs($answer, $error);
1331 }
1332 }
1333 }
1334 }
1336 my $filter = POE::Filter::Reference->new();
1337 my %result = (
1338 status => "seems ok to me",
1339 answer => $client_answer,
1340 );
1342 my $output = $filter->put( [ \%result ] );
1343 print @$output;
1346 }
1349 sub trigger_db_loop {
1350 my ($kernel) = @_ ;
1351 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1352 $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay);
1353 $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1354 $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1355 $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1356 }
1359 sub watch_for_done_jobs {
1360 my ($kernel,$heap) = @_[KERNEL, HEAP];
1362 my $sql_statement = "SELECT * FROM ".$job_queue_tn.
1363 " WHERE status='done'";
1364 my $res = $job_db->select_dbentry( $sql_statement );
1366 while( my ($id, $hit) = each %{$res} ) {
1367 my $jobdb_id = $hit->{id};
1368 my $sql_statement = "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1369 my $res = $job_db->del_dbentry($sql_statement);
1370 }
1372 $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1373 }
1376 sub watch_for_new_jobs {
1377 if($watch_for_new_jobs_in_progress == 0) {
1378 $watch_for_new_jobs_in_progress = 1;
1379 my ($kernel,$heap) = @_[KERNEL, HEAP];
1381 # check gosa job queue for jobs with executable timestamp
1382 my $timestamp = &get_time();
1383 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE status='waiting' AND (CAST (timestamp AS INTEGER)) < $timestamp ORDER BY timestamp";
1384 my $res = $job_db->exec_statement( $sql_statement );
1386 # Merge all new jobs that would do the same actions
1387 my @drops;
1388 my $hits;
1389 foreach my $hit (reverse @{$res} ) {
1390 my $macaddress= lc @{$hit}[8];
1391 my $headertag= @{$hit}[5];
1392 if(
1393 defined($hits->{$macaddress}) &&
1394 defined($hits->{$macaddress}->{$headertag}) &&
1395 defined($hits->{$macaddress}->{$headertag}[0])
1396 ) {
1397 push @drops, "DELETE FROM $job_queue_tn WHERE id = $hits->{$macaddress}->{$headertag}[0]";
1398 }
1399 $hits->{$macaddress}->{$headertag}= $hit;
1400 }
1402 # Delete new jobs with a matching job in state 'processing'
1403 foreach my $macaddress (keys %{$hits}) {
1404 foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1405 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1406 if(defined($jobdb_id)) {
1407 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND headertag='$jobdb_headertag' AND status='processing'";
1408 my $res = $job_db->exec_statement( $sql_statement );
1409 foreach my $hit (@{$res}) {
1410 push @drops, "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1411 }
1412 } else {
1413 daemon_log("J ERROR: Job without id exists for macaddress $macaddress!", 1);
1414 }
1415 }
1416 }
1418 # Commit deletion
1419 $job_db->exec_statementlist(\@drops);
1421 # Look for new jobs that could be executed
1422 foreach my $macaddress (keys %{$hits}) {
1424 # Look if there is an executing job
1425 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='processing'";
1426 my $res = $job_db->exec_statement( $sql_statement );
1428 # Skip new jobs for host if there is a processing job
1429 if(defined($res) and defined @{$res}[0]) {
1430 next;
1431 }
1433 foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1434 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1435 if(defined($jobdb_id)) {
1436 my $job_msg = @{$hits->{$macaddress}->{$jobdb_headertag}}[7];
1438 daemon_log("J DEBUG: its time to execute $job_msg", 7);
1439 my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1440 my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1442 # expect macaddress is unique!!!!!!
1443 my $target = $res_hash->{1}->{hostname};
1445 # change header
1446 $job_msg =~ s/<header>job_/<header>gosa_/;
1448 # add sqlite_id
1449 $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1451 $job_msg =~ /<header>(\S+)<\/header>/;
1452 my $header = $1 ;
1453 my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");
1455 # update status in job queue to 'processing'
1456 $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
1457 my $res = $job_db->update_dbentry($sql_statement);
1458 # TODO: abfangen ob alles in ordnung ist oder nicht, wenn nicht error schmeißen
1460 # We don't want parallel processing
1461 last;
1462 }
1463 }
1464 }
1466 $watch_for_new_jobs_in_progress = 0;
1467 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1468 }
1469 }
1472 sub watch_for_new_messages {
1473 my ($kernel,$heap) = @_[KERNEL, HEAP];
1474 my @coll_user_msg; # collection list of outgoing messages
1476 # check messaging_db for new incoming messages with executable timestamp
1477 my $timestamp = &get_time();
1478 my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS INTEGER))<$timestamp AND flag='n' AND direction='in' )";
1479 my $res = $messaging_db->exec_statement( $sql_statement );
1480 foreach my $hit (@{$res}) {
1482 # create outgoing messages
1483 my $message_to = @{$hit}[3];
1484 # translate message_to to plain login name
1485 my @message_to_l = split(/,/, $message_to);
1486 my %receiver_h;
1487 foreach my $receiver (@message_to_l) {
1488 if ($receiver =~ /^u_([\s\S]*)$/) {
1489 $receiver_h{$1} = 0;
1490 } elsif ($receiver =~ /^g_([\s\S]*)$/) {
1491 # TODO implement receiver translation
1492 } else {
1493 my $sbjct = &encode_base64(@{$hit}[1]);
1494 my $msg = &encode_base64(@{$hit}[7]);
1495 &daemon_log("M WARNING: unknown receiver '$receiver' for a user-message 'sbjct - msg'", 3);
1496 }
1497 }
1498 my @receiver_l = keys(%receiver_h);
1500 my $message_id = @{$hit}[0];
1502 #add each outgoing msg to messaging_db
1503 my $receiver;
1504 foreach $receiver (@receiver_l) {
1505 my $sql_statement = "INSERT INTO $messaging_tn (id, subject, message_from, message_to, flag, direction, delivery_time, message, timestamp) ".
1506 "VALUES ('".
1507 $message_id."', '". # id
1508 @{$hit}[1]."', '". # subject
1509 @{$hit}[2]."', '". # message_from
1510 $receiver."', '". # message_to
1511 "none"."', '". # flag
1512 "out"."', '". # direction
1513 @{$hit}[6]."', '". # delivery_time
1514 @{$hit}[7]."', '". # message
1515 $timestamp."'". # timestamp
1516 ")";
1517 &daemon_log("M DEBUG: $sql_statement", 1);
1518 my $res = $messaging_db->exec_statement($sql_statement);
1519 &daemon_log("M INFO: message '".@{$hit}[0]."' is prepared for delivery to receiver '$receiver'", 5);
1520 }
1522 # set incoming message to flag d=deliverd
1523 $sql_statement = "UPDATE $messaging_tn SET flag='p' WHERE id='$message_id'";
1524 &daemon_log("M DEBUG: $sql_statement", 7);
1525 $res = $messaging_db->update_dbentry($sql_statement);
1526 &daemon_log("M INFO: message '$message_id' is set to flag 'p' (processed)", 5);
1527 }
1529 $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1530 return;
1531 }
1533 sub watch_for_delivery_messages {
1534 my ($kernel, $heap) = @_[KERNEL, HEAP];
1536 # select outgoing messages
1537 my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( flag='p' AND direction='out' )";
1538 #&daemon_log("0 DEBUG: $sql", 7);
1539 my $res = $messaging_db->exec_statement( $sql_statement );
1541 # build out msg for each usr
1542 foreach my $hit (@{$res}) {
1543 my $receiver = @{$hit}[3];
1544 my $msg_id = @{$hit}[0];
1545 my $subject = @{$hit}[1];
1546 my $message = @{$hit}[7];
1548 # resolve usr -> host where usr is logged in
1549 my $sql = "SELECT * FROM $login_users_tn WHERE (user='$receiver')";
1550 #&daemon_log("0 DEBUG: $sql", 7);
1551 my $res = $login_users_db->exec_statement($sql);
1553 # reciver is logged in nowhere
1554 if (not ref(@$res[0]) eq "ARRAY") { next; }
1556 my $send_succeed = 0;
1557 foreach my $hit (@$res) {
1558 my $receiver_host = @$hit[0];
1559 &daemon_log("M DEBUG: user '$receiver' is logged in at host '$receiver_host'", 7);
1561 # fetch key to encrypt msg propperly for usr/host
1562 my $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$receiver_host')";
1563 &daemon_log("0 DEBUG: $sql", 7);
1564 my $res = $known_clients_db->exec_statement($sql);
1566 # host is already down
1567 if (not ref(@$res[0]) eq "ARRAY") { next; }
1569 # host is on
1570 my $receiver_key = @{@{$res}[0]}[2];
1571 my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
1572 my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data );
1573 my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0);
1574 if ($error == 0 ) {
1575 $send_succeed++ ;
1576 }
1577 }
1579 if ($send_succeed) {
1580 # set outgoing msg at db to deliverd
1581 my $sql = "UPDATE $messaging_tn SET flag='d' WHERE (id='$msg_id' AND direction='out' AND message_to='$receiver')";
1582 &daemon_log("0 DEBUG: $sql", 7);
1583 my $res = $messaging_db->exec_statement($sql);
1584 }
1585 }
1587 $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1588 return;
1589 }
1592 sub watch_for_done_messages {
1593 my ($kernel,$heap) = @_[KERNEL, HEAP];
1595 my $sql = "SELECT * FROM $messaging_tn WHERE (flag='p' AND direction='in')";
1596 #&daemon_log("0 DEBUG: $sql", 7);
1597 my $res = $messaging_db->exec_statement($sql);
1599 foreach my $hit (@{$res}) {
1600 my $msg_id = @{$hit}[0];
1602 my $sql = "SELECT * FROM $messaging_tn WHERE (id='$msg_id' AND direction='out' AND (NOT flag='s'))";
1603 #&daemon_log("0 DEBUG: $sql", 7);
1604 my $res = $messaging_db->exec_statement($sql);
1606 # not all usr msgs have been seen till now
1607 if ( ref(@$res[0]) eq "ARRAY") { next; }
1609 $sql = "DELETE FROM $messaging_tn WHERE (id='$msg_id')";
1610 #&daemon_log("0 DEBUG: $sql", 7);
1611 $res = $messaging_db->exec_statement($sql);
1613 }
1615 $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1616 return;
1617 }
1620 sub get_ldap_handle {
1621 my ($session_id) = @_;
1622 my $heap;
1623 my $ldap_handle;
1625 if (not defined $session_id ) { $session_id = 0 };
1626 if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
1628 if ($session_id == 0) {
1629 daemon_log("$session_id DEBUG: get_ldap_handle invoked without a session_id, create a new ldap_handle", 7);
1630 $ldap_handle = Net::LDAP->new( $ldap_uri );
1631 $ldap_handle->bind($ldap_admin_dn, password => $ldap_admin_password);
1633 } else {
1634 my $session_reference = $global_kernel->ID_id_to_session($session_id);
1635 if( defined $session_reference ) {
1636 $heap = $session_reference->get_heap();
1637 }
1639 if (not defined $heap) {
1640 daemon_log("$session_id DEBUG: cannot get heap for session_id '$session_id'", 7);
1641 return;
1642 }
1644 # TODO: This "if" is nonsense, because it doesn't prove that the
1645 # used handle is still valid - or if we've to reconnect...
1646 #if (not exists $heap->{ldap_handle}) {
1647 $ldap_handle = Net::LDAP->new( $ldap_uri );
1648 $ldap_handle->bind($ldap_admin_dn, password => $ldap_admin_password);
1649 $heap->{ldap_handle} = $ldap_handle;
1650 #}
1651 }
1652 return $ldap_handle;
1653 }
1656 sub change_fai_state {
1657 my ($st, $targets, $session_id) = @_;
1658 $session_id = 0 if not defined $session_id;
1659 # Set FAI state to localboot
1660 my %mapActions= (
1661 reboot => '',
1662 update => 'softupdate',
1663 localboot => 'localboot',
1664 reinstall => 'install',
1665 rescan => '',
1666 wake => '',
1667 memcheck => 'memcheck',
1668 sysinfo => 'sysinfo',
1669 install => 'install',
1670 );
1672 # Return if this is unknown
1673 if (!exists $mapActions{ $st }){
1674 daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1);
1675 return;
1676 }
1678 my $state= $mapActions{ $st };
1680 my $ldap_handle = &get_ldap_handle($session_id);
1681 if( defined($ldap_handle) ) {
1683 # Build search filter for hosts
1684 my $search= "(&(objectClass=GOhard)";
1685 foreach (@{$targets}){
1686 $search.= "(macAddress=$_)";
1687 }
1688 $search.= ")";
1690 # If there's any host inside of the search string, procress them
1691 if (!($search =~ /macAddress/)){
1692 daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);
1693 return;
1694 }
1696 # Perform search for Unit Tag
1697 my $mesg = $ldap_handle->search(
1698 base => $ldap_base,
1699 scope => 'sub',
1700 attrs => ['dn', 'FAIstate', 'objectClass'],
1701 filter => "$search"
1702 );
1704 if ($mesg->count) {
1705 my @entries = $mesg->entries;
1706 foreach my $entry (@entries) {
1707 # Only modify entry if it is not set to '$state'
1708 if ($entry->get_value("FAIstate") ne "$state"){
1709 daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
1710 my $result;
1711 my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
1712 if (exists $tmp{'FAIobject'}){
1713 if ($state eq ''){
1714 $result= $ldap_handle->modify($entry->dn, changes => [
1715 delete => [ FAIstate => [] ] ]);
1716 } else {
1717 $result= $ldap_handle->modify($entry->dn, changes => [
1718 replace => [ FAIstate => $state ] ]);
1719 }
1720 } elsif ($state ne ''){
1721 $result= $ldap_handle->modify($entry->dn, changes => [
1722 add => [ objectClass => 'FAIobject' ],
1723 add => [ FAIstate => $state ] ]);
1724 }
1726 # Errors?
1727 if ($result->code){
1728 daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
1729 }
1730 } else {
1731 daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 7);
1732 }
1733 }
1734 }
1735 # if no ldap handle defined
1736 } else {
1737 daemon_log("$session_id ERROR: no LDAP handle defined for update FAIstate", 1);
1738 }
1740 }
1743 sub change_goto_state {
1744 my ($st, $targets, $session_id) = @_;
1745 $session_id = 0 if not defined $session_id;
1747 # Switch on or off?
1748 my $state= $st eq 'active' ? 'active': 'locked';
1750 my $ldap_handle = &get_ldap_handle($session_id);
1751 if( defined($ldap_handle) ) {
1753 # Build search filter for hosts
1754 my $search= "(&(objectClass=GOhard)";
1755 foreach (@{$targets}){
1756 $search.= "(macAddress=$_)";
1757 }
1758 $search.= ")";
1760 # If there's any host inside of the search string, procress them
1761 if (!($search =~ /macAddress/)){
1762 return;
1763 }
1765 # Perform search for Unit Tag
1766 my $mesg = $ldap_handle->search(
1767 base => $ldap_base,
1768 scope => 'sub',
1769 attrs => ['dn', 'gotoMode'],
1770 filter => "$search"
1771 );
1773 if ($mesg->count) {
1774 my @entries = $mesg->entries;
1775 foreach my $entry (@entries) {
1777 # Only modify entry if it is not set to '$state'
1778 if ($entry->get_value("gotoMode") ne $state){
1780 daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
1781 my $result;
1782 $result= $ldap_handle->modify($entry->dn, changes => [
1783 replace => [ gotoMode => $state ] ]);
1785 # Errors?
1786 if ($result->code){
1787 &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
1788 }
1790 }
1791 }
1792 }
1794 }
1795 }
1798 sub run_create_fai_server_db {
1799 my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
1800 my $session_id = $session->ID;
1801 my $task = POE::Wheel::Run->new(
1802 Program => sub { &create_fai_server_db($table_name,$kernel, undef, $session_id) },
1803 StdoutEvent => "session_run_result",
1804 StderrEvent => "session_run_debug",
1805 CloseEvent => "session_run_done",
1806 );
1808 $heap->{task}->{ $task->ID } = $task;
1809 return;
1810 }
1813 sub create_fai_server_db {
1814 my ($table_name, $kernel, $dont_create_packages_list, $session_id) = @_;
1815 my $result;
1817 if (not defined $session_id) { $session_id = 0; }
1818 my $ldap_handle = &get_ldap_handle();
1819 if(defined($ldap_handle)) {
1820 daemon_log("$session_id INFO: create_fai_server_db: start", 5);
1821 my $mesg= $ldap_handle->search(
1822 base => $ldap_base,
1823 scope => 'sub',
1824 attrs => ['FAIrepository', 'gosaUnitTag'],
1825 filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
1826 );
1827 if($mesg->{'resultCode'} == 0 &&
1828 $mesg->count != 0) {
1829 foreach my $entry (@{$mesg->{entries}}) {
1830 if($entry->exists('FAIrepository')) {
1831 # Add an entry for each Repository configured for server
1832 foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
1833 my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
1834 my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
1835 $result= $fai_server_db->add_dbentry( {
1836 table => $table_name,
1837 primkey => ['server', 'release', 'tag'],
1838 server => $tmp_url,
1839 release => $tmp_release,
1840 sections => $tmp_sections,
1841 tag => (length($tmp_tag)>0)?$tmp_tag:"",
1842 } );
1843 }
1844 }
1845 }
1846 }
1847 daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
1849 # TODO: Find a way to post the 'create_packages_list_db' event
1850 if(not defined($dont_create_packages_list)) {
1851 &create_packages_list_db(undef, undef, $session_id);
1852 }
1853 }
1855 $ldap_handle->disconnect;
1856 return $result;
1857 }
1860 sub run_create_fai_release_db {
1861 my ($session, $heap, $table_name) = @_[SESSION, HEAP, ARG0];
1862 my $session_id = $session->ID;
1863 my $task = POE::Wheel::Run->new(
1864 Program => sub { &create_fai_release_db($table_name, $session_id) },
1865 StdoutEvent => "session_run_result",
1866 StderrEvent => "session_run_debug",
1867 CloseEvent => "session_run_done",
1868 );
1870 $heap->{task}->{ $task->ID } = $task;
1871 return;
1872 }
1875 sub create_fai_release_db {
1876 my ($table_name, $session_id) = @_;
1877 my $result;
1879 # used for logging
1880 if (not defined $session_id) { $session_id = 0; }
1882 my $ldap_handle = &get_ldap_handle();
1883 if(defined($ldap_handle)) {
1884 daemon_log("$session_id INFO: create_fai_release_db: start",5);
1885 my $mesg= $ldap_handle->search(
1886 base => $ldap_base,
1887 scope => 'sub',
1888 attrs => [],
1889 filter => "(&(objectClass=organizationalUnit)(ou=fai))",
1890 );
1891 if($mesg->{'resultCode'} == 0 &&
1892 $mesg->count != 0) {
1893 # Walk through all possible FAI container ou's
1894 my @sql_list;
1895 my $timestamp= &get_time();
1896 foreach my $ou (@{$mesg->{entries}}) {
1897 my $tmp_classes= resolve_fai_classes($ou->dn, $ldap_handle, $session_id);
1898 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
1899 my @tmp_array=get_fai_release_entries($tmp_classes);
1900 if(@tmp_array) {
1901 foreach my $entry (@tmp_array) {
1902 if(defined($entry) && ref($entry) eq 'HASH') {
1903 my $sql=
1904 "INSERT INTO $table_name "
1905 ."(timestamp, release, class, type, state) VALUES ("
1906 .$timestamp.","
1907 ."'".$entry->{'release'}."',"
1908 ."'".$entry->{'class'}."',"
1909 ."'".$entry->{'type'}."',"
1910 ."'".$entry->{'state'}."')";
1911 push @sql_list, $sql;
1912 }
1913 }
1914 }
1915 }
1916 }
1918 daemon_log("$session_id DEBUG: Inserting ".scalar @sql_list." entries to DB",8);
1919 if(@sql_list) {
1920 unshift @sql_list, "VACUUM";
1921 unshift @sql_list, "DELETE FROM $table_name";
1922 $fai_release_db->exec_statementlist(\@sql_list);
1923 }
1924 daemon_log("$session_id DEBUG: Done with inserting",7);
1925 }
1926 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
1927 }
1928 $ldap_handle->disconnect;
1929 return $result;
1930 }
1932 sub get_fai_types {
1933 my $tmp_classes = shift || return undef;
1934 my @result;
1936 foreach my $type(keys %{$tmp_classes}) {
1937 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
1938 my $entry = {
1939 type => $type,
1940 state => $tmp_classes->{$type}[0],
1941 };
1942 push @result, $entry;
1943 }
1944 }
1946 return @result;
1947 }
1949 sub get_fai_state {
1950 my $result = "";
1951 my $tmp_classes = shift || return $result;
1953 foreach my $type(keys %{$tmp_classes}) {
1954 if(defined($tmp_classes->{$type}[0])) {
1955 $result = $tmp_classes->{$type}[0];
1957 # State is equal for all types in class
1958 last;
1959 }
1960 }
1962 return $result;
1963 }
1965 sub resolve_fai_classes {
1966 my ($fai_base, $ldap_handle, $session_id) = @_;
1967 if (not defined $session_id) { $session_id = 0; }
1968 my $result;
1969 my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
1970 my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
1971 my $fai_classes;
1973 daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base",7);
1974 my $mesg= $ldap_handle->search(
1975 base => $fai_base,
1976 scope => 'sub',
1977 attrs => ['cn','objectClass','FAIstate'],
1978 filter => $fai_filter,
1979 );
1980 daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries",7);
1982 if($mesg->{'resultCode'} == 0 &&
1983 $mesg->count != 0) {
1984 foreach my $entry (@{$mesg->{entries}}) {
1985 if($entry->exists('cn')) {
1986 my $tmp_dn= $entry->dn();
1988 # Skip classname and ou dn parts for class
1989 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?),$fai_base$/;
1991 # Skip classes without releases
1992 if((!defined($tmp_release)) || length($tmp_release)==0) {
1993 next;
1994 }
1996 my $tmp_cn= $entry->get_value('cn');
1997 my $tmp_state= $entry->get_value('FAIstate');
1999 my $tmp_type;
2000 # Get FAI type
2001 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
2002 if(grep $_ eq $oclass, @possible_fai_classes) {
2003 $tmp_type= $oclass;
2004 last;
2005 }
2006 }
2008 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2009 # A Subrelease
2010 my @sub_releases = split(/,/, $tmp_release);
2012 # Walk through subreleases and build hash tree
2013 my $hash;
2014 while(my $tmp_sub_release = pop @sub_releases) {
2015 $hash .= "\{'$tmp_sub_release'\}->";
2016 }
2017 eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
2018 } else {
2019 # A branch, no subrelease
2020 push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
2021 }
2022 } elsif (!$entry->exists('cn')) {
2023 my $tmp_dn= $entry->dn();
2024 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?),$fai_base$/;
2026 # Skip classes without releases
2027 if((!defined($tmp_release)) || length($tmp_release)==0) {
2028 next;
2029 }
2031 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2032 # A Subrelease
2033 my @sub_releases= split(/,/, $tmp_release);
2035 # Walk through subreleases and build hash tree
2036 my $hash;
2037 while(my $tmp_sub_release = pop @sub_releases) {
2038 $hash .= "\{'$tmp_sub_release'\}->";
2039 }
2040 # Remove the last two characters
2041 chop($hash);
2042 chop($hash);
2044 eval('$fai_classes->'.$hash.'= {}');
2045 } else {
2046 # A branch, no subrelease
2047 if(!exists($fai_classes->{$tmp_release})) {
2048 $fai_classes->{$tmp_release} = {};
2049 }
2050 }
2051 }
2052 }
2054 # The hash is complete, now we can honor the copy-on-write based missing entries
2055 foreach my $release (keys %$fai_classes) {
2056 $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
2057 }
2058 }
2059 return $result;
2060 }
2062 sub apply_fai_inheritance {
2063 my $fai_classes = shift || return {};
2064 my $tmp_classes;
2066 # Get the classes from the branch
2067 foreach my $class (keys %{$fai_classes}) {
2068 # Skip subreleases
2069 if($class =~ /^ou=.*$/) {
2070 next;
2071 } else {
2072 $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
2073 }
2074 }
2076 # Apply to each subrelease
2077 foreach my $subrelease (keys %{$fai_classes}) {
2078 if($subrelease =~ /ou=/) {
2079 foreach my $tmp_class (keys %{$tmp_classes}) {
2080 if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
2081 $fai_classes->{$subrelease}->{$tmp_class} =
2082 deep_copy($tmp_classes->{$tmp_class});
2083 } else {
2084 foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
2085 if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
2086 $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
2087 deep_copy($tmp_classes->{$tmp_class}->{$type});
2088 }
2089 }
2090 }
2091 }
2092 }
2093 }
2095 # Find subreleases in deeper levels
2096 foreach my $subrelease (keys %{$fai_classes}) {
2097 if($subrelease =~ /ou=/) {
2098 foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
2099 if($subsubrelease =~ /ou=/) {
2100 apply_fai_inheritance($fai_classes->{$subrelease});
2101 }
2102 }
2103 }
2104 }
2106 return $fai_classes;
2107 }
2109 sub get_fai_release_entries {
2110 my $tmp_classes = shift || return;
2111 my $parent = shift || "";
2112 my @result = shift || ();
2114 foreach my $entry (keys %{$tmp_classes}) {
2115 if(defined($entry)) {
2116 if($entry =~ /^ou=.*$/) {
2117 my $release_name = $entry;
2118 $release_name =~ s/ou=//g;
2119 if(length($parent)>0) {
2120 $release_name = $parent."/".$release_name;
2121 }
2122 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2123 foreach my $bufentry(@bufentries) {
2124 push @result, $bufentry;
2125 }
2126 } else {
2127 my @types = get_fai_types($tmp_classes->{$entry});
2128 foreach my $type (@types) {
2129 push @result,
2130 {
2131 'class' => $entry,
2132 'type' => $type->{'type'},
2133 'release' => $parent,
2134 'state' => $type->{'state'},
2135 };
2136 }
2137 }
2138 }
2139 }
2141 return @result;
2142 }
2144 sub deep_copy {
2145 my $this = shift;
2146 if (not ref $this) {
2147 $this;
2148 } elsif (ref $this eq "ARRAY") {
2149 [map deep_copy($_), @$this];
2150 } elsif (ref $this eq "HASH") {
2151 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2152 } else { die "what type is $_?" }
2153 }
2156 sub session_run_result {
2157 my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];
2158 $kernel->sig(CHLD => "child_reap");
2159 }
2161 sub session_run_debug {
2162 my $result = $_[ARG0];
2163 print STDERR "$result\n";
2164 }
2166 sub session_run_done {
2167 my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2168 delete $heap->{task}->{$task_id};
2169 }
2172 sub create_sources_list {
2173 my $session_id = shift;
2174 my $ldap_handle = &main::get_ldap_handle;
2175 my $result="/tmp/gosa_si_tmp_sources_list";
2177 # Remove old file
2178 if(stat($result)) {
2179 unlink($result);
2180 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7);
2181 }
2183 my $fh;
2184 open($fh, ">$result");
2185 if (not defined $fh) {
2186 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7);
2187 return undef;
2188 }
2189 if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2190 my $mesg=$ldap_handle->search(
2191 base => $main::ldap_server_dn,
2192 scope => 'base',
2193 attrs => 'FAIrepository',
2194 filter => 'objectClass=FAIrepositoryServer'
2195 );
2196 if($mesg->count) {
2197 foreach my $entry(@{$mesg->{'entries'}}) {
2198 foreach my $value(@{$entry->get_value('FAIrepository', asref => 1)}) {
2199 my ($server, $tag, $release, $sections)= split /\|/, $value;
2200 my $line = "deb $server $release";
2201 $sections =~ s/,/ /g;
2202 $line.= " $sections";
2203 print $fh $line."\n";
2204 }
2205 }
2206 }
2207 } else {
2208 if (defined $main::ldap_server_dn){
2209 &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1);
2210 } else {
2211 &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2212 }
2213 }
2214 close($fh);
2216 return $result;
2217 }
2220 sub run_create_packages_list_db {
2221 my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2222 my $session_id = $session->ID;
2224 my $task = POE::Wheel::Run->new(
2225 Priority => +20,
2226 Program => sub {&create_packages_list_db(undef, undef, $session_id)},
2227 StdoutEvent => "session_run_result",
2228 StderrEvent => "session_run_debug",
2229 CloseEvent => "session_run_done",
2230 );
2231 $heap->{task}->{ $task->ID } = $task;
2232 }
2235 sub create_packages_list_db {
2236 my ($ldap_handle, $sources_file, $session_id) = @_;
2238 # it should not be possible to trigger a recreation of packages_list_db
2239 # while packages_list_db is under construction, so set flag packages_list_under_construction
2240 # which is tested befor recreation can be started
2241 if (-r $packages_list_under_construction) {
2242 daemon_log("$session_id WARNING: packages_list_db is right now under construction, please wait until this process is finished", 3);
2243 return;
2244 } else {
2245 daemon_log("$session_id INFO: create_packages_list_db: start", 5);
2246 # set packages_list_under_construction to true
2247 system("touch $packages_list_under_construction");
2248 @packages_list_statements=();
2249 }
2251 if (not defined $session_id) { $session_id = 0; }
2252 if (not defined $ldap_handle) {
2253 $ldap_handle= &get_ldap_handle();
2255 if (not defined $ldap_handle) {
2256 daemon_log("$session_id ERROR: no ldap_handle available to create_packages_list_db", 1);
2257 unlink($packages_list_under_construction);
2258 return;
2259 }
2260 }
2261 if (not defined $sources_file) {
2262 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5);
2263 $sources_file = &create_sources_list($session_id);
2264 }
2266 if (not defined $sources_file) {
2267 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1);
2268 unlink($packages_list_under_construction);
2269 return;
2270 }
2272 my $line;
2274 open(CONFIG, "<$sources_file") or do {
2275 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2276 unlink($packages_list_under_construction);
2277 return;
2278 };
2280 # Read lines
2281 while ($line = <CONFIG>){
2282 # Unify
2283 chop($line);
2284 $line =~ s/^\s+//;
2285 $line =~ s/^\s+/ /;
2287 # Strip comments
2288 $line =~ s/#.*$//g;
2290 # Skip empty lines
2291 if ($line =~ /^\s*$/){
2292 next;
2293 }
2295 # Interpret deb line
2296 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2297 my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2298 my $section;
2299 foreach $section (split(' ', $sections)){
2300 &parse_package_info( $baseurl, $dist, $section, $session_id );
2301 }
2302 }
2303 }
2305 close (CONFIG);
2307 find(\&cleanup_and_extract, keys( %repo_dirs ));
2308 &main::strip_packages_list_statements();
2309 unshift @packages_list_statements, "VACUUM";
2310 $packages_list_db->exec_statementlist(\@packages_list_statements);
2311 unlink($packages_list_under_construction);
2312 daemon_log("$session_id INFO: create_packages_list_db: finished", 5);
2313 return;
2314 }
2316 # This function should do some intensive task to minimize the db-traffic
2317 sub strip_packages_list_statements {
2318 my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2319 my @new_statement_list=();
2320 my $hash;
2321 my $insert_hash;
2322 my $update_hash;
2323 my $delete_hash;
2324 my $local_timestamp=get_time();
2326 foreach my $existing_entry (@existing_entries) {
2327 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
2328 }
2330 foreach my $statement (@packages_list_statements) {
2331 if($statement =~ /^INSERT/i) {
2332 # Assign the values from the insert statement
2333 my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~
2334 /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
2335 if(exists($hash->{$distribution}->{$package}->{$version})) {
2336 # If section or description has changed, update the DB
2337 if(
2338 (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or
2339 (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
2340 ) {
2341 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
2342 }
2343 } else {
2344 # Insert a non-existing entry to db
2345 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2346 }
2347 } elsif ($statement =~ /^UPDATE/i) {
2348 my ($template,$package,$version) = ($1,$2,$3) if $statement =~
2349 /^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;
2350 foreach my $distribution (keys %{$hash}) {
2351 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
2352 # update the insertion hash to execute only one query per package (insert instead insert+update)
2353 @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
2354 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
2355 if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
2356 my $section;
2357 my $description;
2358 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
2359 length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
2360 $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
2361 }
2362 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2363 $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
2364 }
2365 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2366 }
2367 }
2368 }
2369 }
2370 }
2372 # TODO: Check for orphaned entries
2374 # unroll the insert_hash
2375 foreach my $distribution (keys %{$insert_hash}) {
2376 foreach my $package (keys %{$insert_hash->{$distribution}}) {
2377 foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
2378 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
2379 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
2380 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
2381 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
2382 ."'$local_timestamp')";
2383 }
2384 }
2385 }
2387 # unroll the update hash
2388 foreach my $distribution (keys %{$update_hash}) {
2389 foreach my $package (keys %{$update_hash->{$distribution}}) {
2390 foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
2391 my $set = "";
2392 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
2393 $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
2394 }
2395 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2396 $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
2397 }
2398 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
2399 $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
2400 }
2401 if(defined($set) and length($set) > 0) {
2402 $set .= "timestamp = '$local_timestamp'";
2403 } else {
2404 next;
2405 }
2406 push @new_statement_list,
2407 "UPDATE $main::packages_list_tn SET $set WHERE"
2408 ." distribution = '$distribution'"
2409 ." AND package = '$package'"
2410 ." AND version = '$version'";
2411 }
2412 }
2413 }
2415 @packages_list_statements = @new_statement_list;
2416 }
2419 sub parse_package_info {
2420 my ($baseurl, $dist, $section, $session_id)= @_;
2421 my ($package);
2422 if (not defined $session_id) { $session_id = 0; }
2423 my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
2424 $repo_dirs{ "${repo_path}/pool" } = 1;
2426 foreach $package ("Packages.gz"){
2427 daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
2428 get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
2429 parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
2430 }
2432 }
2435 sub get_package {
2436 my ($url, $dest, $session_id)= @_;
2437 if (not defined $session_id) { $session_id = 0; }
2439 my $tpath = dirname($dest);
2440 -d "$tpath" || mkpath "$tpath";
2442 # This is ugly, but I've no time to take a look at "how it works in perl"
2443 if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
2444 system("gunzip -cd '$dest' > '$dest.in'");
2445 daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 5);
2446 unlink($dest);
2447 daemon_log("$session_id DEBUG: delete file '$dest'", 5);
2448 } else {
2449 daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' failed!", 1);
2450 }
2451 return 0;
2452 }
2455 sub parse_package {
2456 my ($path, $dist, $srv_path, $session_id)= @_;
2457 if (not defined $session_id) { $session_id = 0;}
2458 my ($package, $version, $section, $description);
2459 my $PACKAGES;
2460 my $timestamp = &get_time();
2462 if(not stat("$path.in")) {
2463 daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
2464 return;
2465 }
2467 open($PACKAGES, "<$path.in");
2468 if(not defined($PACKAGES)) {
2469 daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1);
2470 return;
2471 }
2473 # Read lines
2474 while (<$PACKAGES>){
2475 my $line = $_;
2476 # Unify
2477 chop($line);
2479 # Use empty lines as a trigger
2480 if ($line =~ /^\s*$/){
2481 my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
2482 push(@packages_list_statements, $sql);
2483 $package = "none";
2484 $version = "none";
2485 $section = "none";
2486 $description = "none";
2487 next;
2488 }
2490 # Trigger for package name
2491 if ($line =~ /^Package:\s/){
2492 ($package)= ($line =~ /^Package: (.*)$/);
2493 next;
2494 }
2496 # Trigger for version
2497 if ($line =~ /^Version:\s/){
2498 ($version)= ($line =~ /^Version: (.*)$/);
2499 next;
2500 }
2502 # Trigger for description
2503 if ($line =~ /^Description:\s/){
2504 ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
2505 next;
2506 }
2508 # Trigger for section
2509 if ($line =~ /^Section:\s/){
2510 ($section)= ($line =~ /^Section: (.*)$/);
2511 next;
2512 }
2514 # Trigger for filename
2515 if ($line =~ /^Filename:\s/){
2516 my ($filename) = ($line =~ /^Filename: (.*)$/);
2517 store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
2518 next;
2519 }
2520 }
2522 close( $PACKAGES );
2523 unlink( "$path.in" );
2524 &main::daemon_log("$session_id DEBUG: unlink '$path.in'", 1);
2525 }
2528 sub store_fileinfo {
2529 my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
2531 my %fileinfo = (
2532 'package' => $package,
2533 'dist' => $dist,
2534 'version' => $vers,
2535 );
2537 $repo_files{ "${srvdir}/$file" } = \%fileinfo;
2538 }
2541 sub cleanup_and_extract {
2542 my $fileinfo = $repo_files{ $File::Find::name };
2544 if( defined $fileinfo ) {
2546 my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
2547 my $sql;
2548 my $package = $fileinfo->{ 'package' };
2549 my $newver = $fileinfo->{ 'version' };
2551 mkpath($dir);
2552 system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
2554 if( -f "$dir/DEBIAN/templates" ) {
2556 daemon_log("DEBUG: Found debconf templates in '$package' - $newver", 5);
2558 my $tmpl= "";
2559 {
2560 local $/=undef;
2561 open FILE, "$dir/DEBIAN/templates";
2562 $tmpl = &encode_base64(<FILE>);
2563 close FILE;
2564 }
2565 rmtree("$dir/DEBIAN/templates");
2567 $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
2568 push @packages_list_statements, $sql;
2569 }
2570 }
2572 return;
2573 }
2576 #==== MAIN = main ==============================================================
2577 # parse commandline options
2578 Getopt::Long::Configure( "bundling" );
2579 GetOptions("h|help" => \&usage,
2580 "c|config=s" => \$cfg_file,
2581 "f|foreground" => \$foreground,
2582 "v|verbose+" => \$verbose,
2583 "no-bus+" => \$no_bus,
2584 "no-arp+" => \$no_arp,
2585 );
2587 # read and set config parameters
2588 &check_cmdline_param ;
2589 &read_configfile;
2590 &check_pid;
2592 $SIG{CHLD} = 'IGNORE';
2594 # forward error messages to logfile
2595 if( ! $foreground ) {
2596 open( STDIN, '+>/dev/null' );
2597 open( STDOUT, '+>&STDIN' );
2598 open( STDERR, '+>&STDIN' );
2599 }
2601 # Just fork, if we are not in foreground mode
2602 if( ! $foreground ) {
2603 chdir '/' or die "Can't chdir to /: $!";
2604 $pid = fork;
2605 setsid or die "Can't start a new session: $!";
2606 umask 0;
2607 } else {
2608 $pid = $$;
2609 }
2611 # Do something useful - put our PID into the pid_file
2612 if( 0 != $pid ) {
2613 open( LOCK_FILE, ">$pid_file" );
2614 print LOCK_FILE "$pid\n";
2615 close( LOCK_FILE );
2616 if( !$foreground ) {
2617 exit( 0 )
2618 };
2619 }
2621 # parse head url and revision from svn
2622 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
2623 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
2624 $server_headURL = defined $1 ? $1 : 'unknown' ;
2625 $server_revision = defined $2 ? $2 : 'unknown' ;
2626 if ($server_headURL =~ /\/tag\// ||
2627 $server_headURL =~ /\/branches\// ) {
2628 $server_status = "stable";
2629 } else {
2630 $server_status = "developmental" ;
2631 }
2634 daemon_log(" ", 1);
2635 daemon_log("$0 started!", 1);
2636 daemon_log("status: $server_status", 1);
2637 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1);
2639 if ($no_bus > 0) {
2640 $bus_activ = "false"
2641 }
2643 # connect to incoming_db
2644 unlink($incoming_file_name);
2645 $incoming_db = GOSA::DBsqlite->new($incoming_file_name);
2646 $incoming_db->create_table($incoming_tn, \@incoming_col_names);
2648 # connect to gosa-si job queue
2649 $job_db = GOSA::DBsqlite->new($job_queue_file_name);
2650 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
2652 # connect to known_clients_db
2653 $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
2654 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
2656 # connect to known_server_db
2657 $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
2658 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
2660 # connect to login_usr_db
2661 $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
2662 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
2664 # connect to fai_server_db and fai_release_db
2665 unlink($fai_server_file_name);
2666 $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
2667 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
2669 unlink($fai_release_file_name);
2670 $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
2671 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
2673 # connect to packages_list_db
2674 #unlink($packages_list_file_name);
2675 unlink($packages_list_under_construction);
2676 $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
2677 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
2679 # connect to messaging_db
2680 $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
2681 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
2684 # create xml object used for en/decrypting
2685 $xml = new XML::Simple();
2687 # create socket for incoming xml messages
2689 POE::Component::Server::TCP->new(
2690 Port => $server_port,
2691 ClientInput => sub {
2692 my ($kernel, $input) = @_[KERNEL, ARG0];
2693 push(@tasks, $input);
2694 push(@msgs_to_decrypt, $input);
2695 $kernel->yield("next_task");
2696 $kernel->yield("msg_to_decrypt");
2697 },
2698 InlineStates => {
2699 next_task => \&next_task,
2700 msg_to_decrypt => \&msg_to_decrypt,
2701 task_result => \&handle_task_result,
2702 task_done => \&handle_task_done,
2703 task_debug => \&handle_task_debug,
2704 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!" },
2705 }
2706 );
2708 daemon_log("start socket for incoming xml messages at port '$server_port' ", 1);
2710 # create session for repeatedly checking the job queue for jobs
2711 POE::Session->create(
2712 inline_states => {
2713 _start => \&_start,
2714 sig_handler => \&sig_handler,
2715 watch_for_new_messages => \&watch_for_new_messages,
2716 watch_for_delivery_messages => \&watch_for_delivery_messages,
2717 watch_for_done_messages => \&watch_for_done_messages,
2718 watch_for_new_jobs => \&watch_for_new_jobs,
2719 watch_for_done_jobs => \&watch_for_done_jobs,
2720 create_packages_list_db => \&run_create_packages_list_db,
2721 create_fai_server_db => \&run_create_fai_server_db,
2722 create_fai_release_db => \&run_create_fai_release_db,
2723 session_run_result => \&session_run_result,
2724 session_run_debug => \&session_run_debug,
2725 session_run_done => \&session_run_done,
2726 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!" },
2727 }
2728 );
2731 # import all modules
2732 &import_modules;
2734 # check wether all modules are gosa-si valid passwd check
2736 POE::Kernel->run();
2737 exit;