6aecf17d86042ab44be3449865a3fa83e2574595
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 use strict;
25 use warnings;
26 use Getopt::Long;
27 use Config::IniFiles;
28 use POSIX;
30 use Fcntl;
31 use IO::Socket::INET;
32 use IO::Handle;
33 use IO::Select;
34 use Symbol qw(qualify_to_ref);
35 use Crypt::Rijndael;
36 use MIME::Base64;
37 use Digest::MD5 qw(md5 md5_hex md5_base64);
38 use XML::Simple;
39 use Data::Dumper;
40 use Sys::Syslog qw( :DEFAULT setlogsock);
41 use Cwd;
42 use File::Spec;
43 use File::Basename;
44 use File::Find;
45 use File::Copy;
46 use File::Path;
47 use GOSA::DBsqlite;
48 use GOSA::GosaSupportDaemon;
49 use POE qw(Component::Server::TCP Wheel::Run Filter::Reference);
50 use Net::LDAP;
51 use Net::LDAP::Util qw(:escape);
53 my $modules_path = "/usr/lib/gosa-si/modules";
54 use lib "/usr/lib/gosa-si/modules";
55 my $server_version = '$HeadURL$:$Rev$';
56 my $server_headURL;
57 my $server_revision;
58 my $server_status;
61 # TODO es gibt eine globale funktion get_ldap_handle
62 # - ist in einer session dieses ldap handle schon vorhanden, wird es zurückgegeben
63 # - ist es nicht vorhanden, wird es erzeugt, im heap für spätere ldap anfragen gespeichert und zurückgegeben
64 # - sessions die kein ldap handle brauchen, sollen auch keins haben
65 # - wird eine session geschlossen, muss das ldap verbindung vorher beendet werden
66 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 $ClientPackages_key, $ServerPackages_key,
86 $arp_activ, $gosa_unit_tag,
87 $GosaPackages_key, $gosa_ip, $gosa_port, $gosa_timeout,
88 );
90 # additional variable which should be globaly accessable
91 our $server_address;
92 our $server_mac_address;
93 our $bus_address;
94 our $gosa_address;
95 our $no_bus;
96 our $no_arp;
97 our $verbose;
98 our $forground;
99 our $cfg_file;
100 #our ($ldap_handle, $ldap_uri, $ldap_base, $ldap_admin_dn, $ldap_admin_password, $ldap_server_dn);
101 our ($ldap_uri, $ldap_base, $ldap_admin_dn, $ldap_admin_password, $ldap_server_dn);
104 # specifies the verbosity of the daemon_log
105 $verbose = 0 ;
107 # if foreground is not null, script will be not forked to background
108 $foreground = 0 ;
110 # specifies the timeout seconds while checking the online status of a registrating client
111 $ping_timeout = 5;
113 $no_bus = 0;
114 $bus_activ = "true";
115 $no_arp = 0;
116 my $packages_list_under_construction = "/tmp/packages_list_creation_in_progress";
117 my @packages_list_statements;
118 my $watch_for_new_jobs_in_progress = 0;
120 our $prg= basename($0);
122 # holds all gosa jobs
123 our $job_db;
124 our $job_queue_tn = 'jobs';
125 my $job_queue_file_name;
126 my @job_queue_col_names = ("id INTEGER PRIMARY KEY",
127 "timestamp DEFAULT 'none'",
128 "status DEFAULT 'none'",
129 "result DEFAULT 'none'",
130 "progress DEFAULT 'none'",
131 "headertag DEFAULT 'none'",
132 "targettag DEFAULT 'none'",
133 "xmlmessage DEFAULT 'none'",
134 "macaddress DEFAULT 'none'",
135 "plainname DEFAULT 'none'",
136 );
138 # holds all other gosa-sd as well as the gosa-sd-bus
139 our $known_server_db;
140 our $known_server_tn = "known_server";
141 my $known_server_file_name;
142 my @known_server_col_names = ("hostname", "status", "hostkey", "timestamp");
144 # holds all registrated clients
145 our $known_clients_db;
146 our $known_clients_tn = "known_clients";
147 my $known_clients_file_name;
148 my @known_clients_col_names = ("hostname", "status", "hostkey", "timestamp", "macaddress", "events");
150 # holds all logged in user at each client
151 our $login_users_db;
152 our $login_users_tn = "login_users";
153 my $login_users_file_name;
154 my @login_users_col_names = ("client", "user", "timestamp");
156 # holds all fai server, the debian release and tag
157 our $fai_server_db;
158 our $fai_server_tn = "fai_server";
159 my $fai_server_file_name;
160 our @fai_server_col_names = ("timestamp", "server", "release", "sections", "tag");
162 our $fai_release_db;
163 our $fai_release_tn = "fai_release";
164 my $fai_release_file_name;
165 our @fai_release_col_names = ("timestamp", "release", "class", "type", "state");
167 # holds all packages available from different repositories
168 our $packages_list_db;
169 our $packages_list_tn = "packages_list";
170 my $packages_list_file_name;
171 our @packages_list_col_names = ("distribution", "package", "version", "section", "description", "template", "timestamp");
172 my $outdir = "/tmp/packages_list_db";
173 my $arch = "i386";
175 # holds all messages which should be delivered to a user
176 our $messaging_db;
177 our $messaging_tn = "messaging";
178 our @messaging_col_names = ("id INTEGER", "subject", "message_from", "message_to",
179 "flag", "direction", "delivery_time", "message", "timestamp" );
180 my $messaging_file_name;
182 # path to directory to store client install log files
183 our $client_fai_log_dir = "/var/log/fai";
185 # queue which stores taskes until one of the $max_children children are ready to process the task
186 my @tasks = qw();
187 my $max_children = 2;
190 %cfg_defaults = (
191 "general" => {
192 "log-file" => [\$log_file, "/var/run/".$prg.".log"],
193 "pid-file" => [\$pid_file, "/var/run/".$prg.".pid"],
194 },
195 "bus" => {
196 "activ" => [\$bus_activ, "true"],
197 },
198 "server" => {
199 "port" => [\$server_port, "20081"],
200 "known-clients" => [\$known_clients_file_name, '/var/lib/gosa-si/clients.db' ],
201 "known-servers" => [\$known_server_file_name, '/var/lib/gosa-si/servers.db'],
202 "login-users" => [\$login_users_file_name, '/var/lib/gosa-si/users.db'],
203 "fai-server" => [\$fai_server_file_name, '/var/lib/gosa-si/fai_server.db'],
204 "fai-release" => [\$fai_release_file_name, '/var/lib/gosa-si/fai_release.db'],
205 "packages-list" => [\$packages_list_file_name, '/var/lib/gosa-si/packages.db'],
206 "messaging" => [\$messaging_file_name, '/var/lib/gosa-si/messaging.db'],
207 "source-list" => [\$sources_list, '/etc/apt/sources.list'],
208 "repo-path" => [\$repo_path, '/srv/www/repository'],
209 "ldap-uri" => [\$ldap_uri, ""],
210 "ldap-base" => [\$ldap_base, ""],
211 "ldap-admin-dn" => [\$ldap_admin_dn, ""],
212 "ldap-admin-password" => [\$ldap_admin_password, ""],
213 "gosa-unit-tag" => [\$gosa_unit_tag, ""],
214 "max-clients" => [\$max_clients, 10],
215 },
216 "GOsaPackages" => {
217 "ip" => [\$gosa_ip, "0.0.0.0"],
218 "port" => [\$gosa_port, "20082"],
219 "job-queue" => [\$job_queue_file_name, '/var/lib/gosa-si/jobs.db'],
220 "job-queue-loop-delay" => [\$job_queue_loop_delay, 3],
221 "messaging-db-loop-delay" => [\$messaging_db_loop_delay, 3],
222 "key" => [\$GosaPackages_key, "none"],
223 },
224 "SIPackages" => {
225 "key" => [\$SIPackages_key, "none"],
226 },
227 "ClientPackages" => {
228 "key" => [\$ClientPackages_key, "none"],
229 },
230 "ServerPackages" => {
231 "key" => [\$ServerPackages_key, "none"],
232 },
233 );
236 #=== FUNCTION ================================================================
237 # NAME: usage
238 # PARAMETERS: nothing
239 # RETURNS: nothing
240 # DESCRIPTION: print out usage text to STDERR
241 #===============================================================================
242 sub usage {
243 print STDERR << "EOF" ;
244 usage: $prg [-hvf] [-c config]
246 -h : this (help) message
247 -c <file> : config file
248 -f : foreground, process will not be forked to background
249 -v : be verbose (multiple to increase verbosity)
250 -no-bus : starts $prg without connection to bus
251 -no-arp : starts $prg without connection to arp module
253 EOF
254 print "\n" ;
255 }
258 #=== FUNCTION ================================================================
259 # NAME: read_configfile
260 # PARAMETERS: cfg_file - string -
261 # RETURNS: nothing
262 # DESCRIPTION: read cfg_file and set variables
263 #===============================================================================
264 sub read_configfile {
265 my $cfg;
266 if( defined( $cfg_file) && ( (-s $cfg_file) > 0 )) {
267 if( -r $cfg_file ) {
268 $cfg = Config::IniFiles->new( -file => $cfg_file );
269 } else {
270 print STDERR "Couldn't read config file!\n";
271 }
272 } else {
273 $cfg = Config::IniFiles->new() ;
274 }
275 foreach my $section (keys %cfg_defaults) {
276 foreach my $param (keys %{$cfg_defaults{ $section }}) {
277 my $pinfo = $cfg_defaults{ $section }{ $param };
278 ${@$pinfo[ 0 ]} = $cfg->val( $section, $param, @$pinfo[ 1 ] );
279 }
280 }
281 }
284 #=== FUNCTION ================================================================
285 # NAME: logging
286 # PARAMETERS: level - string - default 'info'
287 # msg - string -
288 # facility - string - default 'LOG_DAEMON'
289 # RETURNS: nothing
290 # DESCRIPTION: function for logging
291 #===============================================================================
292 sub daemon_log {
293 # log into log_file
294 my( $msg, $level ) = @_;
295 if(not defined $msg) { return }
296 if(not defined $level) { $level = 1 }
297 if(defined $log_file){
298 open(LOG_HANDLE, ">>$log_file");
299 chmod 0600, $log_file;
300 if(not defined open( LOG_HANDLE, ">>$log_file" )) {
301 print STDERR "cannot open $log_file: $!";
302 return }
303 chomp($msg);
304 $msg =~s/\n//g; # no newlines are allowed in log messages, this is important for later log parsing
305 if($level <= $verbose){
306 my ($seconds, $minutes, $hours, $monthday, $month,
307 $year, $weekday, $yearday, $sommertime) = localtime(time);
308 $hours = $hours < 10 ? $hours = "0".$hours : $hours;
309 $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
310 $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
311 my @monthnames = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
312 $month = $monthnames[$month];
313 $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
314 $year+=1900;
315 my $name = $prg;
317 my $log_msg = "$month $monthday $hours:$minutes:$seconds $name $msg\n";
318 print LOG_HANDLE $log_msg;
319 if( $foreground ) {
320 print STDERR $log_msg;
321 }
322 }
323 close( LOG_HANDLE );
324 }
325 }
328 #=== FUNCTION ================================================================
329 # NAME: check_cmdline_param
330 # PARAMETERS: nothing
331 # RETURNS: nothing
332 # DESCRIPTION: validates commandline parameter
333 #===============================================================================
334 sub check_cmdline_param () {
335 my $err_config;
336 my $err_counter = 0;
337 if(not defined($cfg_file)) {
338 $cfg_file = "/etc/gosa-si/server.conf";
339 if(! -r $cfg_file) {
340 $err_config = "please specify a config file";
341 $err_counter += 1;
342 }
343 }
344 if( $err_counter > 0 ) {
345 &usage( "", 1 );
346 if( defined( $err_config)) { print STDERR "$err_config\n"}
347 print STDERR "\n";
348 exit( -1 );
349 }
350 }
353 #=== FUNCTION ================================================================
354 # NAME: check_pid
355 # PARAMETERS: nothing
356 # RETURNS: nothing
357 # DESCRIPTION: handels pid processing
358 #===============================================================================
359 sub check_pid {
360 $pid = -1;
361 # Check, if we are already running
362 if( open(LOCK_FILE, "<$pid_file") ) {
363 $pid = <LOCK_FILE>;
364 if( defined $pid ) {
365 chomp( $pid );
366 if( -f "/proc/$pid/stat" ) {
367 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
368 if( $stat ) {
369 daemon_log("ERROR: Already running",1);
370 close( LOCK_FILE );
371 exit -1;
372 }
373 }
374 }
375 close( LOCK_FILE );
376 unlink( $pid_file );
377 }
379 # create a syslog msg if it is not to possible to open PID file
380 if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
381 my($msg) = "Couldn't obtain lockfile '$pid_file' ";
382 if (open(LOCK_FILE, '<', $pid_file)
383 && ($pid = <LOCK_FILE>))
384 {
385 chomp($pid);
386 $msg .= "(PID $pid)\n";
387 } else {
388 $msg .= "(unable to read PID)\n";
389 }
390 if( ! ($foreground) ) {
391 openlog( $0, "cons,pid", "daemon" );
392 syslog( "warning", $msg );
393 closelog();
394 }
395 else {
396 print( STDERR " $msg " );
397 }
398 exit( -1 );
399 }
400 }
402 #=== FUNCTION ================================================================
403 # NAME: import_modules
404 # PARAMETERS: module_path - string - abs. path to the directory the modules
405 # are stored
406 # RETURNS: nothing
407 # DESCRIPTION: each file in module_path which ends with '.pm' and activation
408 # state is on is imported by "require 'file';"
409 #===============================================================================
410 sub import_modules {
411 daemon_log(" ", 1);
413 if (not -e $modules_path) {
414 daemon_log("ERROR: cannot find directory or directory is not readable: $modules_path", 1);
415 }
417 opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
418 while (defined (my $file = readdir (DIR))) {
419 if (not $file =~ /(\S*?).pm$/) {
420 next;
421 }
422 my $mod_name = $1;
424 if( $file =~ /ArpHandler.pm/ ) {
425 if( $no_arp > 0 ) {
426 next;
427 }
428 }
430 eval { require $file; };
431 if ($@) {
432 daemon_log("ERROR: gosa-si-server could not load module $file", 1);
433 daemon_log("$@", 5);
434 } else {
435 my $info = eval($mod_name.'::get_module_info()');
436 # Only load module if get_module_info() returns a non-null object
437 if( $info ) {
438 my ($input_address, $input_key, $input, $input_active, $input_type) = @{$info};
439 $known_modules->{$mod_name} = $info;
440 daemon_log("INFO: module $mod_name loaded", 5);
441 }
442 }
443 }
444 close (DIR);
445 }
448 #=== FUNCTION ================================================================
449 # NAME: sig_int_handler
450 # PARAMETERS: signal - string - signal arose from system
451 # RETURNS: noting
452 # DESCRIPTION: handels tasks to be done befor signal becomes active
453 #===============================================================================
454 sub sig_int_handler {
455 my ($signal) = @_;
457 # if (defined($ldap_handle)) {
458 # $ldap_handle->disconnect;
459 # }
460 # TODO alle verbliebenden ldap verbindungen aus allen heaps beenden
463 daemon_log("shutting down gosa-si-server", 1);
464 system("kill `ps -C gosa-si-server -o pid=`");
465 }
466 $SIG{INT} = \&sig_int_handler;
469 sub check_key_and_xml_validity {
470 my ($crypted_msg, $module_key, $session_id) = @_;
471 my $msg;
472 my $msg_hash;
473 my $error_string;
474 eval{
475 $msg = &decrypt_msg($crypted_msg, $module_key);
477 if ($msg =~ /<xml>/i){
478 $msg =~ s/\s+/ /g; # just for better daemon_log
479 daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 8);
480 $msg_hash = $xml->XMLin($msg, ForceArray=>1);
482 ##############
483 # check header
484 if( not exists $msg_hash->{'header'} ) { die "no header specified"; }
485 my $header_l = $msg_hash->{'header'};
486 if( 1 > @{$header_l} ) { die 'empty header tag'; }
487 if( 1 < @{$header_l} ) { die 'more than one header specified'; }
488 my $header = @{$header_l}[0];
489 if( 0 == length $header) { die 'empty string in header tag'; }
491 ##############
492 # check source
493 if( not exists $msg_hash->{'source'} ) { die "no source specified"; }
494 my $source_l = $msg_hash->{'source'};
495 if( 1 > @{$source_l} ) { die 'empty source tag'; }
496 if( 1 < @{$source_l} ) { die 'more than one source specified'; }
497 my $source = @{$source_l}[0];
498 if( 0 == length $source) { die 'source error'; }
500 ##############
501 # check target
502 if( not exists $msg_hash->{'target'} ) { die "no target specified"; }
503 my $target_l = $msg_hash->{'target'};
504 if( 1 > @{$target_l} ) { die 'empty target tag'; }
505 }
506 };
507 if($@) {
508 daemon_log("$session_id DEBUG: do not understand the message: $@", 7);
509 $msg = undef;
510 $msg_hash = undef;
511 }
513 return ($msg, $msg_hash);
514 }
517 sub check_outgoing_xml_validity {
518 my ($msg) = @_;
520 my $msg_hash;
521 eval{
522 $msg_hash = $xml->XMLin($msg, ForceArray=>1);
524 ##############
525 # check header
526 my $header_l = $msg_hash->{'header'};
527 if( 1 != @{$header_l} ) {
528 die 'no or more than one headers specified';
529 }
530 my $header = @{$header_l}[0];
531 if( 0 == length $header) {
532 die 'header has length 0';
533 }
535 ##############
536 # check source
537 my $source_l = $msg_hash->{'source'};
538 if( 1 != @{$source_l} ) {
539 die 'no or more than 1 sources specified';
540 }
541 my $source = @{$source_l}[0];
542 if( 0 == length $source) {
543 die 'source has length 0';
544 }
545 unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
546 $source =~ /^GOSA$/i ) {
547 die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
548 }
550 ##############
551 # check target
552 my $target_l = $msg_hash->{'target'};
553 if( 0 == @{$target_l} ) {
554 die "no targets specified";
555 }
556 foreach my $target (@$target_l) {
557 if( 0 == length $target) {
558 die "target has length 0";
559 }
560 unless( $target =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
561 $target =~ /^GOSA$/i ||
562 $target =~ /^\*$/ ||
563 $target =~ /KNOWN_SERVER/i ||
564 $target =~ /JOBDB/i ||
565 $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 ){
566 die "target '$target' is not a complete ip-address with port or a valid target name or a mac-address";
567 }
568 }
569 };
570 if($@) {
571 daemon_log("WARNING: outgoing msg is not gosa-si envelope conform", 5);
572 daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 8);
573 $msg_hash = undef;
574 }
576 return ($msg_hash);
577 }
580 sub input_from_known_server {
581 my ($input, $remote_ip, $session_id) = @_ ;
582 my ($msg, $msg_hash, $module);
584 my $sql_statement= "SELECT * FROM known_server";
585 my $query_res = $known_server_db->select_dbentry( $sql_statement );
587 while( my ($hit_num, $hit) = each %{ $query_res } ) {
588 my $host_name = $hit->{hostname};
589 if( not $host_name =~ "^$remote_ip") {
590 next;
591 }
592 my $host_key = $hit->{hostkey};
593 daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 7);
594 daemon_log("DEBUG: input_from_known_server: host_key: $host_key", 7);
596 # check if module can open msg envelope with module key
597 my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
598 if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
599 daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 7);
600 daemon_log("$@", 8);
601 next;
602 }
603 else {
604 $msg = $tmp_msg;
605 $msg_hash = $tmp_msg_hash;
606 $module = "SIPackages";
607 last;
608 }
609 }
611 if( (!$msg) || (!$msg_hash) || (!$module) ) {
612 daemon_log("$session_id DEBUG: Incoming message is not from a known server", 7);
613 }
615 return ($msg, $msg_hash, $module);
616 }
619 sub input_from_known_client {
620 my ($input, $remote_ip, $session_id) = @_ ;
621 my ($msg, $msg_hash, $module);
623 my $sql_statement= "SELECT * FROM known_clients";
624 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
625 while( my ($hit_num, $hit) = each %{ $query_res } ) {
626 my $host_name = $hit->{hostname};
627 if( not $host_name =~ /^$remote_ip:\d*$/) {
628 next;
629 }
630 my $host_key = $hit->{hostkey};
631 &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 7);
632 &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 7);
634 # check if module can open msg envelope with module key
635 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
637 if( (!$msg) || (!$msg_hash) ) {
638 &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 7);
639 &daemon_log("$@", 8);
640 next;
641 }
642 else {
643 $module = "SIPackages";
644 last;
645 }
646 }
648 if( (!$msg) || (!$msg_hash) || (!$module) ) {
649 &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 7);
650 }
652 return ($msg, $msg_hash, $module);
653 }
656 sub input_from_unknown_host {
657 no strict "refs";
658 my ($input, $session_id) = @_ ;
659 my ($msg, $msg_hash, $module);
660 my $error_string;
662 my %act_modules = %$known_modules;
664 while( my ($mod, $info) = each(%act_modules)) {
666 # check a key exists for this module
667 my $module_key = ${$mod."_key"};
668 if( not defined $module_key ) {
669 if( $mod eq 'ArpHandler' ) {
670 next;
671 }
672 daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
673 next;
674 }
675 daemon_log("$session_id DEBUG: $mod: $module_key", 7);
677 # check if module can open msg envelope with module key
678 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
679 if( (not defined $msg) || (not defined $msg_hash) ) {
680 next;
681 }
682 else {
683 $module = $mod;
684 last;
685 }
686 }
688 if( (!$msg) || (!$msg_hash) || (!$module)) {
689 daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 7);
690 }
692 return ($msg, $msg_hash, $module);
693 }
696 sub create_ciphering {
697 my ($passwd) = @_;
698 if((!defined($passwd)) || length($passwd)==0) {
699 $passwd = "";
700 }
701 $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
702 my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
703 my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
704 $my_cipher->set_iv($iv);
705 return $my_cipher;
706 }
709 sub encrypt_msg {
710 my ($msg, $key) = @_;
711 my $my_cipher = &create_ciphering($key);
712 my $len;
713 {
714 use bytes;
715 $len= 16-length($msg)%16;
716 }
717 $msg = "\0"x($len).$msg;
718 $msg = $my_cipher->encrypt($msg);
719 chomp($msg = &encode_base64($msg));
720 # there are no newlines allowed inside msg
721 $msg=~ s/\n//g;
722 return $msg;
723 }
726 sub decrypt_msg {
728 my ($msg, $key) = @_ ;
729 $msg = &decode_base64($msg);
730 my $my_cipher = &create_ciphering($key);
731 $msg = $my_cipher->decrypt($msg);
732 $msg =~ s/\0*//g;
733 return $msg;
734 }
737 sub get_encrypt_key {
738 my ($target) = @_ ;
739 my $encrypt_key;
740 my $error = 0;
742 # target can be in known_server
743 if( not defined $encrypt_key ) {
744 my $sql_statement= "SELECT * FROM known_server WHERE hostname='$target'";
745 my $query_res = $known_server_db->select_dbentry( $sql_statement );
746 while( my ($hit_num, $hit) = each %{ $query_res } ) {
747 my $host_name = $hit->{hostname};
748 if( $host_name ne $target ) {
749 next;
750 }
751 $encrypt_key = $hit->{hostkey};
752 last;
753 }
754 }
756 # target can be in known_client
757 if( not defined $encrypt_key ) {
758 my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$target'";
759 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
760 while( my ($hit_num, $hit) = each %{ $query_res } ) {
761 my $host_name = $hit->{hostname};
762 if( $host_name ne $target ) {
763 next;
764 }
765 $encrypt_key = $hit->{hostkey};
766 last;
767 }
768 }
770 return $encrypt_key;
771 }
774 #=== FUNCTION ================================================================
775 # NAME: open_socket
776 # PARAMETERS: PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
777 # [PeerPort] string necessary if port not appended by PeerAddr
778 # RETURNS: socket IO::Socket::INET
779 # DESCRIPTION: open a socket to PeerAddr
780 #===============================================================================
781 sub open_socket {
782 my ($PeerAddr, $PeerPort) = @_ ;
783 if(defined($PeerPort)){
784 $PeerAddr = $PeerAddr.":".$PeerPort;
785 }
786 my $socket;
787 $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
788 Porto => "tcp",
789 Type => SOCK_STREAM,
790 Timeout => 5,
791 );
792 if(not defined $socket) {
793 return;
794 }
795 # &daemon_log("DEBUG: open_socket: $PeerAddr", 7);
796 return $socket;
797 }
800 #=== FUNCTION ================================================================
801 # NAME: get_ip
802 # PARAMETERS: interface name (i.e. eth0)
803 # RETURNS: (ip address)
804 # DESCRIPTION: Uses ioctl to get ip address directly from system.
805 #===============================================================================
806 sub get_ip {
807 my $ifreq= shift;
808 my $result= "";
809 my $SIOCGIFADDR= 0x8915; # man 2 ioctl_list
810 my $proto= getprotobyname('ip');
812 socket SOCKET, PF_INET, SOCK_DGRAM, $proto
813 or die "socket: $!";
815 if(ioctl SOCKET, $SIOCGIFADDR, $ifreq) {
816 my ($if, $sin) = unpack 'a16 a16', $ifreq;
817 my ($port, $addr) = sockaddr_in $sin;
818 my $ip = inet_ntoa $addr;
820 if ($ip && length($ip) > 0) {
821 $result = $ip;
822 }
823 }
825 return $result;
826 }
829 sub get_local_ip_for_remote_ip {
830 my $remote_ip= shift;
831 my $result="0.0.0.0";
833 if($remote_ip =~ /^(\d\d?\d?\.){3}\d\d?\d?$/) {
834 if($remote_ip eq "127.0.0.1") {
835 $result = "127.0.0.1";
836 } else {
837 my $PROC_NET_ROUTE= ('/proc/net/route');
839 open(PROC_NET_ROUTE, "<$PROC_NET_ROUTE")
840 or die "Could not open $PROC_NET_ROUTE";
842 my @ifs = <PROC_NET_ROUTE>;
844 close(PROC_NET_ROUTE);
846 # Eat header line
847 shift @ifs;
848 chomp @ifs;
849 foreach my $line(@ifs) {
850 my ($Iface,$Destination,$Gateway,$Flags,$RefCnt,$Use,$Metric,$Mask,$MTU,$Window,$IRTT)=split(/\s/, $line);
851 my $destination;
852 my $mask;
853 my ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Destination);
854 $destination= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
855 ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Mask);
856 $mask= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
857 if(new NetAddr::IP($remote_ip)->within(new NetAddr::IP($destination, $mask))) {
858 # destination matches route, save mac and exit
859 $result= &get_ip($Iface);
860 last;
861 }
862 }
863 }
864 } else {
865 daemon_log("get_local_ip_for_remote_ip was called with a non-ip parameter: $remote_ip", 1);
866 }
867 return $result;
868 }
871 sub send_msg_to_target {
872 my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
873 my $error = 0;
874 my $header;
875 my $new_status;
876 my $act_status;
877 my ($sql_statement, $res);
879 if( $msg_header ) {
880 $header = "'$msg_header'-";
881 } else {
882 $header = "";
883 }
885 # Patch the source ip
886 if($msg =~ /<source>0\.0\.0\.0:\d*?<\/source>/) {
887 my $remote_ip = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
888 $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$remote_ip:$2<\/source>/s;
889 }
891 # encrypt xml msg
892 my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
894 # opensocket
895 my $socket = &open_socket($address);
896 if( !$socket ) {
897 daemon_log("$session_id ERROR: cannot send ".$header."msg to $address , host not reachable", 1);
898 $error++;
899 }
901 if( $error == 0 ) {
902 # send xml msg
903 print $socket $crypted_msg."\n";
905 daemon_log("$session_id INFO: send ".$header."msg to $address", 5);
906 #daemon_log("DEBUG: message:\n$msg", 9);
908 }
910 # close socket in any case
911 if( $socket ) {
912 close $socket;
913 }
915 if( $error > 0 ) { $new_status = "down"; }
916 else { $new_status = $msg_header; }
919 # known_clients
920 $sql_statement = "SELECT * FROM known_clients WHERE hostname='$address'";
921 $res = $known_clients_db->select_dbentry($sql_statement);
922 if( keys(%$res) > 0) {
923 $act_status = $res->{1}->{'status'};
924 if( $act_status eq "down" ) {
925 $sql_statement = "DELETE FROM known_clients WHERE hostname='$address'";
926 $res = $known_clients_db->del_dbentry($sql_statement);
927 daemon_log("$session_id WARNING: failed 2x to send msg to host '$address', delete host from known_clients", 3);
928 } else {
929 $sql_statement = "UPDATE known_clients SET status='$new_status' WHERE hostname='$address'";
930 $res = $known_clients_db->update_dbentry($sql_statement);
931 if($new_status eq "down"){
932 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
933 } else {
934 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
935 }
936 }
937 }
939 # known_server
940 $sql_statement = "SELECT * FROM known_server WHERE hostname='$address'";
941 $res = $known_server_db->select_dbentry($sql_statement);
942 if( keys(%$res) > 0 ) {
943 $act_status = $res->{1}->{'status'};
944 if( $act_status eq "down" ) {
945 $sql_statement = "DELETE FROM known_server WHERE hostname='$address'";
946 $res = $known_server_db->del_dbentry($sql_statement);
947 daemon_log("$session_id WARNING: failed 2x to a send msg to host '$address', delete host from known_server", 3);
948 }
949 else {
950 $sql_statement = "UPDATE known_server SET status='$new_status' WHERE hostname='$address'";
951 $res = $known_server_db->update_dbentry($sql_statement);
952 if($new_status eq "down"){
953 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
954 }
955 else {
956 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
957 }
958 }
959 }
960 return $error;
961 }
964 sub update_jobdb_status_for_send_msgs {
965 my ($answer, $error) = @_;
966 if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
967 my $jobdb_id = $1;
969 # sending msg faild
970 if( $error ) {
971 if (not $answer =~ /<header>trigger_action_reinstall<\/header>/) {
972 my $sql_statement = "UPDATE $job_queue_tn ".
973 "SET status='error', result='can not deliver msg, please consult log file' ".
974 "WHERE id=$jobdb_id";
975 my $res = $job_db->update_dbentry($sql_statement);
976 }
978 # sending msg was successful
979 } else {
980 my $sql_statement = "UPDATE $job_queue_tn ".
981 "SET status='done' ".
982 "WHERE id=$jobdb_id AND status='processed'";
983 my $res = $job_db->update_dbentry($sql_statement);
984 }
985 }
986 }
988 sub _start {
989 my ($kernel) = $_[KERNEL];
990 &trigger_db_loop($kernel);
991 $global_kernel = $kernel;
992 $kernel->yield('create_fai_server_db', $fai_server_tn );
993 $kernel->yield('create_fai_release_db', $fai_release_tn );
994 $kernel->sig(USR1 => "sig_handler");
995 $kernel->sig(USR2 => "create_packages_list_db");
996 }
998 sub sig_handler {
999 my ($kernel, $signal) = @_[KERNEL, ARG0] ;
1000 daemon_log("0 INFO got signal '$signal'", 1);
1001 $kernel->sig_handled();
1002 return;
1003 }
1005 sub next_task {
1006 my ($session, $heap) = @_[SESSION, HEAP];
1008 while ( keys( %{ $heap->{task} } ) < $max_children ) {
1009 my $next_task = shift @tasks;
1010 last unless defined $next_task;
1012 my $task = POE::Wheel::Run->new(
1013 Program => sub { process_task($session, $heap, $next_task) },
1014 StdioFilter => POE::Filter::Reference->new(),
1015 StdoutEvent => "task_result",
1016 StderrEvent => "task_debug",
1017 CloseEvent => "task_done",
1018 );
1020 $heap->{task}->{ $task->ID } = $task;
1021 }
1022 }
1024 sub handle_task_result {
1025 my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
1026 my $client_answer = $result->{'answer'};
1027 if( $client_answer =~ s/session_id=(\d+)$// ) {
1028 my $session_id = $1;
1029 if( defined $session_id ) {
1030 my $session_reference = $kernel->ID_id_to_session($session_id);
1031 if( defined $session_reference ) {
1032 $heap = $session_reference->get_heap();
1033 }
1034 }
1036 if(exists $heap->{'client'}) {
1037 $heap->{'client'}->put($client_answer);
1038 }
1039 }
1040 $kernel->sig(CHLD => "child_reap");
1041 }
1043 sub handle_task_debug {
1044 my $result = $_[ARG0];
1045 print STDERR "$result\n";
1046 }
1048 sub handle_task_done {
1049 my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1050 delete $heap->{task}->{$task_id};
1051 $kernel->yield("next_task");
1052 }
1054 sub process_task {
1055 no strict "refs";
1056 my ($session, $heap, $input) = @_;
1057 my $session_id = $session->ID;
1058 my ($msg, $msg_hash, $module);
1059 my $error = 0;
1060 my $answer_l;
1061 my ($answer_header, @answer_target_l, $answer_source);
1062 my $client_answer = "";
1064 daemon_log("", 5);
1065 daemon_log("$session_id INFO: Incoming msg with session ID $session_id from '".$heap->{'remote_ip'}."'", 5);
1066 #daemon_log("$session_id DEBUG: Incoming msg:\n$input", 9);
1068 ####################
1069 # check incoming msg
1070 # msg is from a new client or gosa
1071 ($msg, $msg_hash, $module) = &input_from_unknown_host($input, $session_id);
1072 # msg is from a gosa-si-server or gosa-si-bus
1073 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1074 ($msg, $msg_hash, $module) = &input_from_known_server($input, $heap->{'remote_ip'}, $session_id);
1075 }
1076 # msg is from a gosa-si-client
1077 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1078 ($msg, $msg_hash, $module) = &input_from_known_client($input, $heap->{'remote_ip'}, $session_id);
1079 }
1080 # an error occurred
1081 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1082 # if an incoming msg could not be decrypted (maybe a wrong key), send client a ping. If the client
1083 # could not understand a msg from its server the client cause a re-registering process
1084 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);
1085 my $sql_statement = "SELECT * FROM $main::known_clients_tn WHERE (hostname LIKE '".$heap->{'remote_ip'}."%')";
1086 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1087 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1088 my $host_name = $hit->{'hostname'};
1089 my $host_key = $hit->{'hostkey'};
1090 my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source> <target>$host_name</target></xml>";
1091 my $error = &send_msg_to_target($ping_msg, $host_name, $host_key, "gosa_ping", $session_id);
1092 &update_jobdb_status_for_send_msgs($ping_msg, $error);
1093 }
1094 $error++;
1095 }
1097 ######################
1098 # process incoming msg
1099 if( $error == 0) {
1100 daemon_log("$session_id INFO: Incoming msg with header '".@{$msg_hash->{'header'}}[0].
1101 "' from '".$heap->{'remote_ip'}."'", 5);
1102 daemon_log("$session_id DEBUG: Processing module ".$module, 7);
1103 $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1105 if ( 0 < @{$answer_l} ) {
1106 my $answer_str = join("\n", @{$answer_l});
1107 daemon_log("$session_id DEBUG: $module: Got answer from module: \n".$answer_str,8);
1108 } else {
1109 daemon_log("$session_id DEBUG: $module: Got no answer from module!" ,8);
1110 }
1112 }
1113 if( !$answer_l ) { $error++ };
1115 ########
1116 # answer
1117 if( $error == 0 ) {
1119 foreach my $answer ( @{$answer_l} ) {
1120 # for each answer in answer list
1122 # check outgoing msg to xml validity
1123 my $answer_hash = &check_outgoing_xml_validity($answer);
1124 if( not defined $answer_hash ) {
1125 next;
1126 }
1128 $answer_header = @{$answer_hash->{'header'}}[0];
1129 @answer_target_l = @{$answer_hash->{'target'}};
1130 $answer_source = @{$answer_hash->{'source'}}[0];
1132 # deliver msg to all targets
1133 foreach my $answer_target ( @answer_target_l ) {
1135 # targets of msg are all gosa-si-clients in known_clients_db
1136 if( $answer_target eq "*" ) {
1137 # answer is for all clients
1138 my $sql_statement= "SELECT * FROM known_clients";
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 $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1144 &update_jobdb_status_for_send_msgs($answer, $error);
1145 }
1146 }
1148 # targets of msg are all gosa-si-server in known_server_db
1149 elsif( $answer_target eq "KNOWN_SERVER" ) {
1150 # answer is for all server in known_server
1151 my $sql_statement= "SELECT * FROM known_server";
1152 my $query_res = $known_server_db->select_dbentry( $sql_statement );
1153 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1154 my $host_name = $hit->{hostname};
1155 my $host_key = $hit->{hostkey};
1156 $answer =~ s/KNOWN_SERVER/$host_name/g;
1157 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1158 &update_jobdb_status_for_send_msgs($answer, $error);
1159 }
1160 }
1162 # target of msg is GOsa
1163 elsif( $answer_target eq "GOSA" ) {
1164 my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1165 my $add_on = "";
1166 if( defined $session_id ) {
1167 $add_on = ".session_id=$session_id";
1168 }
1169 # answer is for GOSA and has to returned to connected client
1170 my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1171 $client_answer = $gosa_answer.$add_on;
1172 }
1174 # target of msg is job queue at this host
1175 elsif( $answer_target eq "JOBDB") {
1176 $answer =~ /<header>(\S+)<\/header>/;
1177 my $header;
1178 if( defined $1 ) { $header = $1; }
1179 my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1180 &update_jobdb_status_for_send_msgs($answer, $error);
1181 }
1183 # target of msg is a mac address
1184 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 ) {
1185 daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients", 5);
1186 my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1187 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1188 my $found_ip_flag = 0;
1189 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1190 my $host_name = $hit->{hostname};
1191 my $host_key = $hit->{hostkey};
1192 $answer =~ s/$answer_target/$host_name/g;
1193 daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1194 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1195 &update_jobdb_status_for_send_msgs($answer, $error);
1196 $found_ip_flag++ ;
1197 }
1198 if( $found_ip_flag == 0) {
1199 daemon_log("$session_id WARNING: no host found in known_clients with mac address '$answer_target'", 3);
1200 if( $bus_activ eq "true" ) {
1201 daemon_log("$session_id INFO: try to forward msg '$answer_header' to bus '$bus_address'", 5);
1202 my $sql_statement = "SELECT * FROM known_server WHERE hostname='$bus_address'";
1203 my $query_res = $known_server_db->select_dbentry( $sql_statement );
1204 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1205 my $bus_address = $hit->{hostname};
1206 my $bus_key = $hit->{hostkey};
1207 my $error = &send_msg_to_target($answer, $bus_address, $bus_key, $answer_header, $session_id);
1208 &update_jobdb_status_for_send_msgs($answer, $error);
1209 last;
1210 }
1211 }
1213 }
1215 # answer is for one specific host
1216 } else {
1217 # get encrypt_key
1218 my $encrypt_key = &get_encrypt_key($answer_target);
1219 if( not defined $encrypt_key ) {
1220 # unknown target, forward msg to bus
1221 daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1222 if( $bus_activ eq "true" ) {
1223 daemon_log("$session_id INFO: try to forward msg '$answer_header' to bus '$bus_address'", 5);
1224 my $sql_statement = "SELECT * FROM known_server WHERE hostname='$bus_address'";
1225 my $query_res = $known_server_db->select_dbentry( $sql_statement );
1226 my $res_length = keys( %{$query_res} );
1227 if( $res_length == 0 ){
1228 daemon_log("$session_id WARNING: send '$answer_header' to '$bus_address' failed, ".
1229 "no bus found in known_server", 3);
1230 }
1231 else {
1232 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1233 my $bus_key = $hit->{hostkey};
1234 my $error = &send_msg_to_target($answer, $bus_address, $bus_key, $answer_header,$session_id );
1235 &update_jobdb_status_for_send_msgs($answer, $error);
1236 }
1237 }
1238 }
1239 next;
1240 }
1241 my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1242 &update_jobdb_status_for_send_msgs($answer, $error);
1243 }
1244 }
1245 }
1246 }
1248 my $filter = POE::Filter::Reference->new();
1249 my %result = (
1250 status => "seems ok to me",
1251 answer => $client_answer,
1252 );
1254 my $output = $filter->put( [ \%result ] );
1255 print @$output;
1258 }
1261 sub trigger_db_loop {
1262 my ($kernel) = @_ ;
1263 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1264 $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay);
1265 $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1266 $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1267 $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1268 }
1271 sub watch_for_done_jobs {
1272 my ($kernel,$heap) = @_[KERNEL, HEAP];
1274 my $sql_statement = "SELECT * FROM ".$job_queue_tn.
1275 " WHERE status='done'";
1276 my $res = $job_db->select_dbentry( $sql_statement );
1278 while( my ($id, $hit) = each %{$res} ) {
1279 my $jobdb_id = $hit->{id};
1280 my $sql_statement = "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1281 my $res = $job_db->del_dbentry($sql_statement);
1282 }
1284 $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1285 }
1288 sub watch_for_new_jobs {
1289 if($watch_for_new_jobs_in_progress == 0) {
1290 $watch_for_new_jobs_in_progress = 1;
1291 my ($kernel,$heap) = @_[KERNEL, HEAP];
1293 # check gosa job queue for jobs with executable timestamp
1294 my $timestamp = &get_time();
1295 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE status='waiting' AND (CAST (timestamp AS INTEGER)) < $timestamp ORDER BY timestamp";
1296 my $res = $job_db->exec_statement( $sql_statement );
1298 # Merge all new jobs that would do the same actions
1299 my @drops;
1300 my $hits;
1301 foreach my $hit (reverse @{$res} ) {
1302 my $macaddress= lc @{$hit}[8];
1303 my $headertag= @{$hit}[5];
1304 if(
1305 defined($hits->{$macaddress}) &&
1306 defined($hits->{$macaddress}->{$headertag}) &&
1307 defined($hits->{$macaddress}->{$headertag}[0])
1308 ) {
1309 push @drops, "DELETE FROM $job_queue_tn WHERE id = $hits->{$macaddress}->{$headertag}[0]";
1310 }
1311 $hits->{$macaddress}->{$headertag}= $hit;
1312 }
1314 # Delete new jobs with a matching job in state 'processing'
1315 foreach my $macaddress (keys %{$hits}) {
1316 foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1317 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1318 if(defined($jobdb_id)) {
1319 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND headertag='$jobdb_headertag' AND status='processing'";
1320 my $res = $job_db->exec_statement( $sql_statement );
1321 foreach my $hit (@{$res}) {
1322 push @drops, "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1323 }
1324 } else {
1325 daemon_log("J ERROR: Job without id exists for macaddress $macaddress!", 1);
1326 }
1327 }
1328 }
1330 # Commit deletion
1331 $job_db->exec_statementlist(\@drops);
1333 # Look for new jobs that could be executed
1334 foreach my $macaddress (keys %{$hits}) {
1336 # Look if there is an executing job
1337 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='processing'";
1338 my $res = $job_db->exec_statement( $sql_statement );
1340 # Skip new jobs for host if there is a processing job
1341 if(defined($res) and defined @{$res}[0]) {
1342 next;
1343 }
1345 foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1346 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1347 if(defined($jobdb_id)) {
1348 my $job_msg = @{$hits->{$macaddress}->{$jobdb_headertag}}[7];
1350 daemon_log("J DEBUG: its time to execute $job_msg", 7);
1351 my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1352 my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1354 # expect macaddress is unique!!!!!!
1355 my $target = $res_hash->{1}->{hostname};
1357 # change header
1358 $job_msg =~ s/<header>job_/<header>gosa_/;
1360 # add sqlite_id
1361 $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1363 $job_msg =~ /<header>(\S+)<\/header>/;
1364 my $header = $1 ;
1365 my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");
1367 # update status in job queue to 'processing'
1368 $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
1369 my $res = $job_db->update_dbentry($sql_statement);
1370 # TODO: abfangen ob alles in ordnung ist oder nicht, wenn nicht error schmeißen
1372 # We don't want parallel processing
1373 last;
1374 }
1375 }
1376 }
1378 $watch_for_new_jobs_in_progress = 0;
1379 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1380 }
1381 }
1384 sub watch_for_new_messages {
1385 my ($kernel,$heap) = @_[KERNEL, HEAP];
1386 my @coll_user_msg; # collection list of outgoing messages
1388 # check messaging_db for new incoming messages with executable timestamp
1389 my $timestamp = &get_time();
1390 my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS INTEGER))<$timestamp AND flag='n' AND direction='in' )";
1391 my $res = $messaging_db->exec_statement( $sql_statement );
1392 foreach my $hit (@{$res}) {
1394 # create outgoing messages
1395 my $message_to = @{$hit}[3];
1396 # translate message_to to plain login name
1397 my @message_to_l = split(/,/, $message_to);
1398 my %receiver_h;
1399 foreach my $receiver (@message_to_l) {
1400 if ($receiver =~ /^u_([\s\S]*)$/) {
1401 $receiver_h{$1} = 0;
1402 } elsif ($receiver =~ /^g_([\s\S]*)$/) {
1403 # TODO implement receiver translation
1404 } else {
1405 my $sbjct = &encode_base64(@{$hit}[1]);
1406 my $msg = &encode_base64(@{$hit}[7]);
1407 &daemon_log("M WARNING: unknown receiver '$receiver' for a user-message 'sbjct - msg'", 3);
1408 }
1409 }
1410 my @receiver_l = keys(%receiver_h);
1412 my $message_id = @{$hit}[0];
1414 #add each outgoing msg to messaging_db
1415 my $receiver;
1416 foreach $receiver (@receiver_l) {
1417 my $sql_statement = "INSERT INTO $messaging_tn (id, subject, message_from, message_to, flag, direction, delivery_time, message, timestamp) ".
1418 "VALUES ('".
1419 $message_id."', '". # id
1420 @{$hit}[1]."', '". # subject
1421 @{$hit}[2]."', '". # message_from
1422 $receiver."', '". # message_to
1423 "none"."', '". # flag
1424 "out"."', '". # direction
1425 @{$hit}[6]."', '". # delivery_time
1426 @{$hit}[7]."', '". # message
1427 $timestamp."'". # timestamp
1428 ")";
1429 &daemon_log("M DEBUG: $sql_statement", 1);
1430 my $res = $messaging_db->exec_statement($sql_statement);
1431 &daemon_log("M INFO: message '".@{$hit}[0]."' is prepared for delivery to receiver '$receiver'", 5);
1432 }
1434 # set incoming message to flag d=deliverd
1435 $sql_statement = "UPDATE $messaging_tn SET flag='p' WHERE id='$message_id'";
1436 &daemon_log("M DEBUG: $sql_statement", 7);
1437 $res = $messaging_db->update_dbentry($sql_statement);
1438 &daemon_log("M INFO: message '$message_id' is set to flag 'p' (processed)", 5);
1439 }
1441 $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1442 return;
1443 }
1445 sub watch_for_delivery_messages {
1446 my ($kernel, $heap) = @_[KERNEL, HEAP];
1448 # select outgoing messages
1449 my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( flag='p' AND direction='out' )";
1450 #&daemon_log("0 DEBUG: $sql", 7);
1451 my $res = $messaging_db->exec_statement( $sql_statement );
1453 # build out msg for each usr
1454 foreach my $hit (@{$res}) {
1455 my $receiver = @{$hit}[3];
1456 my $msg_id = @{$hit}[0];
1457 my $subject = @{$hit}[1];
1458 my $message = @{$hit}[7];
1460 # resolve usr -> host where usr is logged in
1461 my $sql = "SELECT * FROM $login_users_tn WHERE (user='$receiver')";
1462 #&daemon_log("0 DEBUG: $sql", 7);
1463 my $res = $login_users_db->exec_statement($sql);
1465 # reciver is logged in nowhere
1466 if (not ref(@$res[0]) eq "ARRAY") { next; }
1468 my $send_succeed = 0;
1469 foreach my $hit (@$res) {
1470 my $receiver_host = @$hit[0];
1471 &daemon_log("M DEBUG: user '$receiver' is logged in at host '$receiver_host'", 7);
1473 # fetch key to encrypt msg propperly for usr/host
1474 my $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$receiver_host')";
1475 &daemon_log("0 DEBUG: $sql", 7);
1476 my $res = $known_clients_db->exec_statement($sql);
1478 # host is already down
1479 if (not ref(@$res[0]) eq "ARRAY") { next; }
1481 # host is on
1482 my $receiver_key = @{@{$res}[0]}[2];
1483 my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
1484 my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data );
1485 my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0);
1486 if ($error == 0 ) {
1487 $send_succeed++ ;
1488 }
1489 }
1491 if ($send_succeed) {
1492 # set outgoing msg at db to deliverd
1493 my $sql = "UPDATE $messaging_tn SET flag='d' WHERE (id='$msg_id' AND direction='out' AND message_to='$receiver')";
1494 &daemon_log("0 DEBUG: $sql", 7);
1495 my $res = $messaging_db->exec_statement($sql);
1496 }
1497 }
1499 $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1500 return;
1501 }
1504 sub watch_for_done_messages {
1505 my ($kernel,$heap) = @_[KERNEL, HEAP];
1507 my $sql = "SELECT * FROM $messaging_tn WHERE (flag='p' AND direction='in')";
1508 #&daemon_log("0 DEBUG: $sql", 7);
1509 my $res = $messaging_db->exec_statement($sql);
1511 foreach my $hit (@{$res}) {
1512 my $msg_id = @{$hit}[0];
1514 my $sql = "SELECT * FROM $messaging_tn WHERE (id='$msg_id' AND direction='out' AND (NOT flag='s'))";
1515 #&daemon_log("0 DEBUG: $sql", 7);
1516 my $res = $messaging_db->exec_statement($sql);
1518 # not all usr msgs have been seen till now
1519 if ( ref(@$res[0]) eq "ARRAY") { next; }
1521 $sql = "DELETE FROM $messaging_tn WHERE (id='$msg_id')";
1522 #&daemon_log("0 DEBUG: $sql", 7);
1523 $res = $messaging_db->exec_statement($sql);
1525 }
1527 $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1528 return;
1529 }
1532 sub get_ldap_handle {
1533 my ($session_id) = @_;
1534 my $heap;
1535 my $ldap_handle;
1537 if (not defined $session_id ) { $session_id = 0 };
1538 if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
1540 if ($session_id == 0) {
1541 daemon_log("$session_id DEBUG: get_ldap_handle invoked without a session_id, create a new ldap_handle", 7);
1542 $ldap_handle = Net::LDAP->new( $ldap_uri );
1543 $ldap_handle->bind($ldap_admin_dn, password => $ldap_admin_password);
1545 } else {
1546 my $session_reference = $global_kernel->ID_id_to_session($session_id);
1547 if( defined $session_reference ) {
1548 $heap = $session_reference->get_heap();
1549 }
1551 if (not defined $heap) {
1552 daemon_log("$session_id DEBUG: cannot get heap for session_id '$session_id'", 7);
1553 return;
1554 }
1556 # TODO: This "if" is nonsense, because it doesn't prove that the
1557 # used handle is still valid - or if we've to reconnect...
1558 #if (not exists $heap->{ldap_handle}) {
1559 $ldap_handle = Net::LDAP->new( $ldap_uri );
1560 $ldap_handle->bind($ldap_admin_dn, password => $ldap_admin_password);
1561 $heap->{ldap_handle} = $ldap_handle;
1562 #}
1563 }
1564 return $ldap_handle;
1565 }
1568 sub change_fai_state {
1569 my ($st, $targets, $session_id) = @_;
1570 $session_id = 0 if not defined $session_id;
1571 # Set FAI state to localboot
1572 my %mapActions= (
1573 reboot => '',
1574 update => 'softupdate',
1575 localboot => 'localboot',
1576 reinstall => 'install',
1577 rescan => '',
1578 wake => '',
1579 memcheck => 'memcheck',
1580 sysinfo => 'sysinfo',
1581 install => 'install',
1582 );
1584 # Return if this is unknown
1585 if (!exists $mapActions{ $st }){
1586 daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1);
1587 return;
1588 }
1590 my $state= $mapActions{ $st };
1592 my $ldap_handle = &get_ldap_handle($session_id);
1593 if( defined($ldap_handle) ) {
1595 # Build search filter for hosts
1596 my $search= "(&(objectClass=GOhard)";
1597 foreach (@{$targets}){
1598 $search.= "(macAddress=$_)";
1599 }
1600 $search.= ")";
1602 # If there's any host inside of the search string, procress them
1603 if (!($search =~ /macAddress/)){
1604 daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);
1605 return;
1606 }
1608 # Perform search for Unit Tag
1609 my $mesg = $ldap_handle->search(
1610 base => $ldap_base,
1611 scope => 'sub',
1612 attrs => ['dn', 'FAIstate', 'objectClass'],
1613 filter => "$search"
1614 );
1616 if ($mesg->count) {
1617 my @entries = $mesg->entries;
1618 foreach my $entry (@entries) {
1619 # Only modify entry if it is not set to '$state'
1620 if ($entry->get_value("FAIstate") ne "$state"){
1621 daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
1622 my $result;
1623 my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
1624 if (exists $tmp{'FAIobject'}){
1625 if ($state eq ''){
1626 $result= $ldap_handle->modify($entry->dn, changes => [
1627 delete => [ FAIstate => [] ] ]);
1628 } else {
1629 $result= $ldap_handle->modify($entry->dn, changes => [
1630 replace => [ FAIstate => $state ] ]);
1631 }
1632 } elsif ($state ne ''){
1633 $result= $ldap_handle->modify($entry->dn, changes => [
1634 add => [ objectClass => 'FAIobject' ],
1635 add => [ FAIstate => $state ] ]);
1636 }
1638 # Errors?
1639 if ($result->code){
1640 daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
1641 }
1642 } else {
1643 daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 7);
1644 }
1645 }
1646 }
1647 # if no ldap handle defined
1648 } else {
1649 daemon_log("$session_id ERROR: no LDAP handle defined for update FAIstate", 1);
1650 }
1652 }
1655 sub change_goto_state {
1656 my ($st, $targets, $session_id) = @_;
1657 $session_id = 0 if not defined $session_id;
1659 # Switch on or off?
1660 my $state= $st eq 'active' ? 'active': 'locked';
1662 my $ldap_handle = &get_ldap_handle($session_id);
1663 if( defined($ldap_handle) ) {
1665 # Build search filter for hosts
1666 my $search= "(&(objectClass=GOhard)";
1667 foreach (@{$targets}){
1668 $search.= "(macAddress=$_)";
1669 }
1670 $search.= ")";
1672 # If there's any host inside of the search string, procress them
1673 if (!($search =~ /macAddress/)){
1674 return;
1675 }
1677 # Perform search for Unit Tag
1678 my $mesg = $ldap_handle->search(
1679 base => $ldap_base,
1680 scope => 'sub',
1681 attrs => ['dn', 'gotoMode'],
1682 filter => "$search"
1683 );
1685 if ($mesg->count) {
1686 my @entries = $mesg->entries;
1687 foreach my $entry (@entries) {
1689 # Only modify entry if it is not set to '$state'
1690 if ($entry->get_value("gotoMode") ne $state){
1692 daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
1693 my $result;
1694 $result= $ldap_handle->modify($entry->dn, changes => [
1695 replace => [ gotoMode => $state ] ]);
1697 # Errors?
1698 if ($result->code){
1699 &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
1700 }
1702 }
1703 }
1704 }
1706 }
1707 }
1710 sub run_create_fai_server_db {
1711 my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
1712 my $session_id = $session->ID;
1713 my $task = POE::Wheel::Run->new(
1714 Program => sub { &create_fai_server_db($table_name,$kernel, undef, $session_id) },
1715 StdoutEvent => "session_run_result",
1716 StderrEvent => "session_run_debug",
1717 CloseEvent => "session_run_done",
1718 );
1720 $heap->{task}->{ $task->ID } = $task;
1721 return;
1722 }
1725 sub create_fai_server_db {
1726 my ($table_name, $kernel, $dont_create_packages_list, $session_id) = @_;
1727 my $result;
1729 if (not defined $session_id) { $session_id = 0; }
1730 my $ldap_handle = &get_ldap_handle();
1731 if(defined($ldap_handle)) {
1732 daemon_log("$session_id INFO: create_fai_server_db: start", 5);
1733 my $mesg= $ldap_handle->search(
1734 base => $ldap_base,
1735 scope => 'sub',
1736 attrs => ['FAIrepository', 'gosaUnitTag'],
1737 filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
1738 );
1739 if($mesg->{'resultCode'} == 0 &&
1740 $mesg->count != 0) {
1741 foreach my $entry (@{$mesg->{entries}}) {
1742 if($entry->exists('FAIrepository')) {
1743 # Add an entry for each Repository configured for server
1744 foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
1745 my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
1746 my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
1747 $result= $fai_server_db->add_dbentry( {
1748 table => $table_name,
1749 primkey => ['server', 'release', 'tag'],
1750 server => $tmp_url,
1751 release => $tmp_release,
1752 sections => $tmp_sections,
1753 tag => (length($tmp_tag)>0)?$tmp_tag:"",
1754 } );
1755 }
1756 }
1757 }
1758 }
1759 daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
1761 # TODO: Find a way to post the 'create_packages_list_db' event
1762 if(not defined($dont_create_packages_list)) {
1763 &create_packages_list_db(undef, undef, $session_id);
1764 }
1765 }
1767 $ldap_handle->disconnect;
1768 return $result;
1769 }
1772 sub run_create_fai_release_db {
1773 my ($session, $heap, $table_name) = @_[SESSION, HEAP, ARG0];
1774 my $session_id = $session->ID;
1775 my $task = POE::Wheel::Run->new(
1776 Program => sub { &create_fai_release_db($table_name, $session_id) },
1777 StdoutEvent => "session_run_result",
1778 StderrEvent => "session_run_debug",
1779 CloseEvent => "session_run_done",
1780 );
1782 $heap->{task}->{ $task->ID } = $task;
1783 return;
1784 }
1787 sub create_fai_release_db {
1788 my ($table_name, $session_id) = @_;
1789 my $result;
1791 # used for logging
1792 if (not defined $session_id) { $session_id = 0; }
1794 my $ldap_handle = &get_ldap_handle();
1795 if(defined($ldap_handle)) {
1796 daemon_log("$session_id INFO: create_fai_release_db: start",5);
1797 my $mesg= $ldap_handle->search(
1798 base => $ldap_base,
1799 scope => 'sub',
1800 attrs => [],
1801 filter => "(&(objectClass=organizationalUnit)(ou=fai))",
1802 );
1803 if($mesg->{'resultCode'} == 0 &&
1804 $mesg->count != 0) {
1805 # Walk through all possible FAI container ou's
1806 my @sql_list;
1807 my $timestamp= &get_time();
1808 foreach my $ou (@{$mesg->{entries}}) {
1809 my $tmp_classes= resolve_fai_classes($ou->dn, $ldap_handle, $session_id);
1810 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
1811 my @tmp_array=get_fai_release_entries($tmp_classes);
1812 if(@tmp_array) {
1813 foreach my $entry (@tmp_array) {
1814 if(defined($entry) && ref($entry) eq 'HASH') {
1815 my $sql=
1816 "INSERT INTO $table_name "
1817 ."(timestamp, release, class, type, state) VALUES ("
1818 .$timestamp.","
1819 ."'".$entry->{'release'}."',"
1820 ."'".$entry->{'class'}."',"
1821 ."'".$entry->{'type'}."',"
1822 ."'".$entry->{'state'}."')";
1823 push @sql_list, $sql;
1824 }
1825 }
1826 }
1827 }
1828 }
1830 daemon_log("$session_id DEBUG: Inserting ".scalar @sql_list." entries to DB",8);
1831 if(@sql_list) {
1832 unshift @sql_list, "VACUUM";
1833 unshift @sql_list, "DELETE FROM $table_name";
1834 $fai_release_db->exec_statementlist(\@sql_list);
1835 }
1836 daemon_log("$session_id DEBUG: Done with inserting",7);
1837 }
1838 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
1839 }
1840 $ldap_handle->disconnect;
1841 return $result;
1842 }
1844 sub get_fai_types {
1845 my $tmp_classes = shift || return undef;
1846 my @result;
1848 foreach my $type(keys %{$tmp_classes}) {
1849 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
1850 my $entry = {
1851 type => $type,
1852 state => $tmp_classes->{$type}[0],
1853 };
1854 push @result, $entry;
1855 }
1856 }
1858 return @result;
1859 }
1861 sub get_fai_state {
1862 my $result = "";
1863 my $tmp_classes = shift || return $result;
1865 foreach my $type(keys %{$tmp_classes}) {
1866 if(defined($tmp_classes->{$type}[0])) {
1867 $result = $tmp_classes->{$type}[0];
1869 # State is equal for all types in class
1870 last;
1871 }
1872 }
1874 return $result;
1875 }
1877 sub resolve_fai_classes {
1878 my ($fai_base, $ldap_handle, $session_id) = @_;
1879 if (not defined $session_id) { $session_id = 0; }
1880 my $result;
1881 my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
1882 my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
1883 my $fai_classes;
1885 daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base",7);
1886 my $mesg= $ldap_handle->search(
1887 base => $fai_base,
1888 scope => 'sub',
1889 attrs => ['cn','objectClass','FAIstate'],
1890 filter => $fai_filter,
1891 );
1892 daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries",7);
1894 if($mesg->{'resultCode'} == 0 &&
1895 $mesg->count != 0) {
1896 foreach my $entry (@{$mesg->{entries}}) {
1897 if($entry->exists('cn')) {
1898 my $tmp_dn= $entry->dn();
1900 # Skip classname and ou dn parts for class
1901 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?),$fai_base$/;
1903 # Skip classes without releases
1904 if((!defined($tmp_release)) || length($tmp_release)==0) {
1905 next;
1906 }
1908 my $tmp_cn= $entry->get_value('cn');
1909 my $tmp_state= $entry->get_value('FAIstate');
1911 my $tmp_type;
1912 # Get FAI type
1913 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
1914 if(grep $_ eq $oclass, @possible_fai_classes) {
1915 $tmp_type= $oclass;
1916 last;
1917 }
1918 }
1920 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
1921 # A Subrelease
1922 my @sub_releases = split(/,/, $tmp_release);
1924 # Walk through subreleases and build hash tree
1925 my $hash;
1926 while(my $tmp_sub_release = pop @sub_releases) {
1927 $hash .= "\{'$tmp_sub_release'\}->";
1928 }
1929 eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
1930 } else {
1931 # A branch, no subrelease
1932 push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
1933 }
1934 } elsif (!$entry->exists('cn')) {
1935 my $tmp_dn= $entry->dn();
1936 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?),$fai_base$/;
1938 # Skip classes without releases
1939 if((!defined($tmp_release)) || length($tmp_release)==0) {
1940 next;
1941 }
1943 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
1944 # A Subrelease
1945 my @sub_releases= split(/,/, $tmp_release);
1947 # Walk through subreleases and build hash tree
1948 my $hash;
1949 while(my $tmp_sub_release = pop @sub_releases) {
1950 $hash .= "\{'$tmp_sub_release'\}->";
1951 }
1952 # Remove the last two characters
1953 chop($hash);
1954 chop($hash);
1956 eval('$fai_classes->'.$hash.'= {}');
1957 } else {
1958 # A branch, no subrelease
1959 if(!exists($fai_classes->{$tmp_release})) {
1960 $fai_classes->{$tmp_release} = {};
1961 }
1962 }
1963 }
1964 }
1966 # The hash is complete, now we can honor the copy-on-write based missing entries
1967 foreach my $release (keys %$fai_classes) {
1968 $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
1969 }
1970 }
1971 return $result;
1972 }
1974 sub apply_fai_inheritance {
1975 my $fai_classes = shift || return {};
1976 my $tmp_classes;
1978 # Get the classes from the branch
1979 foreach my $class (keys %{$fai_classes}) {
1980 # Skip subreleases
1981 if($class =~ /^ou=.*$/) {
1982 next;
1983 } else {
1984 $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
1985 }
1986 }
1988 # Apply to each subrelease
1989 foreach my $subrelease (keys %{$fai_classes}) {
1990 if($subrelease =~ /ou=/) {
1991 foreach my $tmp_class (keys %{$tmp_classes}) {
1992 if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
1993 $fai_classes->{$subrelease}->{$tmp_class} =
1994 deep_copy($tmp_classes->{$tmp_class});
1995 } else {
1996 foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
1997 if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
1998 $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
1999 deep_copy($tmp_classes->{$tmp_class}->{$type});
2000 }
2001 }
2002 }
2003 }
2004 }
2005 }
2007 # Find subreleases in deeper levels
2008 foreach my $subrelease (keys %{$fai_classes}) {
2009 if($subrelease =~ /ou=/) {
2010 foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
2011 if($subsubrelease =~ /ou=/) {
2012 apply_fai_inheritance($fai_classes->{$subrelease});
2013 }
2014 }
2015 }
2016 }
2018 return $fai_classes;
2019 }
2021 sub get_fai_release_entries {
2022 my $tmp_classes = shift || return;
2023 my $parent = shift || "";
2024 my @result = shift || ();
2026 foreach my $entry (keys %{$tmp_classes}) {
2027 if(defined($entry)) {
2028 if($entry =~ /^ou=.*$/) {
2029 my $release_name = $entry;
2030 $release_name =~ s/ou=//g;
2031 if(length($parent)>0) {
2032 $release_name = $parent."/".$release_name;
2033 }
2034 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2035 foreach my $bufentry(@bufentries) {
2036 push @result, $bufentry;
2037 }
2038 } else {
2039 my @types = get_fai_types($tmp_classes->{$entry});
2040 foreach my $type (@types) {
2041 push @result,
2042 {
2043 'class' => $entry,
2044 'type' => $type->{'type'},
2045 'release' => $parent,
2046 'state' => $type->{'state'},
2047 };
2048 }
2049 }
2050 }
2051 }
2053 return @result;
2054 }
2056 sub deep_copy {
2057 my $this = shift;
2058 if (not ref $this) {
2059 $this;
2060 } elsif (ref $this eq "ARRAY") {
2061 [map deep_copy($_), @$this];
2062 } elsif (ref $this eq "HASH") {
2063 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2064 } else { die "what type is $_?" }
2065 }
2068 sub session_run_result {
2069 my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];
2070 $kernel->sig(CHLD => "child_reap");
2071 }
2073 sub session_run_debug {
2074 my $result = $_[ARG0];
2075 print STDERR "$result\n";
2076 }
2078 sub session_run_done {
2079 my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2080 delete $heap->{task}->{$task_id};
2081 }
2084 sub create_sources_list {
2085 my $session_id = shift;
2086 my $ldap_handle = &main::get_ldap_handle;
2087 my $result="/tmp/gosa_si_tmp_sources_list";
2089 # Remove old file
2090 if(stat($result)) {
2091 unlink($result);
2092 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7);
2093 }
2095 my $fh;
2096 open($fh, ">$result");
2097 if (not defined $fh) {
2098 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7);
2099 return undef;
2100 }
2101 if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2102 my $mesg=$ldap_handle->search(
2103 base => $main::ldap_server_dn,
2104 scope => 'base',
2105 attrs => 'FAIrepository',
2106 filter => 'objectClass=FAIrepositoryServer'
2107 );
2108 if($mesg->count) {
2109 foreach my $entry(@{$mesg->{'entries'}}) {
2110 foreach my $value(@{$entry->get_value('FAIrepository', asref => 1)}) {
2111 my ($server, $tag, $release, $sections)= split /\|/, $value;
2112 my $line = "deb $server $release";
2113 $sections =~ s/,/ /g;
2114 $line.= " $sections";
2115 print $fh $line."\n";
2116 }
2117 }
2118 }
2119 } else {
2120 if (defined $main::ldap_server_dn){
2121 &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1);
2122 } else {
2123 &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2124 }
2125 }
2126 close($fh);
2128 return $result;
2129 }
2132 sub run_create_packages_list_db {
2133 my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2134 my $session_id = $session->ID;
2136 my $task = POE::Wheel::Run->new(
2137 Priority => +20,
2138 Program => sub {&create_packages_list_db(undef, undef, $session_id)},
2139 StdoutEvent => "session_run_result",
2140 StderrEvent => "session_run_debug",
2141 CloseEvent => "session_run_done",
2142 );
2143 $heap->{task}->{ $task->ID } = $task;
2144 }
2147 sub create_packages_list_db {
2148 my ($ldap_handle, $sources_file, $session_id) = @_;
2150 # it should not be possible to trigger a recreation of packages_list_db
2151 # while packages_list_db is under construction, so set flag packages_list_under_construction
2152 # which is tested befor recreation can be started
2153 if (-r $packages_list_under_construction) {
2154 daemon_log("$session_id WARNING: packages_list_db is right now under construction, please wait until this process is finished", 3);
2155 return;
2156 } else {
2157 daemon_log("$session_id INFO: create_packages_list_db: start", 5);
2158 # set packages_list_under_construction to true
2159 system("touch $packages_list_under_construction");
2160 @packages_list_statements=();
2161 }
2163 if (not defined $session_id) { $session_id = 0; }
2164 if (not defined $ldap_handle) {
2165 $ldap_handle= &get_ldap_handle();
2167 if (not defined $ldap_handle) {
2168 daemon_log("$session_id ERROR: no ldap_handle available to create_packages_list_db", 1);
2169 unlink($packages_list_under_construction);
2170 return;
2171 }
2172 }
2173 if (not defined $sources_file) {
2174 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5);
2175 $sources_file = &create_sources_list($session_id);
2176 }
2178 if (not defined $sources_file) {
2179 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1);
2180 unlink($packages_list_under_construction);
2181 return;
2182 }
2184 my $line;
2186 open(CONFIG, "<$sources_file") or do {
2187 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2188 unlink($packages_list_under_construction);
2189 return;
2190 };
2192 # Read lines
2193 while ($line = <CONFIG>){
2194 # Unify
2195 chop($line);
2196 $line =~ s/^\s+//;
2197 $line =~ s/^\s+/ /;
2199 # Strip comments
2200 $line =~ s/#.*$//g;
2202 # Skip empty lines
2203 if ($line =~ /^\s*$/){
2204 next;
2205 }
2207 # Interpret deb line
2208 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2209 my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2210 my $section;
2211 foreach $section (split(' ', $sections)){
2212 &parse_package_info( $baseurl, $dist, $section, $session_id );
2213 }
2214 }
2215 }
2217 close (CONFIG);
2219 find(\&cleanup_and_extract, keys( %repo_dirs ));
2220 eval {
2221 &main::strip_packages_list_statements();
2222 };
2223 if($@) {
2224 daemon_log("$session_id ERROR: Preparation of statement list failed with '$@'!", 1);
2225 }
2226 unshift @packages_list_statements, "VACUUM";
2227 eval {
2228 $packages_list_db->exec_statementlist(\@packages_list_statements);
2229 };
2230 if($@) {
2231 daemon_log("$session_id ERROR: Updating package_list_db failed with '$@'!", 1);
2232 }
2233 unlink($packages_list_under_construction);
2234 daemon_log("$session_id INFO: create_packages_list_db: finished", 5);
2235 return;
2236 }
2239 # This function should do some intensive task to minimize the db-traffic
2240 sub strip_packages_list_statements {
2241 my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2242 my @new_statement_list=();
2243 my $hash;
2244 my $insert_hash;
2245 my $update_hash;
2246 my $delete_hash;
2247 my $local_timestamp=get_time();
2249 foreach my $existing_entry (@existing_entries) {
2250 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
2251 }
2253 foreach my $statement (@packages_list_statements) {
2254 if($statement =~ /^INSERT/i) {
2255 # Assign the values from the insert statement
2256 my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~
2257 /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
2258 if(exists($hash->{$distribution}->{$package}->{$version})) {
2259 # If section or description has changed, update the DB
2260 if(
2261 (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or
2262 (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
2263 ) {
2264 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
2265 }
2266 } else {
2267 # Insert a non-existing entry to db
2268 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2269 }
2270 } elsif ($statement =~ /^UPDATE/i) {
2271 my ($template,$package,$version) = ($1,$2,$3) if $statement =~
2272 /^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;
2273 foreach my $distribution (keys %{$hash}) {
2274 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
2275 # update the insertion hash to execute only one query per package (insert instead insert+update)
2276 @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
2277 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
2278 if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
2279 my $section;
2280 my $description;
2281 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
2282 length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
2283 $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
2284 }
2285 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2286 $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
2287 }
2288 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2289 }
2290 }
2291 }
2292 }
2293 }
2295 # TODO: Check for orphaned entries
2297 # unroll the insert_hash
2298 foreach my $distribution (keys %{$insert_hash}) {
2299 foreach my $package (keys %{$insert_hash->{$distribution}}) {
2300 foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
2301 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
2302 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
2303 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
2304 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
2305 ."'$local_timestamp')";
2306 }
2307 }
2308 }
2310 # unroll the update hash
2311 foreach my $distribution (keys %{$update_hash}) {
2312 foreach my $package (keys %{$update_hash->{$distribution}}) {
2313 foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
2314 my $set = "";
2315 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
2316 $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
2317 }
2318 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2319 $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
2320 }
2321 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
2322 $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
2323 }
2324 if(defined($set) and length($set) > 0) {
2325 $set .= "timestamp = '$local_timestamp'";
2326 } else {
2327 next;
2328 }
2329 push @new_statement_list,
2330 "UPDATE $main::packages_list_tn SET $set WHERE"
2331 ." distribution = '$distribution'"
2332 ." AND package = '$package'"
2333 ." AND version = '$version'";
2334 }
2335 }
2336 }
2338 @packages_list_statements = @new_statement_list;
2339 }
2342 sub parse_package_info {
2343 my ($baseurl, $dist, $section, $session_id)= @_;
2344 my ($package);
2345 if (not defined $session_id) { $session_id = 0; }
2346 my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
2347 $repo_dirs{ "${repo_path}/pool" } = 1;
2349 foreach $package ("Packages.gz"){
2350 daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
2351 get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
2352 parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
2353 }
2355 }
2358 sub get_package {
2359 my ($url, $dest, $session_id)= @_;
2360 if (not defined $session_id) { $session_id = 0; }
2362 my $tpath = dirname($dest);
2363 -d "$tpath" || mkpath "$tpath";
2365 # This is ugly, but I've no time to take a look at "how it works in perl"
2366 if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
2367 system("gunzip -cd '$dest' > '$dest.in'");
2368 daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 5);
2369 unlink($dest);
2370 daemon_log("$session_id DEBUG: delete file '$dest'", 5);
2371 } else {
2372 daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' failed!", 1);
2373 }
2374 return 0;
2375 }
2378 sub parse_package {
2379 my ($path, $dist, $srv_path, $session_id)= @_;
2380 if (not defined $session_id) { $session_id = 0;}
2381 my ($package, $version, $section, $description);
2382 my $PACKAGES;
2383 my $timestamp = &get_time();
2385 if(not stat("$path.in")) {
2386 daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
2387 return;
2388 }
2390 open($PACKAGES, "<$path.in");
2391 if(not defined($PACKAGES)) {
2392 daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1);
2393 return;
2394 }
2396 # Read lines
2397 while (<$PACKAGES>){
2398 my $line = $_;
2399 # Unify
2400 chop($line);
2402 # Use empty lines as a trigger
2403 if ($line =~ /^\s*$/){
2404 my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
2405 push(@packages_list_statements, $sql);
2406 $package = "none";
2407 $version = "none";
2408 $section = "none";
2409 $description = "none";
2410 next;
2411 }
2413 # Trigger for package name
2414 if ($line =~ /^Package:\s/){
2415 ($package)= ($line =~ /^Package: (.*)$/);
2416 next;
2417 }
2419 # Trigger for version
2420 if ($line =~ /^Version:\s/){
2421 ($version)= ($line =~ /^Version: (.*)$/);
2422 next;
2423 }
2425 # Trigger for description
2426 if ($line =~ /^Description:\s/){
2427 ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
2428 next;
2429 }
2431 # Trigger for section
2432 if ($line =~ /^Section:\s/){
2433 ($section)= ($line =~ /^Section: (.*)$/);
2434 next;
2435 }
2437 # Trigger for filename
2438 if ($line =~ /^Filename:\s/){
2439 my ($filename) = ($line =~ /^Filename: (.*)$/);
2440 store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
2441 next;
2442 }
2443 }
2445 close( $PACKAGES );
2446 unlink( "$path.in" );
2447 &main::daemon_log("$session_id DEBUG: unlink '$path.in'", 1);
2448 }
2451 sub store_fileinfo {
2452 my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
2454 my %fileinfo = (
2455 'package' => $package,
2456 'dist' => $dist,
2457 'version' => $vers,
2458 );
2460 $repo_files{ "${srvdir}/$file" } = \%fileinfo;
2461 }
2464 sub cleanup_and_extract {
2465 my $fileinfo = $repo_files{ $File::Find::name };
2467 if( defined $fileinfo ) {
2469 my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
2470 my $sql;
2471 my $package = $fileinfo->{ 'package' };
2472 my $newver = $fileinfo->{ 'version' };
2474 mkpath($dir);
2475 system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
2477 if( -f "$dir/DEBIAN/templates" ) {
2479 daemon_log("DEBUG: Found debconf templates in '$package' - $newver", 5);
2481 my $tmpl= "";
2482 {
2483 local $/=undef;
2484 open FILE, "$dir/DEBIAN/templates";
2485 $tmpl = &encode_base64(<FILE>);
2486 close FILE;
2487 }
2488 rmtree("$dir/DEBIAN/templates");
2490 $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
2491 push @packages_list_statements, $sql;
2492 }
2493 }
2495 return;
2496 }
2499 #==== MAIN = main ==============================================================
2500 # parse commandline options
2501 Getopt::Long::Configure( "bundling" );
2502 GetOptions("h|help" => \&usage,
2503 "c|config=s" => \$cfg_file,
2504 "f|foreground" => \$foreground,
2505 "v|verbose+" => \$verbose,
2506 "no-bus+" => \$no_bus,
2507 "no-arp+" => \$no_arp,
2508 );
2510 # read and set config parameters
2511 &check_cmdline_param ;
2512 &read_configfile;
2513 &check_pid;
2515 $SIG{CHLD} = 'IGNORE';
2517 # forward error messages to logfile
2518 if( ! $foreground ) {
2519 open( STDIN, '+>/dev/null' );
2520 open( STDOUT, '+>&STDIN' );
2521 open( STDERR, '+>&STDIN' );
2522 }
2524 # Just fork, if we are not in foreground mode
2525 if( ! $foreground ) {
2526 chdir '/' or die "Can't chdir to /: $!";
2527 $pid = fork;
2528 setsid or die "Can't start a new session: $!";
2529 umask 0;
2530 } else {
2531 $pid = $$;
2532 }
2534 # Do something useful - put our PID into the pid_file
2535 if( 0 != $pid ) {
2536 open( LOCK_FILE, ">$pid_file" );
2537 print LOCK_FILE "$pid\n";
2538 close( LOCK_FILE );
2539 if( !$foreground ) {
2540 exit( 0 )
2541 };
2542 }
2544 # parse head url and revision from svn
2545 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
2546 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
2547 $server_headURL = defined $1 ? $1 : 'unknown' ;
2548 $server_revision = defined $2 ? $2 : 'unknown' ;
2549 if ($server_headURL =~ /\/tag\// ||
2550 $server_headURL =~ /\/branches\// ) {
2551 $server_status = "stable";
2552 } else {
2553 $server_status = "developmental" ;
2554 }
2557 daemon_log(" ", 1);
2558 daemon_log("$0 started!", 1);
2559 daemon_log("status: $server_status", 1);
2560 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1);
2562 if ($no_bus > 0) {
2563 $bus_activ = "false"
2564 }
2566 # connect to gosa-si job queue
2567 $job_db = GOSA::DBsqlite->new($job_queue_file_name);
2568 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
2570 # connect to known_clients_db
2571 $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
2572 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
2574 # connect to known_server_db
2575 $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
2576 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
2578 # connect to login_usr_db
2579 $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
2580 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
2582 # connect to fai_server_db and fai_release_db
2583 unlink($fai_server_file_name);
2584 $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
2585 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
2587 unlink($fai_release_file_name);
2588 $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
2589 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
2591 # connect to packages_list_db
2592 #unlink($packages_list_file_name);
2593 unlink($packages_list_under_construction);
2594 $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
2595 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
2597 # connect to messaging_db
2598 $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
2599 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
2602 # create xml object used for en/decrypting
2603 $xml = new XML::Simple();
2605 # create socket for incoming xml messages
2607 POE::Component::Server::TCP->new(
2608 Port => $server_port,
2609 ClientInput => sub {
2610 my ($kernel, $input) = @_[KERNEL, ARG0];
2611 push(@tasks, $input);
2612 $kernel->yield("next_task");
2613 },
2614 InlineStates => {
2615 next_task => \&next_task,
2616 task_result => \&handle_task_result,
2617 task_done => \&handle_task_done,
2618 task_debug => \&handle_task_debug,
2619 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!" },
2620 }
2621 );
2623 daemon_log("start socket for incoming xml messages at port '$server_port' ", 1);
2625 # create session for repeatedly checking the job queue for jobs
2626 POE::Session->create(
2627 inline_states => {
2628 _start => \&_start,
2629 sig_handler => \&sig_handler,
2630 watch_for_new_messages => \&watch_for_new_messages,
2631 watch_for_delivery_messages => \&watch_for_delivery_messages,
2632 watch_for_done_messages => \&watch_for_done_messages,
2633 watch_for_new_jobs => \&watch_for_new_jobs,
2634 watch_for_done_jobs => \&watch_for_done_jobs,
2635 create_packages_list_db => \&run_create_packages_list_db,
2636 create_fai_server_db => \&run_create_fai_server_db,
2637 create_fai_release_db => \&run_create_fai_release_db,
2638 session_run_result => \&session_run_result,
2639 session_run_debug => \&session_run_debug,
2640 session_run_done => \&session_run_done,
2641 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!" },
2642 }
2643 );
2646 # import all modules
2647 &import_modules;
2649 # check wether all modules are gosa-si valid passwd check
2651 POE::Kernel->run();
2652 exit;