7653c67f444b051358bafcc6e5e7971182df2453
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 install => 'install',
1330 );
1332 # Return if this is unknown
1333 if (!exists $mapActions{ $st }){
1334 daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1);
1335 return;
1336 }
1338 my $state= $mapActions{ $st };
1340 &refresh_ldap_handle();
1341 if( defined($ldap_handle) ) {
1343 # Build search filter for hosts
1344 my $search= "(&(objectClass=GOhard)";
1345 foreach (@{$targets}){
1346 $search.= "(macAddress=$_)";
1347 }
1348 $search.= ")";
1350 # If there's any host inside of the search string, procress them
1351 if (!($search =~ /macAddress/)){
1352 daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);
1353 return;
1354 }
1356 # Perform search for Unit Tag
1357 my $mesg = $ldap_handle->search(
1358 base => $ldap_base,
1359 scope => 'sub',
1360 attrs => ['dn', 'FAIstate', 'objectClass'],
1361 filter => "$search"
1362 );
1364 if ($mesg->count) {
1365 my @entries = $mesg->entries;
1366 foreach my $entry (@entries) {
1367 # Only modify entry if it is not set to '$state'
1368 if ($entry->get_value("FAIstate") ne "$state"){
1369 daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
1370 my $result;
1371 my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
1372 if (exists $tmp{'FAIobject'}){
1373 if ($state eq ''){
1374 $result= $ldap_handle->modify($entry->dn, changes => [
1375 delete => [ FAIstate => [] ] ]);
1376 } else {
1377 $result= $ldap_handle->modify($entry->dn, changes => [
1378 replace => [ FAIstate => $state ] ]);
1379 }
1380 } elsif ($state ne ''){
1381 $result= $ldap_handle->modify($entry->dn, changes => [
1382 add => [ objectClass => 'FAIobject' ],
1383 add => [ FAIstate => $state ] ]);
1384 }
1386 # Errors?
1387 if ($result->code){
1388 daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
1389 }
1391 } else {
1392 daemon_log("$session_id DEBUG FAIstate at host found with filter statement '$search' already at state '$st'", 7);
1393 }
1394 }
1395 }
1396 # if no ldap handle defined
1397 } else {
1398 daemon_log("$session_id ERROR: no LDAP handle defined for update FAIstate", 1);
1399 }
1400 }
1403 sub change_goto_state {
1404 my ($st, $targets, $session_id) = @_;
1405 $session_id = 0 if not defined $session_id;
1407 # Switch on or off?
1408 my $state= $st eq 'active' ? 'active': 'locked';
1410 &refresh_ldap_handle();
1411 if( defined($ldap_handle) ) {
1413 # Build search filter for hosts
1414 my $search= "(&(objectClass=GOhard)";
1415 foreach (@{$targets}){
1416 $search.= "(macAddress=$_)";
1417 }
1418 $search.= ")";
1420 # If there's any host inside of the search string, procress them
1421 if (!($search =~ /macAddress/)){
1422 return;
1423 }
1425 # Perform search for Unit Tag
1426 my $mesg = $ldap_handle->search(
1427 base => $ldap_base,
1428 scope => 'sub',
1429 attrs => ['dn', 'gotoMode'],
1430 filter => "$search"
1431 );
1433 if ($mesg->count) {
1434 my @entries = $mesg->entries;
1435 foreach my $entry (@entries) {
1437 # Only modify entry if it is not set to '$state'
1438 if ($entry->get_value("gotoMode") ne $state){
1440 daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
1441 my $result;
1442 $result= $ldap_handle->modify($entry->dn, changes => [
1443 replace => [ gotoMode => $state ] ]);
1445 # Errors?
1446 if ($result->code){
1447 &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
1448 }
1450 }
1451 }
1452 }
1454 }
1455 }
1458 sub create_fai_server_db {
1459 my ($table_name, $kernel) = @_;
1460 my $result;
1462 if(defined($ldap_handle)) {
1463 daemon_log("INFO: create_fai_server_db: start", 5);
1464 my $mesg= $ldap_handle->search(
1465 base => $ldap_base,
1466 scope => 'sub',
1467 attrs => ['FAIrepository', 'gosaUnitTag'],
1468 filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
1469 );
1470 if($mesg->{'resultCode'} == 0 &&
1471 $mesg->count != 0) {
1472 foreach my $entry (@{$mesg->{entries}}) {
1473 if($entry->exists('FAIrepository')) {
1474 # Add an entry for each Repository configured for server
1475 foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
1476 my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
1477 my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
1478 $result= $fai_server_db->add_dbentry( {
1479 table => $table_name,
1480 primkey => ['server', 'release', 'tag'],
1481 server => $tmp_url,
1482 release => $tmp_release,
1483 sections => $tmp_sections,
1484 tag => (length($tmp_tag)>0)?$tmp_tag:"",
1485 } );
1486 }
1487 }
1488 }
1489 }
1490 daemon_log("INFO: create_fai_server_db: finished", 5);
1492 # TODO: Find a way to post the 'create_packages_list_db' event
1493 &create_packages_list_db();
1494 }
1496 return $result;
1497 }
1499 sub run_create_fai_server_db {
1500 my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
1501 my $task = POE::Wheel::Run->new(
1502 Program => sub { &create_fai_server_db($table_name,$kernel) },
1503 StdoutEvent => "session_run_result",
1504 StderrEvent => "session_run_debug",
1505 CloseEvent => "session_run_done",
1506 );
1508 $heap->{task}->{ $task->ID } = $task;
1509 return;
1510 }
1513 sub create_fai_release_db {
1514 my ($table_name) = @_;
1515 my $result;
1517 if(defined($ldap_handle)) {
1518 daemon_log("INFO: create_fai_release_db: start",5);
1519 my $mesg= $ldap_handle->search(
1520 base => $ldap_base,
1521 scope => 'sub',
1522 attrs => [],
1523 filter => "(&(objectClass=organizationalUnit)(ou=fai))",
1524 );
1525 if($mesg->{'resultCode'} == 0 &&
1526 $mesg->count != 0) {
1527 # Walk through all possible FAI container ou's
1528 my @sql_list;
1529 my $timestamp= &get_time();
1530 foreach my $ou (@{$mesg->{entries}}) {
1531 my $tmp_classes= resolve_fai_classes($ou->dn);
1532 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
1533 my @tmp_array=get_fai_release_entries($tmp_classes);
1534 if(@tmp_array) {
1535 foreach my $entry (@tmp_array) {
1536 if(defined($entry) && ref($entry) eq 'HASH') {
1537 my $sql=
1538 "INSERT INTO $table_name "
1539 ."(timestamp, release, class, type, state) VALUES ("
1540 .$timestamp.","
1541 ."'".$entry->{'release'}."',"
1542 ."'".$entry->{'class'}."',"
1543 ."'".$entry->{'type'}."',"
1544 ."'".$entry->{'state'}."')";
1545 push @sql_list, $sql;
1546 }
1547 }
1548 }
1549 }
1550 }
1551 daemon_log("DEBUG: Inserting ".scalar @sql_list." entries to DB",6);
1552 if(@sql_list) {
1553 unshift @sql_list, "DELETE FROM $table_name";
1554 $fai_server_db->exec_statementlist(\@sql_list);
1555 }
1556 daemon_log("DEBUG: Done with inserting",6);
1557 }
1558 daemon_log("INFO: create_fai_release_db: finished",5);
1559 }
1561 return $result;
1562 }
1563 sub run_create_fai_release_db {
1564 my ($session, $heap, $table_name) = @_[SESSION, HEAP, ARG0];
1565 my $task = POE::Wheel::Run->new(
1566 Program => sub { &create_fai_release_db($table_name) },
1567 StdoutEvent => "session_run_result",
1568 StderrEvent => "session_run_debug",
1569 CloseEvent => "session_run_done",
1570 );
1572 $heap->{task}->{ $task->ID } = $task;
1573 return;
1574 }
1576 sub get_fai_types {
1577 my $tmp_classes = shift || return undef;
1578 my @result;
1580 foreach my $type(keys %{$tmp_classes}) {
1581 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
1582 my $entry = {
1583 type => $type,
1584 state => $tmp_classes->{$type}[0],
1585 };
1586 push @result, $entry;
1587 }
1588 }
1590 return @result;
1591 }
1593 sub get_fai_state {
1594 my $result = "";
1595 my $tmp_classes = shift || return $result;
1597 foreach my $type(keys %{$tmp_classes}) {
1598 if(defined($tmp_classes->{$type}[0])) {
1599 $result = $tmp_classes->{$type}[0];
1601 # State is equal for all types in class
1602 last;
1603 }
1604 }
1606 return $result;
1607 }
1609 sub resolve_fai_classes {
1610 my $result;
1611 my $fai_base= shift;
1612 my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
1613 my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
1614 my $fai_classes;
1616 daemon_log("DEBUG: Searching for FAI entries in base $fai_base",6);
1617 my $mesg= $ldap_handle->search(
1618 base => $fai_base,
1619 scope => 'sub',
1620 attrs => ['cn','objectClass','FAIstate'],
1621 filter => $fai_filter,
1622 );
1623 daemon_log("DEBUG: Found ".$mesg->count()." FAI entries",6);
1625 if($mesg->{'resultCode'} == 0 &&
1626 $mesg->count != 0) {
1627 foreach my $entry (@{$mesg->{entries}}) {
1628 if($entry->exists('cn')) {
1629 my $tmp_dn= $entry->dn();
1631 # Skip classname and ou dn parts for class
1632 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?),$fai_base$/;
1634 # Skip classes without releases
1635 if((!defined($tmp_release)) || length($tmp_release)==0) {
1636 next;
1637 }
1639 my $tmp_cn= $entry->get_value('cn');
1640 my $tmp_state= $entry->get_value('FAIstate');
1642 my $tmp_type;
1643 # Get FAI type
1644 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
1645 if(grep $_ eq $oclass, @possible_fai_classes) {
1646 $tmp_type= $oclass;
1647 last;
1648 }
1649 }
1651 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
1652 # A Subrelease
1653 my @sub_releases = split(/,/, $tmp_release);
1655 # Walk through subreleases and build hash tree
1656 my $hash;
1657 while(my $tmp_sub_release = pop @sub_releases) {
1658 $hash .= "\{'$tmp_sub_release'\}->";
1659 }
1660 eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
1661 } else {
1662 # A branch, no subrelease
1663 push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
1664 }
1665 } elsif (!$entry->exists('cn')) {
1666 my $tmp_dn= $entry->dn();
1667 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?),$fai_base$/;
1669 # Skip classes without releases
1670 if((!defined($tmp_release)) || length($tmp_release)==0) {
1671 next;
1672 }
1674 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
1675 # A Subrelease
1676 my @sub_releases= split(/,/, $tmp_release);
1678 # Walk through subreleases and build hash tree
1679 my $hash;
1680 while(my $tmp_sub_release = pop @sub_releases) {
1681 $hash .= "\{'$tmp_sub_release'\}->";
1682 }
1683 # Remove the last two characters
1684 chop($hash);
1685 chop($hash);
1687 eval('$fai_classes->'.$hash.'= {}');
1688 } else {
1689 # A branch, no subrelease
1690 if(!exists($fai_classes->{$tmp_release})) {
1691 $fai_classes->{$tmp_release} = {};
1692 }
1693 }
1694 }
1695 }
1697 # The hash is complete, now we can honor the copy-on-write based missing entries
1698 foreach my $release (keys %$fai_classes) {
1699 $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
1700 }
1701 }
1702 return $result;
1703 }
1705 sub apply_fai_inheritance {
1706 my $fai_classes = shift || return {};
1707 my $tmp_classes;
1709 # Get the classes from the branch
1710 foreach my $class (keys %{$fai_classes}) {
1711 # Skip subreleases
1712 if($class =~ /^ou=.*$/) {
1713 next;
1714 } else {
1715 $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
1716 }
1717 }
1719 # Apply to each subrelease
1720 foreach my $subrelease (keys %{$fai_classes}) {
1721 if($subrelease =~ /ou=/) {
1722 foreach my $tmp_class (keys %{$tmp_classes}) {
1723 if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
1724 $fai_classes->{$subrelease}->{$tmp_class} =
1725 deep_copy($tmp_classes->{$tmp_class});
1726 } else {
1727 foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
1728 if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
1729 $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
1730 deep_copy($tmp_classes->{$tmp_class}->{$type});
1731 }
1732 }
1733 }
1734 }
1735 }
1736 }
1738 # Find subreleases in deeper levels
1739 foreach my $subrelease (keys %{$fai_classes}) {
1740 if($subrelease =~ /ou=/) {
1741 foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
1742 if($subsubrelease =~ /ou=/) {
1743 apply_fai_inheritance($fai_classes->{$subrelease});
1744 }
1745 }
1746 }
1747 }
1749 return $fai_classes;
1750 }
1752 sub get_fai_release_entries {
1753 my $tmp_classes = shift || return;
1754 my $parent = shift || "";
1755 my @result = shift || ();
1757 foreach my $entry (keys %{$tmp_classes}) {
1758 if(defined($entry)) {
1759 if($entry =~ /^ou=.*$/) {
1760 my $release_name = $entry;
1761 $release_name =~ s/ou=//g;
1762 if(length($parent)>0) {
1763 $release_name = $parent."/".$release_name;
1764 }
1765 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
1766 foreach my $bufentry(@bufentries) {
1767 push @result, $bufentry;
1768 }
1769 } else {
1770 my @types = get_fai_types($tmp_classes->{$entry});
1771 foreach my $type (@types) {
1772 push @result,
1773 {
1774 'class' => $entry,
1775 'type' => $type->{'type'},
1776 'release' => $parent,
1777 'state' => $type->{'state'},
1778 };
1779 }
1780 }
1781 }
1782 }
1784 return @result;
1785 }
1787 sub deep_copy {
1788 my $this = shift;
1789 if (not ref $this) {
1790 $this;
1791 } elsif (ref $this eq "ARRAY") {
1792 [map deep_copy($_), @$this];
1793 } elsif (ref $this eq "HASH") {
1794 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
1795 } else { die "what type is $_?" }
1796 }
1799 sub session_run_result {
1800 my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];
1801 $kernel->sig(CHLD => "child_reap");
1802 }
1804 sub session_run_debug {
1805 my $result = $_[ARG0];
1806 print STDERR "$result\n";
1807 }
1809 sub session_run_done {
1810 my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1811 delete $heap->{task}->{$task_id};
1812 }
1814 sub create_sources_list {
1815 my $result="/tmp/gosa_si_tmp_sources_list";
1817 # Remove old file
1818 if(stat($result)) {
1819 unlink($result);
1820 }
1822 my $fh;
1823 open($fh, ">$result") or return undef;
1824 if(defined($ldap_server_dn) && length($ldap_server_dn)>0) {
1825 my $mesg=$ldap_handle->search(
1826 base => $ldap_server_dn,
1827 scope => 'base',
1828 attrs => 'FAIrepository',
1829 );
1831 if($mesg->count) {
1832 foreach my $entry(@{$mesg->{'entries'}}) {
1833 my ($server, $tag, $release, $sections)= split /\|/, $entry->get_value('FAIrepository');
1834 my $line = "deb $server $release";
1835 $sections =~ s/,/ /g;
1836 $line.= " $sections";
1837 print $fh $line."\n";
1838 }
1839 }
1840 }
1841 close($fh);
1843 return $result;
1844 }
1846 sub create_packages_list_db {
1847 my ($sources_file) = @_ || &create_sources_list;
1848 my $line;
1849 daemon_log("INFO: create_packages_list_db: start", 5);
1851 open(CONFIG, "<$sources_file") or do {
1852 daemon_log( "ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
1853 return;
1854 };
1856 # Read lines
1857 while ($line = <CONFIG>){
1858 # Unify
1859 chop($line);
1860 $line =~ s/^\s+//;
1861 $line =~ s/^\s+/ /;
1863 # Strip comments
1864 $line =~ s/#.*$//g;
1866 # Skip empty lines
1867 if ($line =~ /^\s*$/){
1868 next;
1869 }
1871 # Interpret deb line
1872 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
1873 my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
1874 my $section;
1875 foreach $section (split(' ', $sections)){
1876 &parse_package_info( $baseurl, $dist, $section );
1877 }
1878 }
1879 }
1881 close (CONFIG);
1883 daemon_log("INFO: create_packages_list_db: finished", 5);
1884 return;
1885 }
1886 sub run_create_packages_list_db {
1887 my ($session, $heap) = @_[SESSION, HEAP];
1888 my $task = POE::Wheel::Run->new(
1889 Program => sub {&create_packages_list_db},
1890 StdoutEvent => "session_run_result",
1891 StderrEvent => "session_run_debug",
1892 CloseEvent => "session_run_done",
1893 );
1894 $heap->{task}->{ $task->ID } = $task;
1895 }
1896 sub parse_package_info {
1897 my ($baseurl, $dist, $section)= @_;
1898 my ($package);
1900 my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
1902 foreach $package ("Packages.gz"){
1903 daemon_log("DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
1904 get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section" );
1905 parse_package( "$outdir/$dist/$section", $dist, $path );
1906 }
1907 }
1908 sub get_package {
1909 my ($url, $dest)= @_;
1911 my $tpath = dirname($dest);
1912 -d "$tpath" || mkpath "$tpath";
1914 # This is ugly, but I've no time to take a look at "how it works in perl"
1915 if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
1916 system("gunzip -cd '$dest' > '$dest.in'");
1917 unlink($dest);
1918 } else {
1919 daemon_log("ERROR: create_packages_list_db: get_packages: fetching '$url' failed!", 1);
1920 }
1921 return 0;
1922 }
1923 sub parse_package {
1924 my ($path, $dist, $srv_path )= @_;
1925 my ($package, $version, $section, $description);
1926 my @sql_list;
1927 my $PACKAGES;
1929 if(not stat("$path.in")) {
1930 daemon_log("ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
1931 return;
1932 }
1934 open($PACKAGES, "<$path.in");
1935 if(not defined($PACKAGES)) {
1936 daemon_log("ERROR: create_packages_list_db: parse_package: can not open '$path.in'",1);
1937 return;
1938 }
1940 # Read lines
1941 while (<$PACKAGES>){
1942 my $line = $_;
1943 # Unify
1944 chop($line);
1946 # Use empty lines as a trigger
1947 if ($line =~ /^\s*$/){
1948 my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '', 'none', '0')";
1949 push(@sql_list, $sql);
1950 $package = "none";
1951 $version = "none";
1952 $section = "none";
1953 $description = "none";
1954 next;
1955 }
1957 # Trigger for package name
1958 if ($line =~ /^Package:\s/){
1959 ($package)= ($line =~ /^Package: (.*)$/);
1960 next;
1961 }
1963 # Trigger for version
1964 if ($line =~ /^Version:\s/){
1965 ($version)= ($line =~ /^Version: (.*)$/);
1966 next;
1967 }
1969 # Trigger for description
1970 if ($line =~ /^Description:\s/){
1971 ($description)= ($line =~ /^Description: (.*)$/);
1972 next;
1973 }
1975 # Trigger for section
1976 if ($line =~ /^Section:\s/){
1977 ($section)= ($line =~ /^Section: (.*)$/);
1978 next;
1979 }
1981 }
1983 close( $PACKAGES );
1984 unlink( "$path.in" );
1986 $packages_list_db->exec_statementlist(\@sql_list);
1987 }
1990 sub store_fileinfo {
1991 my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
1993 my %fileinfo = (
1994 'package' => $package,
1995 'dist' => $dist,
1996 'version' => $vers,
1997 );
1999 $repo_files{ "${srvdir}/$file" } = \%fileinfo;
2000 }
2003 sub cleanup_and_extract {
2004 my $fileinfo = $repo_files{ $File::Find::name };
2006 if( defined $fileinfo ) {
2008 my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
2009 my $sql;
2010 my $package = $fileinfo->{ 'package' };
2011 my $newver = $fileinfo->{ 'version' };
2013 mkpath($dir);
2014 system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
2016 if( -f "$dir/DEBIAN/templates" ) {
2018 daemon_log("DEBUG: Found debconf templates in '$package' - $newver", 5);
2020 my $tmpl= "";
2021 {
2022 local $/=undef;
2023 open FILE, "$dir/DEBIAN/templates";
2024 $tmpl = &encode_base64(<FILE>);
2025 close FILE;
2026 }
2027 rmtree("$dir/DEBIAN/templates");
2029 $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
2031 } else {
2032 $sql= "update $main::packages_list_tn set template = '' where package = '$package' and version = '$newver';";
2033 }
2035 my $res= $main::packages_list_db->update_dbentry($sql);
2036 }
2037 }
2040 #==== MAIN = main ==============================================================
2041 # parse commandline options
2042 Getopt::Long::Configure( "bundling" );
2043 GetOptions("h|help" => \&usage,
2044 "c|config=s" => \$cfg_file,
2045 "f|foreground" => \$foreground,
2046 "v|verbose+" => \$verbose,
2047 "no-bus+" => \$no_bus,
2048 "no-arp+" => \$no_arp,
2049 );
2051 # read and set config parameters
2052 &check_cmdline_param ;
2053 &read_configfile;
2054 &check_pid;
2056 $SIG{CHLD} = 'IGNORE';
2058 # forward error messages to logfile
2059 if( ! $foreground ) {
2060 open( STDIN, '+>/dev/null' );
2061 open( STDOUT, '+>&STDIN' );
2062 open( STDERR, '+>&STDIN' );
2063 }
2065 # Just fork, if we are not in foreground mode
2066 if( ! $foreground ) {
2067 chdir '/' or die "Can't chdir to /: $!";
2068 $pid = fork;
2069 setsid or die "Can't start a new session: $!";
2070 umask 0;
2071 } else {
2072 $pid = $$;
2073 }
2075 # Do something useful - put our PID into the pid_file
2076 if( 0 != $pid ) {
2077 open( LOCK_FILE, ">$pid_file" );
2078 print LOCK_FILE "$pid\n";
2079 close( LOCK_FILE );
2080 if( !$foreground ) {
2081 exit( 0 )
2082 };
2083 }
2085 daemon_log(" ", 1);
2086 daemon_log("$0 started!", 1);
2088 if ($no_bus > 0) {
2089 $bus_activ = "false"
2090 }
2094 # delete old DBsqlite lock files
2095 #unlink('/tmp/gosa_si_lock*');
2097 # connect to gosa-si job queue
2098 $job_db = GOSA::DBsqlite->new($job_queue_file_name);
2099 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
2101 # connect to known_clients_db
2102 $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
2103 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
2105 # connect to known_server_db
2106 $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
2107 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
2109 # connect to login_usr_db
2110 $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
2111 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
2113 # connect to fai_server_db and fai_release_db
2114 $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
2115 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
2116 $fai_server_db->create_table($fai_release_tn, \@fai_release_col_names);
2118 # connect to packages_list_db
2119 unlink($packages_list_file_name);
2120 $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
2121 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
2123 # connect to messaging_db
2124 $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
2125 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
2128 # create xml object used for en/decrypting
2129 $xml = new XML::Simple();
2131 # create socket for incoming xml messages
2133 POE::Component::Server::TCP->new(
2134 Port => $server_port,
2135 ClientInput => sub {
2136 my ($kernel, $input) = @_[KERNEL, ARG0];
2137 push(@tasks, $input);
2138 $kernel->yield("next_task");
2139 },
2140 InlineStates => {
2141 next_task => \&next_task,
2142 task_result => \&handle_task_result,
2143 task_done => \&handle_task_done,
2144 task_debug => \&handle_task_debug,
2145 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!" },
2146 }
2147 );
2149 daemon_log("start socket for incoming xml messages at port '$server_port' ", 1);
2151 # create session for repeatedly checking the job queue for jobs
2152 POE::Session->create(
2153 inline_states => {
2154 _start => \&_start,
2155 sig_handler => \&sig_handler,
2156 watch_for_new_jobs => \&watch_for_new_jobs,
2157 watch_for_done_jobs => \&watch_for_done_jobs,
2158 create_packages_list_db => \&run_create_packages_list_db,
2159 create_fai_server_db => \&run_create_fai_server_db,
2160 create_fai_release_db => \&run_create_fai_release_db,
2161 session_run_result => \&session_run_result,
2162 session_run_debug => \&session_run_debug,
2163 session_run_done => \&session_run_done,
2164 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!" },
2165 }
2166 );
2169 # import all modules
2170 &import_modules;
2172 # check wether all modules are gosa-si valid passwd check
2174 POE::Kernel->run();
2175 exit;