1 #!/usr/bin/perl
2 #===============================================================================
3 #
4 # FILE: gosa-sd
5 #
6 # USAGE: ./gosa-sd
7 #
8 # DESCRIPTION:
9 #
10 # OPTIONS: ---
11 # REQUIREMENTS: libconfig-inifiles-perl libcrypt-rijndael-perl libxml-simple-perl
12 # libdata-dumper-simple-perl libdbd-sqlite3-perl libnet-ldap-perl
13 # libpoe-perl
14 # BUGS: ---
15 # NOTES:
16 # AUTHOR: (Andreas Rettenberger), <rettenberger@gonicus.de>
17 # COMPANY:
18 # VERSION: 1.0
19 # CREATED: 12.09.2007 08:54:41 CEST
20 # REVISION: ---
21 #===============================================================================
23 use strict;
24 use warnings;
25 use Getopt::Long;
26 use Config::IniFiles;
27 use POSIX;
29 use Fcntl;
30 use IO::Socket::INET;
31 use IO::Handle;
32 use IO::Select;
33 use Symbol qw(qualify_to_ref);
34 use Crypt::Rijndael;
35 use MIME::Base64;
36 use Digest::MD5 qw(md5 md5_hex md5_base64);
37 use XML::Simple;
38 use Data::Dumper;
39 use Sys::Syslog qw( :DEFAULT setlogsock);
40 use Cwd;
41 use File::Spec;
42 use File::Basename;
43 use File::Path;
44 use GOSA::DBsqlite;
45 use GOSA::GosaSupportDaemon;
46 use POE qw(Component::Server::TCP Wheel::Run Filter::Reference);
47 use Net::LDAP;
48 use Net::LDAP::Util qw(:escape);
50 my $modules_path = "/usr/lib/gosa-si/modules";
51 use lib "/usr/lib/gosa-si/modules";
53 my (%cfg_defaults, $foreground, $verbose, $ping_timeout);
54 my ($bus_activ, $bus, $msg_to_bus, $bus_cipher);
55 my ($server, $server_mac_address);
56 my ($gosa_server, $job_queue_timeout, $job_queue_loop_delay);
57 my ($known_modules);
58 my ($pid_file, $procid, $pid, $log_file);
59 my ($arp_activ, $arp_fifo);
60 my ($xml);
61 my $sources_list;
62 my $max_clients;
63 # variables declared in config file are always set to 'our'
64 our (%cfg_defaults, $log_file, $pid_file,
65 $server_ip, $server_port, $SIPackages_key,
66 $arp_activ, $gosa_unit_tag,
67 $GosaPackages_key, $gosa_ip, $gosa_port, $gosa_timeout,
68 );
70 # additional variable which should be globaly accessable
71 our $server_address;
72 our $bus_address;
73 our $gosa_address;
74 our $no_bus;
75 our $no_arp;
76 our $verbose;
77 our $forground;
78 our $cfg_file;
79 our ($ldap_handle, $ldap_uri, $ldap_base, $ldap_admin_dn, $ldap_admin_password, $ldap_server_dn);
82 # specifies the verbosity of the daemon_log
83 $verbose = 0 ;
85 # if foreground is not null, script will be not forked to background
86 $foreground = 0 ;
88 # specifies the timeout seconds while checking the online status of a registrating client
89 $ping_timeout = 5;
91 $no_bus = 0;
92 $bus_activ = "true";
94 $no_arp = 0;
96 our $prg= basename($0);
98 # holds all gosa jobs
99 our $job_db;
100 our $job_queue_tn = 'jobs';
101 my $job_queue_file_name;
102 my @job_queue_col_names = ("id INTEGER",
103 "timestamp",
104 "status DEFAULT 'none'",
105 "result DEFAULT 'none'",
106 "progress DEFAULT 'none'",
107 "headertag DEFAULT 'none'",
108 "targettag DEFAULT 'none'",
109 "xmlmessage DEFAULT 'none'",
110 "macaddress DEFAULT 'none'",
111 );
113 # holds all other gosa-sd as well as the gosa-sd-bus
114 our $known_server_db;
115 our $known_server_tn = "known_server";
116 my $known_server_file_name;
117 my @known_server_col_names = ('hostname', 'status', 'hostkey', 'timestamp');
119 # holds all registrated clients
120 our $known_clients_db;
121 our $known_clients_tn = "known_clients";
122 my $known_clients_file_name;
123 my @known_clients_col_names = ('hostname', 'status', 'hostkey', 'timestamp', 'macaddress', 'events');
125 # holds all logged in user at each client
126 our $login_users_db;
127 our $login_users_tn = "login_users";
128 my $login_users_file_name;
129 my @login_users_col_names = ('client', 'user', 'timestamp');
131 # holds all fai server, the debian release and tag
132 our $fai_server_db;
133 our $fai_server_tn = "fai_server";
134 my $fai_server_file_name;
135 our @fai_server_col_names = ('timestamp', 'server', 'release', 'sections', 'tag');
136 our $fai_release_tn = "fai_release";
137 our @fai_release_col_names = ('timestamp', 'release', 'class', 'type', 'state');
139 # holds all packages available from different repositories
140 our $packages_list_db;
141 our $packages_list_tn = "packages_list";
142 my $packages_list_file_name;
143 our @packages_list_col_names = ('distribution', 'package', 'version', 'section', 'description', 'template', 'timestamp');
144 my $outdir = "/tmp/packages_list_db";
145 my $arch = "i386";
147 # holds all messages which should be delivered to a user
148 our $messaging_db;
149 our $messaging_tn = "messaging";
150 our @messaging_col_names = ('subject', 'from', 'to', 'flag', 'direction', 'delivery_time', 'message', 'timestamp', 'id INTEGER', );
151 my $messaging_file_name;
153 # path to directory to store client install log files
154 our $client_fai_log_dir = "/var/log/fai";
156 # queue which stores taskes until one of the $max_children children are ready to process the task
157 my @tasks = qw();
158 my $max_children = 2;
161 %cfg_defaults = (
162 "general" => {
163 "log-file" => [\$log_file, "/var/run/".$prg.".log"],
164 "pid-file" => [\$pid_file, "/var/run/".$prg.".pid"],
165 },
166 "bus" => {
167 "activ" => [\$bus_activ, "true"],
168 },
169 "server" => {
170 "port" => [\$server_port, "20081"],
171 "known-clients" => [\$known_clients_file_name, '/var/lib/gosa-si/clients.db' ],
172 "known-servers" => [\$known_server_file_name, '/var/lib/gosa-si/servers.db'],
173 "login-users" => [\$login_users_file_name, '/var/lib/gosa-si/users.db'],
174 "fai-server" => [\$fai_server_file_name, '/var/lib/gosa-si/fai.db'],
175 "packages-list" => [\$packages_list_file_name, '/var/lib/gosa-si/packages.db'],
176 "messaging" => [\$messaging_file_name, '/var/lib/gosa-si/messaging.db'],
177 "source-list" => [\$sources_list, '/etc/apt/sources.list'],
178 "ldap-uri" => [\$ldap_uri, ""],
179 "ldap-base" => [\$ldap_base, ""],
180 "ldap-admin-dn" => [\$ldap_admin_dn, ""],
181 "ldap-admin-password" => [\$ldap_admin_password, ""],
182 "gosa-unit-tag" => [\$gosa_unit_tag, ""],
183 "max-clients" => [\$max_clients, 10],
184 },
185 "GOsaPackages" => {
186 "ip" => [\$gosa_ip, "0.0.0.0"],
187 "port" => [\$gosa_port, "20082"],
188 "job-queue" => [\$job_queue_file_name, '/var/lib/gosa-si/jobs.db'],
189 "job-queue-loop-delay" => [\$job_queue_loop_delay, 3],
190 "key" => [\$GosaPackages_key, "none"],
191 },
192 "SIPackages" => {
193 "key" => [\$SIPackages_key, "none"],
194 },
195 );
198 #=== FUNCTION ================================================================
199 # NAME: usage
200 # PARAMETERS: nothing
201 # RETURNS: nothing
202 # DESCRIPTION: print out usage text to STDERR
203 #===============================================================================
204 sub usage {
205 print STDERR << "EOF" ;
206 usage: $prg [-hvf] [-c config]
208 -h : this (help) message
209 -c <file> : config file
210 -f : foreground, process will not be forked to background
211 -v : be verbose (multiple to increase verbosity)
212 -no-bus : starts $prg without connection to bus
213 -no-arp : starts $prg without connection to arp module
215 EOF
216 print "\n" ;
217 }
220 #=== FUNCTION ================================================================
221 # NAME: read_configfile
222 # PARAMETERS: cfg_file - string -
223 # RETURNS: nothing
224 # DESCRIPTION: read cfg_file and set variables
225 #===============================================================================
226 sub read_configfile {
227 my $cfg;
228 if( defined( $cfg_file) && ( length($cfg_file) > 0 )) {
229 if( -r $cfg_file ) {
230 $cfg = Config::IniFiles->new( -file => $cfg_file );
231 } else {
232 print STDERR "Couldn't read config file!\n";
233 }
234 } else {
235 $cfg = Config::IniFiles->new() ;
236 }
237 foreach my $section (keys %cfg_defaults) {
238 foreach my $param (keys %{$cfg_defaults{ $section }}) {
239 my $pinfo = $cfg_defaults{ $section }{ $param };
240 ${@$pinfo[ 0 ]} = $cfg->val( $section, $param, @$pinfo[ 1 ] );
241 }
242 }
243 }
246 #=== FUNCTION ================================================================
247 # NAME: logging
248 # PARAMETERS: level - string - default 'info'
249 # msg - string -
250 # facility - string - default 'LOG_DAEMON'
251 # RETURNS: nothing
252 # DESCRIPTION: function for logging
253 #===============================================================================
254 sub daemon_log {
255 # log into log_file
256 my( $msg, $level ) = @_;
257 if(not defined $msg) { return }
258 if(not defined $level) { $level = 1 }
259 if(defined $log_file){
260 open(LOG_HANDLE, ">>$log_file");
261 if(not defined open( LOG_HANDLE, ">>$log_file" )) {
262 print STDERR "cannot open $log_file: $!";
263 return }
264 chomp($msg);
265 if($level <= $verbose){
266 my ($seconds, $minutes, $hours, $monthday, $month,
267 $year, $weekday, $yearday, $sommertime) = localtime(time);
268 $hours = $hours < 10 ? $hours = "0".$hours : $hours;
269 $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
270 $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
271 my @monthnames = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
272 $month = $monthnames[$month];
273 $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
274 $year+=1900;
275 my $name = $prg;
277 my $log_msg = "$month $monthday $hours:$minutes:$seconds $name $msg\n";
278 print LOG_HANDLE $log_msg;
279 if( $foreground ) {
280 print STDERR $log_msg;
281 }
282 }
283 close( LOG_HANDLE );
284 }
285 }
288 #=== FUNCTION ================================================================
289 # NAME: check_cmdline_param
290 # PARAMETERS: nothing
291 # RETURNS: nothing
292 # DESCRIPTION: validates commandline parameter
293 #===============================================================================
294 sub check_cmdline_param () {
295 my $err_config;
296 my $err_counter = 0;
297 if(not defined($cfg_file)) {
298 $cfg_file = "/etc/gosa-si/server.conf";
299 if(! -r $cfg_file) {
300 $err_config = "please specify a config file";
301 $err_counter += 1;
302 }
303 }
304 if( $err_counter > 0 ) {
305 &usage( "", 1 );
306 if( defined( $err_config)) { print STDERR "$err_config\n"}
307 print STDERR "\n";
308 exit( -1 );
309 }
310 }
313 #=== FUNCTION ================================================================
314 # NAME: check_pid
315 # PARAMETERS: nothing
316 # RETURNS: nothing
317 # DESCRIPTION: handels pid processing
318 #===============================================================================
319 sub check_pid {
320 $pid = -1;
321 # Check, if we are already running
322 if( open(LOCK_FILE, "<$pid_file") ) {
323 $pid = <LOCK_FILE>;
324 if( defined $pid ) {
325 chomp( $pid );
326 if( -f "/proc/$pid/stat" ) {
327 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
328 if( $stat ) {
329 daemon_log("ERROR: Already running",1);
330 close( LOCK_FILE );
331 exit -1;
332 }
333 }
334 }
335 close( LOCK_FILE );
336 unlink( $pid_file );
337 }
339 # create a syslog msg if it is not to possible to open PID file
340 if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
341 my($msg) = "Couldn't obtain lockfile '$pid_file' ";
342 if (open(LOCK_FILE, '<', $pid_file)
343 && ($pid = <LOCK_FILE>))
344 {
345 chomp($pid);
346 $msg .= "(PID $pid)\n";
347 } else {
348 $msg .= "(unable to read PID)\n";
349 }
350 if( ! ($foreground) ) {
351 openlog( $0, "cons,pid", "daemon" );
352 syslog( "warning", $msg );
353 closelog();
354 }
355 else {
356 print( STDERR " $msg " );
357 }
358 exit( -1 );
359 }
360 }
362 #=== FUNCTION ================================================================
363 # NAME: import_modules
364 # PARAMETERS: module_path - string - abs. path to the directory the modules
365 # are stored
366 # RETURNS: nothing
367 # DESCRIPTION: each file in module_path which ends with '.pm' and activation
368 # state is on is imported by "require 'file';"
369 #===============================================================================
370 sub import_modules {
371 daemon_log(" ", 1);
373 if (not -e $modules_path) {
374 daemon_log("ERROR: cannot find directory or directory is not readable: $modules_path", 1);
375 }
377 opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
378 while (defined (my $file = readdir (DIR))) {
379 if (not $file =~ /(\S*?).pm$/) {
380 next;
381 }
382 my $mod_name = $1;
384 if( $file =~ /ArpHandler.pm/ ) {
385 if( $no_arp > 0 ) {
386 next;
387 }
388 }
390 eval { require $file; };
391 if ($@) {
392 daemon_log("ERROR: gosa-si-server could not load module $file", 1);
393 daemon_log("$@", 5);
394 } else {
395 my $info = eval($mod_name.'::get_module_info()');
396 # Only load module if get_module_info() returns a non-null object
397 if( $info ) {
398 my ($input_address, $input_key, $input, $input_active, $input_type) = @{$info};
399 $known_modules->{$mod_name} = $info;
400 daemon_log("INFO: module $mod_name loaded", 5);
401 }
402 }
403 }
404 close (DIR);
405 }
408 #=== FUNCTION ================================================================
409 # NAME: sig_int_handler
410 # PARAMETERS: signal - string - signal arose from system
411 # RETURNS: noting
412 # DESCRIPTION: handels tasks to be done befor signal becomes active
413 #===============================================================================
414 sub sig_int_handler {
415 my ($signal) = @_;
417 if(defined($ldap_handle)) {
418 $ldap_handle->disconnect;
419 }
421 daemon_log("shutting down gosa-si-server", 1);
422 system("killall gosa-si-server");
423 }
424 $SIG{INT} = \&sig_int_handler;
427 sub check_key_and_xml_validity {
428 my ($crypted_msg, $module_key, $session_id) = @_;
429 my $msg;
430 my $msg_hash;
431 my $error_string;
432 eval{
433 $msg = &decrypt_msg($crypted_msg, $module_key);
435 if ($msg =~ /<xml>/i){
436 $msg =~ s/\s+/ /g; # just for better daemon_log
437 daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 8);
438 $msg_hash = $xml->XMLin($msg, ForceArray=>1);
440 ##############
441 # check header
442 if( not exists $msg_hash->{'header'} ) { die "no header specified"; }
443 my $header_l = $msg_hash->{'header'};
444 if( 1 > @{$header_l} ) { die 'empty header tag'; }
445 if( 1 < @{$header_l} ) { die 'more than one header specified'; }
446 my $header = @{$header_l}[0];
447 if( 0 == length $header) { die 'empty string in header tag'; }
449 ##############
450 # check source
451 if( not exists $msg_hash->{'source'} ) { die "no source specified"; }
452 my $source_l = $msg_hash->{'source'};
453 if( 1 > @{$source_l} ) { die 'empty source tag'; }
454 if( 1 < @{$source_l} ) { die 'more than one source specified'; }
455 my $source = @{$source_l}[0];
456 if( 0 == length $source) { die 'source error'; }
458 ##############
459 # check target
460 if( not exists $msg_hash->{'target'} ) { die "no target specified"; }
461 my $target_l = $msg_hash->{'target'};
462 if( 1 > @{$target_l} ) { die 'empty target tag'; }
463 }
464 };
465 if($@) {
466 daemon_log("$session_id DEBUG: do not understand the message: $@", 7);
467 $msg = undef;
468 $msg_hash = undef;
469 }
471 return ($msg, $msg_hash);
472 }
475 sub check_outgoing_xml_validity {
476 my ($msg) = @_;
478 my $msg_hash;
479 eval{
480 $msg_hash = $xml->XMLin($msg, ForceArray=>1);
482 ##############
483 # check header
484 my $header_l = $msg_hash->{'header'};
485 if( 1 != @{$header_l} ) {
486 die 'no or more than one headers specified';
487 }
488 my $header = @{$header_l}[0];
489 if( 0 == length $header) {
490 die 'header has length 0';
491 }
493 ##############
494 # check source
495 my $source_l = $msg_hash->{'source'};
496 if( 1 != @{$source_l} ) {
497 die 'no or more than 1 sources specified';
498 }
499 my $source = @{$source_l}[0];
500 if( 0 == length $source) {
501 die 'source has length 0';
502 }
503 unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
504 $source =~ /^GOSA$/i ) {
505 die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
506 }
508 ##############
509 # check target
510 my $target_l = $msg_hash->{'target'};
511 if( 0 == @{$target_l} ) {
512 die "no targets specified";
513 }
514 foreach my $target (@$target_l) {
515 if( 0 == length $target) {
516 die "target has length 0";
517 }
518 unless( $target =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
519 $target =~ /^GOSA$/i ||
520 $target =~ /^\*$/ ||
521 $target =~ /KNOWN_SERVER/i ||
522 $target =~ /JOBDB/i ||
523 $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 ){
524 die "target '$target' is not a complete ip-address with port or a valid target name or a mac-address";
525 }
526 }
527 };
528 if($@) {
529 daemon_log("WARNING: outgoing msg is not gosa-si envelope conform", 5);
530 daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 8);
531 $msg_hash = undef;
532 }
534 return ($msg_hash);
535 }
538 sub input_from_known_server {
539 my ($input, $remote_ip, $session_id) = @_ ;
540 my ($msg, $msg_hash, $module);
542 my $sql_statement= "SELECT * FROM known_server";
543 my $query_res = $known_server_db->select_dbentry( $sql_statement );
545 while( my ($hit_num, $hit) = each %{ $query_res } ) {
546 my $host_name = $hit->{hostname};
547 if( not $host_name =~ "^$remote_ip") {
548 next;
549 }
550 my $host_key = $hit->{hostkey};
551 daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 7);
552 daemon_log("DEBUG: input_from_known_server: host_key: $host_key", 7);
554 # check if module can open msg envelope with module key
555 my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
556 if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
557 daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 7);
558 daemon_log("$@", 8);
559 next;
560 }
561 else {
562 $msg = $tmp_msg;
563 $msg_hash = $tmp_msg_hash;
564 $module = "SIPackages";
565 last;
566 }
567 }
569 if( (!$msg) || (!$msg_hash) || (!$module) ) {
570 daemon_log("$session_id DEBUG: Incoming message is not from a known server", 7);
571 }
573 return ($msg, $msg_hash, $module);
574 }
577 sub input_from_known_client {
578 my ($input, $remote_ip, $session_id) = @_ ;
579 my ($msg, $msg_hash, $module);
581 my $sql_statement= "SELECT * FROM known_clients";
582 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
583 while( my ($hit_num, $hit) = each %{ $query_res } ) {
584 my $host_name = $hit->{hostname};
585 if( not $host_name =~ /^$remote_ip:\d*$/) {
586 next;
587 }
588 my $host_key = $hit->{hostkey};
589 &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 7);
590 &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 7);
592 # check if module can open msg envelope with module key
593 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
595 if( (!$msg) || (!$msg_hash) ) {
596 &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 7);
597 &daemon_log("$@", 8);
598 next;
599 }
600 else {
601 $module = "SIPackages";
602 last;
603 }
604 }
606 if( (!$msg) || (!$msg_hash) || (!$module) ) {
607 &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 7);
608 }
610 return ($msg, $msg_hash, $module);
611 }
614 sub input_from_unknown_host {
615 no strict "refs";
616 my ($input, $session_id) = @_ ;
617 my ($msg, $msg_hash, $module);
618 my $error_string;
620 my %act_modules = %$known_modules;
622 while( my ($mod, $info) = each(%act_modules)) {
624 # check a key exists for this module
625 my $module_key = ${$mod."_key"};
626 if( not defined $module_key ) {
627 if( $mod eq 'ArpHandler' ) {
628 next;
629 }
630 daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
631 next;
632 }
633 daemon_log("$session_id DEBUG: $mod: $module_key", 7);
635 # check if module can open msg envelope with module key
636 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
637 if( (not defined $msg) || (not defined $msg_hash) ) {
638 next;
639 }
640 else {
641 $module = $mod;
642 last;
643 }
644 }
646 if( (!$msg) || (!$msg_hash) || (!$module)) {
647 daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 7);
648 }
650 return ($msg, $msg_hash, $module);
651 }
654 sub create_ciphering {
655 my ($passwd) = @_;
656 if((!defined($passwd)) || length($passwd)==0) {
657 $passwd = "";
658 }
659 $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
660 my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
661 my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
662 $my_cipher->set_iv($iv);
663 return $my_cipher;
664 }
667 sub encrypt_msg {
668 my ($msg, $key) = @_;
669 my $my_cipher = &create_ciphering($key);
670 my $len;
671 {
672 use bytes;
673 $len= 16-length($msg)%16;
674 }
675 $msg = "\0"x($len).$msg;
676 $msg = $my_cipher->encrypt($msg);
677 chomp($msg = &encode_base64($msg));
678 # there are no newlines allowed inside msg
679 $msg=~ s/\n//g;
680 return $msg;
681 }
684 sub decrypt_msg {
686 my ($msg, $key) = @_ ;
687 $msg = &decode_base64($msg);
688 my $my_cipher = &create_ciphering($key);
689 $msg = $my_cipher->decrypt($msg);
690 $msg =~ s/\0*//g;
691 return $msg;
692 }
695 sub get_encrypt_key {
696 my ($target) = @_ ;
697 my $encrypt_key;
698 my $error = 0;
700 # target can be in known_server
701 if( not defined $encrypt_key ) {
702 my $sql_statement= "SELECT * FROM known_server WHERE hostname='$target'";
703 my $query_res = $known_server_db->select_dbentry( $sql_statement );
704 while( my ($hit_num, $hit) = each %{ $query_res } ) {
705 my $host_name = $hit->{hostname};
706 if( $host_name ne $target ) {
707 next;
708 }
709 $encrypt_key = $hit->{hostkey};
710 last;
711 }
712 }
714 # target can be in known_client
715 if( not defined $encrypt_key ) {
716 my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$target'";
717 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
718 while( my ($hit_num, $hit) = each %{ $query_res } ) {
719 my $host_name = $hit->{hostname};
720 if( $host_name ne $target ) {
721 next;
722 }
723 $encrypt_key = $hit->{hostkey};
724 last;
725 }
726 }
728 return $encrypt_key;
729 }
732 #=== FUNCTION ================================================================
733 # NAME: open_socket
734 # PARAMETERS: PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
735 # [PeerPort] string necessary if port not appended by PeerAddr
736 # RETURNS: socket IO::Socket::INET
737 # DESCRIPTION: open a socket to PeerAddr
738 #===============================================================================
739 sub open_socket {
740 my ($PeerAddr, $PeerPort) = @_ ;
741 if(defined($PeerPort)){
742 $PeerAddr = $PeerAddr.":".$PeerPort;
743 }
744 my $socket;
745 $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
746 Porto => "tcp",
747 Type => SOCK_STREAM,
748 Timeout => 5,
749 );
750 if(not defined $socket) {
751 return;
752 }
753 # &daemon_log("DEBUG: open_socket: $PeerAddr", 7);
754 return $socket;
755 }
758 #=== FUNCTION ================================================================
759 # NAME: get_ip
760 # PARAMETERS: interface name (i.e. eth0)
761 # RETURNS: (ip address)
762 # DESCRIPTION: Uses ioctl to get ip address directly from system.
763 #===============================================================================
764 sub get_ip {
765 my $ifreq= shift;
766 my $result= "";
767 my $SIOCGIFADDR= 0x8915; # man 2 ioctl_list
768 my $proto= getprotobyname('ip');
770 socket SOCKET, PF_INET, SOCK_DGRAM, $proto
771 or die "socket: $!";
773 if(ioctl SOCKET, $SIOCGIFADDR, $ifreq) {
774 my ($if, $sin) = unpack 'a16 a16', $ifreq;
775 my ($port, $addr) = sockaddr_in $sin;
776 my $ip = inet_ntoa $addr;
778 if ($ip && length($ip) > 0) {
779 $result = $ip;
780 }
781 }
783 return $result;
784 }
787 sub get_local_ip_for_remote_ip {
788 my $remote_ip= shift;
789 my $result="0.0.0.0";
791 if($remote_ip =~ /^(\d\d?\d?\.){3}\d\d?\d?$/) {
792 if($remote_ip eq "127.0.0.1") {
793 $result = "127.0.0.1";
794 } else {
795 my $PROC_NET_ROUTE= ('/proc/net/route');
797 open(PROC_NET_ROUTE, "<$PROC_NET_ROUTE")
798 or die "Could not open $PROC_NET_ROUTE";
800 my @ifs = <PROC_NET_ROUTE>;
802 close(PROC_NET_ROUTE);
804 # Eat header line
805 shift @ifs;
806 chomp @ifs;
807 foreach my $line(@ifs) {
808 my ($Iface,$Destination,$Gateway,$Flags,$RefCnt,$Use,$Metric,$Mask,$MTU,$Window,$IRTT)=split(/\s/, $line);
809 my $destination;
810 my $mask;
811 my ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Destination);
812 $destination= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
813 ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Mask);
814 $mask= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
815 if(new NetAddr::IP($remote_ip)->within(new NetAddr::IP($destination, $mask))) {
816 # destination matches route, save mac and exit
817 $result= &get_ip($Iface);
818 last;
819 }
820 }
821 }
822 } else {
823 daemon_log("get_local_ip_for_remote_ip was called with a non-ip parameter: $remote_ip", 1);
824 }
825 return $result;
826 }
829 sub send_msg_to_target {
830 my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
831 my $error = 0;
832 my $header;
833 my $new_status;
834 my $act_status;
835 my ($sql_statement, $res);
837 if( $msg_header ) {
838 $header = "'$msg_header'-";
839 } else {
840 $header = "";
841 }
843 # Patch the source ip
844 if($msg =~ /<source>0\.0\.0\.0:\d*?<\/source>/) {
845 my $remote_ip = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
846 $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$remote_ip:$2<\/source>/s;
847 }
849 # encrypt xml msg
850 my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
852 # opensocket
853 my $socket = &open_socket($address);
854 if( !$socket ) {
855 daemon_log("$session_id ERROR: cannot send ".$header."msg to $address , host not reachable", 1);
856 $error++;
857 }
859 if( $error == 0 ) {
860 # send xml msg
861 print $socket $crypted_msg."\n";
863 daemon_log("$session_id INFO: send ".$header."msg to $address", 5);
864 #daemon_log("DEBUG: message:\n$msg", 9);
866 }
868 # close socket in any case
869 if( $socket ) {
870 close $socket;
871 }
873 if( $error > 0 ) { $new_status = "down"; }
874 else { $new_status = $msg_header; }
877 # known_clients
878 $sql_statement = "SELECT * FROM known_clients WHERE hostname='$address'";
879 $res = $known_clients_db->select_dbentry($sql_statement);
880 if( keys(%$res) > 0) {
881 $act_status = $res->{1}->{'status'};
882 if( $act_status eq "down" ) {
883 $sql_statement = "DELETE FROM known_clients WHERE hostname='$address'";
884 $res = $known_clients_db->del_dbentry($sql_statement);
885 daemon_log("$session_id WARNING: failed 2x to send msg to host '$address', delete host from known_clients", 3);
886 } else {
887 $sql_statement = "UPDATE known_clients SET status='$new_status' WHERE hostname='$address'";
888 $res = $known_clients_db->update_dbentry($sql_statement);
889 if($new_status eq "down"){
890 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
891 } else {
892 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
893 }
894 }
895 }
897 # known_server
898 $sql_statement = "SELECT * FROM known_server WHERE hostname='$address'";
899 $res = $known_server_db->select_dbentry($sql_statement);
900 if( keys(%$res) > 0 ) {
901 $act_status = $res->{1}->{'status'};
902 if( $act_status eq "down" ) {
903 $sql_statement = "DELETE FROM known_server WHERE hostname='$address'";
904 $res = $known_server_db->del_dbentry($sql_statement);
905 daemon_log("$session_id WARNING: failed 2x to a send msg to host '$address', delete host from known_server", 3);
906 }
907 else {
908 $sql_statement = "UPDATE known_server SET status='$new_status' WHERE hostname='$address'";
909 $res = $known_server_db->update_dbentry($sql_statement);
910 if($new_status eq "down"){
911 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
912 }
913 else {
914 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
915 }
916 }
917 }
918 return $error;
919 }
922 sub update_jobdb_status_for_send_msgs {
923 my ($answer, $error) = @_;
924 if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
925 my $jobdb_id = $1;
927 # sending msg faild
928 if( $error ) {
929 if (not $answer =~ /<header>trigger_action_reinstall<\/header>/) {
930 my $sql_statement = "UPDATE $job_queue_tn ".
931 "SET status='error', result='can not deliver msg, please consult log file' ".
932 "WHERE id='$jobdb_id'";
933 my $res = $job_db->update_dbentry($sql_statement);
934 }
936 # sending msg was successful
937 } else {
938 my $sql_statement = "UPDATE $job_queue_tn ".
939 "SET status='done' ".
940 "WHERE id='$jobdb_id' AND status='processed'";
941 my $res = $job_db->update_dbentry($sql_statement);
942 }
943 }
944 }
946 sub _start {
947 my ($kernel) = $_[KERNEL];
948 &trigger_db_loop($kernel);
949 #$kernel->yield('create_fai_server_db', $fai_server_tn );
950 #$kernel->yield('create_fai_release_db', $fai_release_tn );
951 $kernel->sig(USR1 => "sig_handler");
952 }
954 sub sig_handler {
955 my ($kernel, $signal) = @_[KERNEL, ARG0] ;
956 daemon_log("0 INFO got signal '$signal'", 1);
957 $kernel->sig_handled();
958 return;
959 }
961 sub next_task {
962 my ($session, $heap) = @_[SESSION, HEAP];
964 while ( keys( %{ $heap->{task} } ) < $max_children ) {
965 my $next_task = shift @tasks;
966 last unless defined $next_task;
968 my $task = POE::Wheel::Run->new(
969 Program => sub { process_task($session, $heap, $next_task) },
970 StdioFilter => POE::Filter::Reference->new(),
971 StdoutEvent => "task_result",
972 StderrEvent => "task_debug",
973 CloseEvent => "task_done",
974 );
976 $heap->{task}->{ $task->ID } = $task;
977 }
978 }
980 sub handle_task_result {
981 my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
982 my $client_answer = $result->{'answer'};
983 if( $client_answer =~ s/session_id=(\d+)$// ) {
984 my $session_id = $1;
985 if( defined $session_id ) {
986 my $session_reference = $kernel->ID_id_to_session($session_id);
987 if( defined $session_reference ) {
988 $heap = $session_reference->get_heap();
989 }
990 }
992 if(exists $heap->{'client'}) {
993 $heap->{'client'}->put($client_answer);
994 }
995 }
996 $kernel->sig(CHLD => "child_reap");
997 }
999 sub handle_task_debug {
1000 my $result = $_[ARG0];
1001 print STDERR "$result\n";
1002 }
1004 sub handle_task_done {
1005 my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1006 delete $heap->{task}->{$task_id};
1007 $kernel->yield("next_task");
1008 }
1010 sub process_task {
1011 no strict "refs";
1012 my ($session, $heap, $input) = @_;
1013 my $session_id = $session->ID;
1014 my ($msg, $msg_hash, $module);
1015 my $error = 0;
1016 my $answer_l;
1017 my ($answer_header, @answer_target_l, $answer_source);
1018 my $client_answer = "";
1020 daemon_log("", 5);
1021 daemon_log("$session_id INFO: Incoming msg with session ID $session_id from '".$heap->{'remote_ip'}."'", 5);
1022 daemon_log("$session_id DEBUG: Incoming msg:\n$input", 9);
1024 ####################
1025 # check incoming msg
1026 # msg is from a new client or gosa
1027 ($msg, $msg_hash, $module) = &input_from_unknown_host($input, $session_id);
1028 # msg is from a gosa-si-server or gosa-si-bus
1029 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1030 ($msg, $msg_hash, $module) = &input_from_known_server($input, $heap->{'remote_ip'}, $session_id);
1031 }
1032 # msg is from a gosa-si-client
1033 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1034 ($msg, $msg_hash, $module) = &input_from_known_client($input, $heap->{'remote_ip'}, $session_id);
1035 }
1036 # an error occurred
1037 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1038 # if an incoming msg could not be decrypted (maybe a wrong key), send client a ping. If the client
1039 # could not understand a msg from its server the client cause a re-registering process
1040 my $sql_statement = "SELECT * FROM $main::known_clients_tn WHERE (hostname LIKE '".$heap->{'remote_ip'}."%')";
1041 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1042 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1043 my $host_name = $hit->{'hostname'};
1044 my $host_key = $hit->{'hostkey'};
1045 my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source> <target>$host_name</target></xml>";
1046 my $error = &send_msg_to_target($ping_msg, $host_name, $host_key, "gosa_ping", $session_id);
1047 &update_jobdb_status_for_send_msgs($ping_msg, $error);
1048 }
1049 $error++;
1050 }
1052 ######################
1053 # process incoming msg
1054 if( $error == 0) {
1055 daemon_log("$session_id INFO: Incoming msg with header '".@{$msg_hash->{'header'}}[0]."'", 5);
1056 daemon_log("$session_id DEBUG: Processing module ".$module, 7);
1057 $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1059 if ( 0 < @{$answer_l} ) {
1060 my $answer_str = join("\n", @{$answer_l});
1061 daemon_log("$session_id DEBUG: $module: Got answer from module: \n".$answer_str,8);
1062 }
1063 }
1064 if( !$answer_l ) { $error++ };
1066 ########
1067 # answer
1068 if( $error == 0 ) {
1070 foreach my $answer ( @{$answer_l} ) {
1071 # for each answer in answer list
1073 # check outgoing msg to xml validity
1074 my $answer_hash = &check_outgoing_xml_validity($answer);
1075 if( not defined $answer_hash ) {
1076 next;
1077 }
1079 $answer_header = @{$answer_hash->{'header'}}[0];
1080 @answer_target_l = @{$answer_hash->{'target'}};
1081 $answer_source = @{$answer_hash->{'source'}}[0];
1083 # deliver msg to all targets
1084 foreach my $answer_target ( @answer_target_l ) {
1086 # targets of msg are all gosa-si-clients in known_clients_db
1087 if( $answer_target eq "*" ) {
1088 # answer is for all clients
1089 my $sql_statement= "SELECT * FROM known_clients";
1090 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1091 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1092 my $host_name = $hit->{hostname};
1093 my $host_key = $hit->{hostkey};
1094 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1095 &update_jobdb_status_for_send_msgs($answer, $error);
1096 }
1097 }
1099 # targets of msg are all gosa-si-server in known_server_db
1100 elsif( $answer_target eq "KNOWN_SERVER" ) {
1101 # answer is for all server in known_server
1102 my $sql_statement= "SELECT * FROM known_server";
1103 my $query_res = $known_server_db->select_dbentry( $sql_statement );
1104 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1105 my $host_name = $hit->{hostname};
1106 my $host_key = $hit->{hostkey};
1107 $answer =~ s/KNOWN_SERVER/$host_name/g;
1108 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1109 &update_jobdb_status_for_send_msgs($answer, $error);
1110 }
1111 }
1113 # target of msg is GOsa
1114 elsif( $answer_target eq "GOSA" ) {
1115 my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1116 my $add_on = "";
1117 if( defined $session_id ) {
1118 $add_on = ".session_id=$session_id";
1119 }
1120 # answer is for GOSA and has to returned to connected client
1121 my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1122 $client_answer = $gosa_answer.$add_on;
1123 }
1125 # target of msg is job queue at this host
1126 elsif( $answer_target eq "JOBDB") {
1127 $answer =~ /<header>(\S+)<\/header>/;
1128 my $header;
1129 if( defined $1 ) { $header = $1; }
1130 my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1131 &update_jobdb_status_for_send_msgs($answer, $error);
1132 }
1134 # target of msg is a mac address
1135 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 ) {
1136 daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients", 5);
1137 my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1138 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1139 my $found_ip_flag = 0;
1140 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1141 my $host_name = $hit->{hostname};
1142 my $host_key = $hit->{hostkey};
1143 $answer =~ s/$answer_target/$host_name/g;
1144 daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1145 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1146 &update_jobdb_status_for_send_msgs($answer, $error);
1147 $found_ip_flag++ ;
1148 }
1149 if( $found_ip_flag == 0) {
1150 daemon_log("$session_id WARNING: no host found in known_clients with mac address '$answer_target'", 3);
1151 if( $bus_activ eq "true" ) {
1152 daemon_log("$session_id INFO: try to forward msg '$answer_header' to bus '$bus_address'", 5);
1153 my $sql_statement = "SELECT * FROM known_server WHERE hostname='$bus_address'";
1154 my $query_res = $known_server_db->select_dbentry( $sql_statement );
1155 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1156 my $bus_address = $hit->{hostname};
1157 my $bus_key = $hit->{hostkey};
1158 my $error = &send_msg_to_target($answer, $bus_address, $bus_key, $answer_header, $session_id);
1159 &update_jobdb_status_for_send_msgs($answer, $error);
1160 last;
1161 }
1162 }
1164 }
1166 # answer is for one specific host
1167 } else {
1168 # get encrypt_key
1169 my $encrypt_key = &get_encrypt_key($answer_target);
1170 if( not defined $encrypt_key ) {
1171 # unknown target, forward msg to bus
1172 daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1173 if( $bus_activ eq "true" ) {
1174 daemon_log("$session_id INFO: try to forward msg '$answer_header' to bus '$bus_address'", 5);
1175 my $sql_statement = "SELECT * FROM known_server WHERE hostname='$bus_address'";
1176 my $query_res = $known_server_db->select_dbentry( $sql_statement );
1177 my $res_length = keys( %{$query_res} );
1178 if( $res_length == 0 ){
1179 daemon_log("$session_id WARNING: send '$answer_header' to '$bus_address' failed, ".
1180 "no bus found in known_server", 3);
1181 }
1182 else {
1183 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1184 my $bus_key = $hit->{hostkey};
1185 my $error = &send_msg_to_target($answer, $bus_address, $bus_key, $answer_header,$session_id );
1186 &update_jobdb_status_for_send_msgs($answer, $error);
1187 }
1188 }
1189 }
1190 next;
1191 }
1192 my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1193 &update_jobdb_status_for_send_msgs($answer, $error);
1194 }
1195 }
1196 }
1197 }
1199 my $filter = POE::Filter::Reference->new();
1200 my %result = (
1201 status => "seems ok to me",
1202 answer => $client_answer,
1203 );
1205 my $output = $filter->put( [ \%result ] );
1206 print @$output;
1209 }
1212 sub trigger_db_loop {
1213 my ($kernel) = @_ ;
1214 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1215 $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay);
1216 }
1218 sub watch_for_done_jobs {
1219 my ($kernel,$heap) = @_[KERNEL, HEAP];
1221 my $sql_statement = "SELECT * FROM ".$job_queue_tn.
1222 " WHERE status='done'";
1223 my $res = $job_db->select_dbentry( $sql_statement );
1225 while( my ($id, $hit) = each %{$res} ) {
1226 my $jobdb_id = $hit->{id};
1227 my $sql_statement = "DELETE FROM $job_queue_tn WHERE id='$jobdb_id'";
1228 my $res = $job_db->del_dbentry($sql_statement);
1229 }
1231 $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1232 }
1234 sub watch_for_new_jobs {
1235 my ($kernel,$heap) = @_[KERNEL, HEAP];
1237 # check gosa job queue for jobs with executable timestamp
1238 my $timestamp = &get_time();
1239 my $sql_statement = "SELECT * FROM ".$job_queue_tn.
1240 " WHERE status='waiting' AND timestamp<'$timestamp'";
1241 my $res = $job_db->select_dbentry( $sql_statement );
1243 while( my ($id, $hit) = each %{$res} ) {
1244 my $jobdb_id = $hit->{id};
1245 my $macaddress = $hit->{'macaddress'};
1246 my $job_msg = $hit->{'xmlmessage'};
1247 daemon_log("J DEBUG: its time to execute $job_msg", 7);
1248 my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1249 my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1250 # expect macaddress is unique!!!!!!
1251 my $target = $res_hash->{1}->{hostname};
1253 # if (not defined $target) {
1254 # &daemon_log("ERROR: no host found for mac address: $macaddress", 1);
1255 # &daemon_log("$hit->{xmlmessage}", 8);
1256 # my $sql_statement = "UPDATE $job_queue_tn ".
1257 # "SET status='error', result='no host found for mac address' ".
1258 # "WHERE id='$jobdb_id'";
1259 # my $res = $job_db->update_dbentry($sql_statement);
1260 # next;
1261 # }
1262 #
1263 # change header
1264 $job_msg =~ s/<header>job_/<header>gosa_/;
1266 # add sqlite_id
1267 $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1269 $job_msg =~ /<header>(\S+)<\/header>/;
1270 my $header = $1 ;
1271 my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");
1273 # update status in job queue to 'processing'
1274 $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id='$jobdb_id'";
1275 my $res = $job_db->update_dbentry($sql_statement);
1276 }
1278 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1279 }
1282 sub refresh_ldap_handle {
1283 my $mesg;
1285 # Get an ldap handle, if we don't have one
1286 if( ! defined $ldap_handle ){
1287 $ldap_handle = Net::LDAP->new( $ldap_uri );
1288 }
1289 # Still not defined?
1290 if( ! defined $ldap_handle ) {
1291 daemon_log( "ch $$: Net::LDAP constructor failed: $!\n" );
1292 return 0;
1293 }
1295 # Bind to ldap server - eventually authenticate
1296 if( defined $ldap_admin_dn ) {
1297 if( defined $ldap_admin_password ) {
1298 $mesg = $ldap_handle->bind( $ldap_admin_dn, password => $ldap_admin_password );
1299 } else {
1300 $mesg = $ldap_handle->bind( $ldap_admin_dn );
1301 }
1302 } else {
1303 $mesg = $ldap_handle->bind();
1304 }
1306 if( 0 != $mesg->code ) {
1307 undef( $ldap_handle ) if( 81 == $mesg->code );
1308 daemon_log( "ch $$: LDAP bind: error (". $mesg->code . ') - ' . $mesg->error . "\n", 1);
1309 return 0;
1310 }
1312 return 1;
1313 }
1316 sub change_fai_state {
1317 my ($st, $targets, $session_id) = @_;
1318 $session_id = 0 if not defined $session_id;
1319 # Set FAI state to localboot
1320 my %mapActions= (
1321 reboot => '',
1322 update => 'softupdate',
1323 localboot => 'localboot',
1324 reinstall => 'install',
1325 rescan => '',
1326 wake => '',
1327 memcheck => 'memcheck',
1328 sysinfo => 'sysinfo',
1329 );
1331 # Return if this is unknown
1332 if (!exists $mapActions{ $st }){
1333 daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1);
1334 return;
1335 }
1337 my $state= $mapActions{ $st };
1339 &refresh_ldap_handle();
1340 if( defined($ldap_handle) ) {
1342 # Build search filter for hosts
1343 my $search= "(&(objectClass=GOhard)";
1344 foreach (@{$targets}){
1345 $search.= "(macAddress=$_)";
1346 }
1347 $search.= ")";
1349 # If there's any host inside of the search string, procress them
1350 if (!($search =~ /macAddress/)){
1351 daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);
1352 return;
1353 }
1355 # Perform search for Unit Tag
1356 my $mesg = $ldap_handle->search(
1357 base => $ldap_base,
1358 scope => 'sub',
1359 attrs => ['dn', 'FAIstate', 'objectClass'],
1360 filter => "$search"
1361 );
1363 if ($mesg->count) {
1364 my @entries = $mesg->entries;
1365 foreach my $entry (@entries) {
1366 # Only modify entry if it is not set to '$state'
1367 if ($entry->get_value("FAIstate") ne "$state"){
1368 daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
1369 my $result;
1370 my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
1371 if (exists $tmp{'FAIobject'}){
1372 if ($state eq ''){
1373 $result= $ldap_handle->modify($entry->dn, changes => [
1374 delete => [ FAIstate => [] ] ]);
1375 } else {
1376 $result= $ldap_handle->modify($entry->dn, changes => [
1377 replace => [ FAIstate => $state ] ]);
1378 }
1379 } elsif ($state ne ''){
1380 $result= $ldap_handle->modify($entry->dn, changes => [
1381 add => [ objectClass => 'FAIobject' ],
1382 add => [ FAIstate => $state ] ]);
1383 }
1385 # Errors?
1386 if ($result->code){
1387 daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
1388 }
1390 } else {
1391 daemon_log("$session_id DEBUG FAIstate at host found with filter statement '$search' already at state '$st'", 7);
1392 }
1393 }
1394 }
1395 # if no ldap handle defined
1396 } else {
1397 daemon_log("$session_id ERROR: no LDAP handle defined for update FAIstate", 1);
1398 }
1399 }
1402 sub change_goto_state {
1403 my ($st, $targets, $session_id) = @_;
1404 $session_id = 0 if not defined $session_id;
1406 # Switch on or off?
1407 my $state= $st eq 'active' ? 'active': 'locked';
1409 &refresh_ldap_handle();
1410 if( defined($ldap_handle) ) {
1412 # Build search filter for hosts
1413 my $search= "(&(objectClass=GOhard)";
1414 foreach (@{$targets}){
1415 $search.= "(macAddress=$_)";
1416 }
1417 $search.= ")";
1419 # If there's any host inside of the search string, procress them
1420 if (!($search =~ /macAddress/)){
1421 return;
1422 }
1424 # Perform search for Unit Tag
1425 my $mesg = $ldap_handle->search(
1426 base => $ldap_base,
1427 scope => 'sub',
1428 attrs => ['dn', 'gotoMode'],
1429 filter => "$search"
1430 );
1432 if ($mesg->count) {
1433 my @entries = $mesg->entries;
1434 foreach my $entry (@entries) {
1436 # Only modify entry if it is not set to '$state'
1437 if ($entry->get_value("gotoMode") ne $state){
1439 daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
1440 my $result;
1441 $result= $ldap_handle->modify($entry->dn, changes => [
1442 replace => [ gotoMode => $state ] ]);
1444 # Errors?
1445 if ($result->code){
1446 &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
1447 }
1449 }
1450 }
1451 }
1453 }
1454 }
1457 sub create_fai_server_db {
1458 my ($table_name, $kernel) = @_;
1459 my $result;
1461 if(defined($ldap_handle)) {
1462 daemon_log("INFO: create_fai_server_db: start", 5);
1463 my $mesg= $ldap_handle->search(
1464 base => $ldap_base,
1465 scope => 'sub',
1466 attrs => ['FAIrepository', 'gosaUnitTag'],
1467 filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
1468 );
1469 if($mesg->{'resultCode'} == 0 &&
1470 $mesg->count != 0) {
1471 foreach my $entry (@{$mesg->{entries}}) {
1472 if($entry->exists('FAIrepository')) {
1473 # Add an entry for each Repository configured for server
1474 foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
1475 my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
1476 my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
1477 $result= $fai_server_db->add_dbentry( {
1478 table => $table_name,
1479 primkey => ['server', 'release', 'tag'],
1480 server => $tmp_url,
1481 release => $tmp_release,
1482 sections => $tmp_sections,
1483 tag => (length($tmp_tag)>0)?$tmp_tag:"",
1484 } );
1485 }
1486 }
1487 }
1488 }
1489 daemon_log("INFO: create_fai_server_db: finished", 5);
1491 # TODO: Find a way to post the 'create_packages_list_db' event
1492 &create_packages_list_db();
1493 }
1495 return $result;
1496 }
1498 sub run_create_fai_server_db {
1499 my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
1500 my $task = POE::Wheel::Run->new(
1501 Program => sub { &create_fai_server_db($table_name,$kernel) },
1502 StdoutEvent => "session_run_result",
1503 StderrEvent => "session_run_debug",
1504 CloseEvent => "session_run_done",
1505 );
1507 $heap->{task}->{ $task->ID } = $task;
1508 return;
1509 }
1512 sub create_fai_release_db {
1513 my ($table_name) = @_;
1514 my $result;
1516 if(defined($ldap_handle)) {
1517 daemon_log("INFO: create_fai_release_db: start",5);
1518 my $mesg= $ldap_handle->search(
1519 base => $ldap_base,
1520 scope => 'sub',
1521 attrs => [],
1522 filter => "(&(objectClass=organizationalUnit)(ou=fai))",
1523 );
1524 if($mesg->{'resultCode'} == 0 &&
1525 $mesg->count != 0) {
1526 # Walk through all possible FAI container ou's
1527 my @sql_list;
1528 my $timestamp= &get_time();
1529 foreach my $ou (@{$mesg->{entries}}) {
1530 my $tmp_classes= resolve_fai_classes($ou->dn);
1531 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
1532 my @tmp_array=get_fai_release_entries($tmp_classes);
1533 if(@tmp_array) {
1534 foreach my $entry (@tmp_array) {
1535 if(defined($entry) && ref($entry) eq 'HASH') {
1536 my $sql=
1537 "INSERT INTO $table_name "
1538 ."(timestamp, release, class, type, state) VALUES ("
1539 .$timestamp.","
1540 ."'".$entry->{'release'}."',"
1541 ."'".$entry->{'class'}."',"
1542 ."'".$entry->{'type'}."',"
1543 ."'".$entry->{'state'}."')";
1544 push @sql_list, $sql;
1545 }
1546 }
1547 }
1548 }
1549 }
1550 daemon_log("DEBUG: Inserting ".scalar @sql_list." entries to DB",6);
1551 if(@sql_list) {
1552 unshift @sql_list, "DELETE FROM $table_name";
1553 $fai_server_db->exec_statementlist(\@sql_list);
1554 }
1555 daemon_log("DEBUG: Done with inserting",6);
1556 }
1557 daemon_log("INFO: create_fai_release_db: finished",5);
1558 }
1560 return $result;
1561 }
1562 sub run_create_fai_release_db {
1563 my ($session, $heap, $table_name) = @_[SESSION, HEAP, ARG0];
1564 my $task = POE::Wheel::Run->new(
1565 Program => sub { &create_fai_release_db($table_name) },
1566 StdoutEvent => "session_run_result",
1567 StderrEvent => "session_run_debug",
1568 CloseEvent => "session_run_done",
1569 );
1571 $heap->{task}->{ $task->ID } = $task;
1572 return;
1573 }
1575 sub get_fai_types {
1576 my $tmp_classes = shift || return undef;
1577 my @result;
1579 foreach my $type(keys %{$tmp_classes}) {
1580 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
1581 my $entry = {
1582 type => $type,
1583 state => $tmp_classes->{$type}[0],
1584 };
1585 push @result, $entry;
1586 }
1587 }
1589 return @result;
1590 }
1592 sub get_fai_state {
1593 my $result = "";
1594 my $tmp_classes = shift || return $result;
1596 foreach my $type(keys %{$tmp_classes}) {
1597 if(defined($tmp_classes->{$type}[0])) {
1598 $result = $tmp_classes->{$type}[0];
1600 # State is equal for all types in class
1601 last;
1602 }
1603 }
1605 return $result;
1606 }
1608 sub resolve_fai_classes {
1609 my $result;
1610 my $fai_base= shift;
1611 my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
1612 my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
1613 my $fai_classes;
1615 daemon_log("DEBUG: Searching for FAI entries in base $fai_base",6);
1616 my $mesg= $ldap_handle->search(
1617 base => $fai_base,
1618 scope => 'sub',
1619 attrs => ['cn','objectClass','FAIstate'],
1620 filter => $fai_filter,
1621 );
1622 daemon_log("DEBUG: Found ".$mesg->count()." FAI entries",6);
1624 if($mesg->{'resultCode'} == 0 &&
1625 $mesg->count != 0) {
1626 foreach my $entry (@{$mesg->{entries}}) {
1627 if($entry->exists('cn')) {
1628 my $tmp_dn= $entry->dn();
1630 # Skip classname and ou dn parts for class
1631 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?),$fai_base$/;
1633 # Skip classes without releases
1634 if((!defined($tmp_release)) || length($tmp_release)==0) {
1635 next;
1636 }
1638 my $tmp_cn= $entry->get_value('cn');
1639 my $tmp_state= $entry->get_value('FAIstate');
1641 my $tmp_type;
1642 # Get FAI type
1643 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
1644 if(grep $_ eq $oclass, @possible_fai_classes) {
1645 $tmp_type= $oclass;
1646 last;
1647 }
1648 }
1650 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
1651 # A Subrelease
1652 my @sub_releases = split(/,/, $tmp_release);
1654 # Walk through subreleases and build hash tree
1655 my $hash;
1656 while(my $tmp_sub_release = pop @sub_releases) {
1657 $hash .= "\{'$tmp_sub_release'\}->";
1658 }
1659 eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
1660 } else {
1661 # A branch, no subrelease
1662 push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
1663 }
1664 } elsif (!$entry->exists('cn')) {
1665 my $tmp_dn= $entry->dn();
1666 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?),$fai_base$/;
1668 # Skip classes without releases
1669 if((!defined($tmp_release)) || length($tmp_release)==0) {
1670 next;
1671 }
1673 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
1674 # A Subrelease
1675 my @sub_releases= split(/,/, $tmp_release);
1677 # Walk through subreleases and build hash tree
1678 my $hash;
1679 while(my $tmp_sub_release = pop @sub_releases) {
1680 $hash .= "\{'$tmp_sub_release'\}->";
1681 }
1682 # Remove the last two characters
1683 chop($hash);
1684 chop($hash);
1686 eval('$fai_classes->'.$hash.'= {}');
1687 } else {
1688 # A branch, no subrelease
1689 if(!exists($fai_classes->{$tmp_release})) {
1690 $fai_classes->{$tmp_release} = {};
1691 }
1692 }
1693 }
1694 }
1696 # The hash is complete, now we can honor the copy-on-write based missing entries
1697 foreach my $release (keys %$fai_classes) {
1698 $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
1699 }
1700 }
1701 return $result;
1702 }
1704 sub apply_fai_inheritance {
1705 my $fai_classes = shift || return {};
1706 my $tmp_classes;
1708 # Get the classes from the branch
1709 foreach my $class (keys %{$fai_classes}) {
1710 # Skip subreleases
1711 if($class =~ /^ou=.*$/) {
1712 next;
1713 } else {
1714 $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
1715 }
1716 }
1718 # Apply to each subrelease
1719 foreach my $subrelease (keys %{$fai_classes}) {
1720 if($subrelease =~ /ou=/) {
1721 foreach my $tmp_class (keys %{$tmp_classes}) {
1722 if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
1723 $fai_classes->{$subrelease}->{$tmp_class} =
1724 deep_copy($tmp_classes->{$tmp_class});
1725 } else {
1726 foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
1727 if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
1728 $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
1729 deep_copy($tmp_classes->{$tmp_class}->{$type});
1730 }
1731 }
1732 }
1733 }
1734 }
1735 }
1737 # Find subreleases in deeper levels
1738 foreach my $subrelease (keys %{$fai_classes}) {
1739 if($subrelease =~ /ou=/) {
1740 foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
1741 if($subsubrelease =~ /ou=/) {
1742 apply_fai_inheritance($fai_classes->{$subrelease});
1743 }
1744 }
1745 }
1746 }
1748 return $fai_classes;
1749 }
1751 sub get_fai_release_entries {
1752 my $tmp_classes = shift || return;
1753 my $parent = shift || "";
1754 my @result = shift || ();
1756 foreach my $entry (keys %{$tmp_classes}) {
1757 if(defined($entry)) {
1758 if($entry =~ /^ou=.*$/) {
1759 my $release_name = $entry;
1760 $release_name =~ s/ou=//g;
1761 if(length($parent)>0) {
1762 $release_name = $parent."/".$release_name;
1763 }
1764 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
1765 foreach my $bufentry(@bufentries) {
1766 push @result, $bufentry;
1767 }
1768 } else {
1769 my @types = get_fai_types($tmp_classes->{$entry});
1770 foreach my $type (@types) {
1771 push @result,
1772 {
1773 'class' => $entry,
1774 'type' => $type->{'type'},
1775 'release' => $parent,
1776 'state' => $type->{'state'},
1777 };
1778 }
1779 }
1780 }
1781 }
1783 return @result;
1784 }
1786 sub deep_copy {
1787 my $this = shift;
1788 if (not ref $this) {
1789 $this;
1790 } elsif (ref $this eq "ARRAY") {
1791 [map deep_copy($_), @$this];
1792 } elsif (ref $this eq "HASH") {
1793 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
1794 } else { die "what type is $_?" }
1795 }
1798 sub session_run_result {
1799 my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];
1800 $kernel->sig(CHLD => "child_reap");
1801 }
1803 sub session_run_debug {
1804 my $result = $_[ARG0];
1805 print STDERR "$result\n";
1806 }
1808 sub session_run_done {
1809 my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1810 delete $heap->{task}->{$task_id};
1811 }
1813 sub create_sources_list {
1814 my $result="/tmp/gosa_si_tmp_sources_list";
1816 # Remove old file
1817 if(stat($result)) {
1818 unlink($result);
1819 }
1821 my $fh;
1822 open($fh, ">$result") or return undef;
1823 if(defined($ldap_server_dn) && length($ldap_server_dn)>0) {
1824 my $mesg=$ldap_handle->search(
1825 base => $ldap_server_dn,
1826 scope => 'base',
1827 attrs => 'FAIrepository',
1828 );
1830 if($mesg->count) {
1831 foreach my $entry(@{$mesg->{'entries'}}) {
1832 my ($server, $tag, $release, $sections)= split /\|/, $entry->get_value('FAIrepository');
1833 my $line = "deb $server $release";
1834 $sections =~ s/,/ /g;
1835 $line.= " $sections";
1836 print $fh $line."\n";
1837 }
1838 }
1839 }
1840 close($fh);
1842 return $result;
1843 }
1845 sub create_packages_list_db {
1846 my ($sources_file) = @_ || &create_sources_list;
1847 my $line;
1848 daemon_log("INFO: create_packages_list_db: start", 5);
1850 open(CONFIG, "<$sources_file") or do {
1851 daemon_log( "ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
1852 return;
1853 };
1855 # Read lines
1856 while ($line = <CONFIG>){
1857 # Unify
1858 chop($line);
1859 $line =~ s/^\s+//;
1860 $line =~ s/^\s+/ /;
1862 # Strip comments
1863 $line =~ s/#.*$//g;
1865 # Skip empty lines
1866 if ($line =~ /^\s*$/){
1867 next;
1868 }
1870 # Interpret deb line
1871 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
1872 my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
1873 my $section;
1874 foreach $section (split(' ', $sections)){
1875 &parse_package_info( $baseurl, $dist, $section );
1876 }
1877 }
1878 }
1880 close (CONFIG);
1882 daemon_log("INFO: create_packages_list_db: finished", 5);
1883 return;
1884 }
1885 sub run_create_packages_list_db {
1886 my ($session, $heap) = @_[SESSION, HEAP];
1887 my $task = POE::Wheel::Run->new(
1888 Program => sub {&create_packages_list_db},
1889 StdoutEvent => "session_run_result",
1890 StderrEvent => "session_run_debug",
1891 CloseEvent => "session_run_done",
1892 );
1893 $heap->{task}->{ $task->ID } = $task;
1894 }
1895 sub parse_package_info {
1896 my ($baseurl, $dist, $section)= @_;
1897 my ($package);
1899 my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
1901 foreach $package ("Packages.gz"){
1902 daemon_log("DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
1903 get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section" );
1904 parse_package( "$outdir/$dist/$section", $dist, $path );
1905 }
1906 }
1907 sub get_package {
1908 my ($url, $dest)= @_;
1910 my $tpath = dirname($dest);
1911 -d "$tpath" || mkpath "$tpath";
1913 # This is ugly, but I've no time to take a look at "how it works in perl"
1914 if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
1915 system("gunzip -cd '$dest' > '$dest.in'");
1916 system("rm -f '$dest'");
1917 } else {
1918 daemon_log("ERROR: create_packages_list_db: get_packages: fetching '$url' failed!", 1);
1919 }
1920 return 0;
1921 }
1922 sub parse_package {
1923 my ($path, $dist, $srv_path )= @_;
1924 my ($package, $version, $section, $description);
1925 my @sql_list;
1926 my $PACKAGES;
1928 if(not stat("$path.in")) {
1929 daemon_log("ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
1930 return;
1931 }
1933 open($PACKAGES, "<$path.in");
1934 if(not defined($PACKAGES)) {
1935 daemon_log("ERROR: create_packages_list_db: parse_package: can not open '$path.in'",1);
1936 return;
1937 }
1939 # Read lines
1940 while (<$PACKAGES>){
1941 my $line = $_;
1942 # Unify
1943 chop($line);
1945 # Use empty lines as a trigger
1946 if ($line =~ /^\s*$/){
1947 my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', 'xxx', 'none', '0')";
1948 push(@sql_list, $sql);
1949 $package = "none";
1950 $version = "none";
1951 $section = "none";
1952 $description = "none";
1953 next;
1954 }
1956 # Trigger for package name
1957 if ($line =~ /^Package:\s/){
1958 ($package)= ($line =~ /^Package: (.*)$/);
1959 next;
1960 }
1962 # Trigger for version
1963 if ($line =~ /^Version:\s/){
1964 ($version)= ($line =~ /^Version: (.*)$/);
1965 next;
1966 }
1968 # Trigger for description
1969 if ($line =~ /^Description:\s/){
1970 ($description)= ($line =~ /^Description: (.*)$/);
1971 next;
1972 }
1974 # Trigger for section
1975 if ($line =~ /^Section:\s/){
1976 ($section)= ($line =~ /^Section: (.*)$/);
1977 next;
1978 }
1980 }
1982 close( $PACKAGES );
1983 unlink( "$path.in" );
1985 $packages_list_db->exec_statementlist(\@sql_list);
1986 }
1988 #
1989 #sub store_fileinfo {
1990 # my( $package, $file, $dist, $path, $vers ) = @_;
1991 #
1992 # my %fileinfo = (
1993 # 'package' => $package,
1994 # 'dist' => $dist,
1995 # 'version' => $vers
1996 # );
1997 #
1998 # $repo_files{ "${srvdir}${path}/$file" } = \%fileinfo;
1999 #}
2002 #==== MAIN = main ==============================================================
2003 # parse commandline options
2004 Getopt::Long::Configure( "bundling" );
2005 GetOptions("h|help" => \&usage,
2006 "c|config=s" => \$cfg_file,
2007 "f|foreground" => \$foreground,
2008 "v|verbose+" => \$verbose,
2009 "no-bus+" => \$no_bus,
2010 "no-arp+" => \$no_arp,
2011 );
2013 # read and set config parameters
2014 &check_cmdline_param ;
2015 &read_configfile;
2016 &check_pid;
2018 $SIG{CHLD} = 'IGNORE';
2020 # forward error messages to logfile
2021 if( ! $foreground ) {
2022 open( STDIN, '+>/dev/null' );
2023 open( STDOUT, '+>&STDIN' );
2024 open( STDERR, '+>&STDIN' );
2025 }
2027 # Just fork, if we are not in foreground mode
2028 if( ! $foreground ) {
2029 chdir '/' or die "Can't chdir to /: $!";
2030 $pid = fork;
2031 setsid or die "Can't start a new session: $!";
2032 umask 0;
2033 } else {
2034 $pid = $$;
2035 }
2037 # Do something useful - put our PID into the pid_file
2038 if( 0 != $pid ) {
2039 open( LOCK_FILE, ">$pid_file" );
2040 print LOCK_FILE "$pid\n";
2041 close( LOCK_FILE );
2042 if( !$foreground ) {
2043 exit( 0 )
2044 };
2045 }
2047 daemon_log(" ", 1);
2048 daemon_log("$0 started!", 1);
2050 if ($no_bus > 0) {
2051 $bus_activ = "false"
2052 }
2056 # delete old DBsqlite lock files
2057 #unlink('/tmp/gosa_si_lock*');
2059 # connect to gosa-si job queue
2060 $job_db = GOSA::DBsqlite->new($job_queue_file_name);
2061 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
2063 # connect to known_clients_db
2064 $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
2065 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
2067 # connect to known_server_db
2068 $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
2069 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
2071 # connect to login_usr_db
2072 $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
2073 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
2075 # connect to fai_server_db and fai_release_db
2076 $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
2077 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
2078 $fai_server_db->create_table($fai_release_tn, \@fai_release_col_names);
2080 # connect to packages_list_db
2081 unlink($packages_list_file_name);
2082 $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
2083 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
2085 # connect to messaging_db
2086 $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
2087 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
2090 # create xml object used for en/decrypting
2091 $xml = new XML::Simple();
2093 # create socket for incoming xml messages
2095 POE::Component::Server::TCP->new(
2096 Port => $server_port,
2097 ClientInput => sub {
2098 my ($kernel, $input) = @_[KERNEL, ARG0];
2099 push(@tasks, $input);
2100 $kernel->yield("next_task");
2101 },
2102 InlineStates => {
2103 next_task => \&next_task,
2104 task_result => \&handle_task_result,
2105 task_done => \&handle_task_done,
2106 task_debug => \&handle_task_debug,
2107 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!" },
2108 }
2109 );
2111 daemon_log("start socket for incoming xml messages at port '$server_port' ", 1);
2113 # create session for repeatedly checking the job queue for jobs
2114 POE::Session->create(
2115 inline_states => {
2116 _start => \&_start,
2117 sig_handler => \&sig_handler,
2118 watch_for_new_jobs => \&watch_for_new_jobs,
2119 watch_for_done_jobs => \&watch_for_done_jobs,
2120 create_packages_list_db => \&run_create_packages_list_db,
2121 create_fai_server_db => \&run_create_fai_server_db,
2122 create_fai_release_db => \&run_create_fai_release_db,
2123 session_run_result => \&session_run_result,
2124 session_run_debug => \&session_run_debug,
2125 session_run_done => \&session_run_done,
2126 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!" },
2127 }
2128 );
2131 # import all modules
2132 &import_modules;
2134 # check wether all modules are gosa-si valid passwd check
2136 POE::Kernel->run();
2137 exit;