52bd3181ac179285f30cd9053391314c9c648871
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::Find;
44 use File::Copy;
45 use File::Path;
46 use GOSA::DBsqlite;
47 use GOSA::GosaSupportDaemon;
48 use POE qw(Component::Server::TCP Wheel::Run Filter::Reference);
49 use Net::LDAP;
50 use Net::LDAP::Util qw(:escape);
52 my $modules_path = "/usr/lib/gosa-si/modules";
53 use lib "/usr/lib/gosa-si/modules";
55 my (%cfg_defaults, $foreground, $verbose, $ping_timeout);
56 my ($bus_activ, $bus, $msg_to_bus, $bus_cipher);
57 my ($server);
58 my ($gosa_server, $job_queue_timeout, $job_queue_loop_delay);
59 my ($known_modules);
60 my ($pid_file, $procid, $pid, $log_file);
61 my ($arp_activ, $arp_fifo);
62 my ($xml);
63 my $sources_list;
64 my $max_clients;
65 my %repo_files=();
66 my $repo_path;
67 my %repo_dirs=();
68 # variables declared in config file are always set to 'our'
69 our (%cfg_defaults, $log_file, $pid_file,
70 $server_ip, $server_port, $SIPackages_key,
71 $arp_activ, $gosa_unit_tag,
72 $GosaPackages_key, $gosa_ip, $gosa_port, $gosa_timeout,
73 );
75 # additional variable which should be globaly accessable
76 our $server_address;
77 our $server_mac_address;
78 our $bus_address;
79 our $gosa_address;
80 our $no_bus;
81 our $no_arp;
82 our $verbose;
83 our $forground;
84 our $cfg_file;
85 our ($ldap_handle, $ldap_uri, $ldap_base, $ldap_admin_dn, $ldap_admin_password, $ldap_server_dn);
88 # specifies the verbosity of the daemon_log
89 $verbose = 0 ;
91 # if foreground is not null, script will be not forked to background
92 $foreground = 0 ;
94 # specifies the timeout seconds while checking the online status of a registrating client
95 $ping_timeout = 5;
97 $no_bus = 0;
98 $bus_activ = "true";
100 $no_arp = 0;
102 our $prg= basename($0);
104 # holds all gosa jobs
105 our $job_db;
106 our $job_queue_tn = 'jobs';
107 my $job_queue_file_name;
108 my @job_queue_col_names = ("id INTEGER",
109 "timestamp",
110 "status DEFAULT 'none'",
111 "result DEFAULT 'none'",
112 "progress DEFAULT 'none'",
113 "headertag DEFAULT 'none'",
114 "targettag DEFAULT 'none'",
115 "xmlmessage DEFAULT 'none'",
116 "macaddress DEFAULT 'none'",
117 );
119 # holds all other gosa-sd as well as the gosa-sd-bus
120 our $known_server_db;
121 our $known_server_tn = "known_server";
122 my $known_server_file_name;
123 my @known_server_col_names = ('hostname', 'status', 'hostkey', 'timestamp');
125 # holds all registrated clients
126 our $known_clients_db;
127 our $known_clients_tn = "known_clients";
128 my $known_clients_file_name;
129 my @known_clients_col_names = ('hostname', 'status', 'hostkey', 'timestamp', 'macaddress', 'events');
131 # holds all logged in user at each client
132 our $login_users_db;
133 our $login_users_tn = "login_users";
134 my $login_users_file_name;
135 my @login_users_col_names = ('client', 'user', 'timestamp');
137 # holds all fai server, the debian release and tag
138 our $fai_server_db;
139 our $fai_server_tn = "fai_server";
140 my $fai_server_file_name;
141 our @fai_server_col_names = ('timestamp', 'server', 'release', 'sections', 'tag');
142 our $fai_release_tn = "fai_release";
143 our @fai_release_col_names = ('timestamp', 'release', 'class', 'type', 'state');
145 # holds all packages available from different repositories
146 our $packages_list_db;
147 our $packages_list_tn = "packages_list";
148 my $packages_list_file_name;
149 our @packages_list_col_names = ('distribution', 'package', 'version', 'section', 'description', 'template', 'timestamp');
150 my $outdir = "/tmp/packages_list_db";
151 my $arch = "i386";
153 # holds all messages which should be delivered to a user
154 our $messaging_db;
155 our $messaging_tn = "messaging";
156 our @messaging_col_names = ('subject', 'from', 'to', 'flag', 'direction', 'delivery_time', 'message', 'timestamp', 'id INTEGER', );
157 my $messaging_file_name;
159 # path to directory to store client install log files
160 our $client_fai_log_dir = "/var/log/fai";
162 # queue which stores taskes until one of the $max_children children are ready to process the task
163 my @tasks = qw();
164 my $max_children = 2;
167 %cfg_defaults = (
168 "general" => {
169 "log-file" => [\$log_file, "/var/run/".$prg.".log"],
170 "pid-file" => [\$pid_file, "/var/run/".$prg.".pid"],
171 },
172 "bus" => {
173 "activ" => [\$bus_activ, "true"],
174 },
175 "server" => {
176 "port" => [\$server_port, "20081"],
177 "known-clients" => [\$known_clients_file_name, '/var/lib/gosa-si/clients.db' ],
178 "known-servers" => [\$known_server_file_name, '/var/lib/gosa-si/servers.db'],
179 "login-users" => [\$login_users_file_name, '/var/lib/gosa-si/users.db'],
180 "fai-server" => [\$fai_server_file_name, '/var/lib/gosa-si/fai.db'],
181 "packages-list" => [\$packages_list_file_name, '/var/lib/gosa-si/packages.db'],
182 "messaging" => [\$messaging_file_name, '/var/lib/gosa-si/messaging.db'],
183 "source-list" => [\$sources_list, '/etc/apt/sources.list'],
184 "repo-path" => [\$repo_path, '/srv/www/repository'],
185 "ldap-uri" => [\$ldap_uri, ""],
186 "ldap-base" => [\$ldap_base, ""],
187 "ldap-admin-dn" => [\$ldap_admin_dn, ""],
188 "ldap-admin-password" => [\$ldap_admin_password, ""],
189 "gosa-unit-tag" => [\$gosa_unit_tag, ""],
190 "max-clients" => [\$max_clients, 10],
191 },
192 "GOsaPackages" => {
193 "ip" => [\$gosa_ip, "0.0.0.0"],
194 "port" => [\$gosa_port, "20082"],
195 "job-queue" => [\$job_queue_file_name, '/var/lib/gosa-si/jobs.db'],
196 "job-queue-loop-delay" => [\$job_queue_loop_delay, 3],
197 "key" => [\$GosaPackages_key, "none"],
198 },
199 "SIPackages" => {
200 "key" => [\$SIPackages_key, "none"],
201 },
202 );
205 #=== FUNCTION ================================================================
206 # NAME: usage
207 # PARAMETERS: nothing
208 # RETURNS: nothing
209 # DESCRIPTION: print out usage text to STDERR
210 #===============================================================================
211 sub usage {
212 print STDERR << "EOF" ;
213 usage: $prg [-hvf] [-c config]
215 -h : this (help) message
216 -c <file> : config file
217 -f : foreground, process will not be forked to background
218 -v : be verbose (multiple to increase verbosity)
219 -no-bus : starts $prg without connection to bus
220 -no-arp : starts $prg without connection to arp module
222 EOF
223 print "\n" ;
224 }
227 #=== FUNCTION ================================================================
228 # NAME: read_configfile
229 # PARAMETERS: cfg_file - string -
230 # RETURNS: nothing
231 # DESCRIPTION: read cfg_file and set variables
232 #===============================================================================
233 sub read_configfile {
234 my $cfg;
235 if( defined( $cfg_file) && ( length($cfg_file) > 0 )) {
236 if( -r $cfg_file ) {
237 $cfg = Config::IniFiles->new( -file => $cfg_file );
238 } else {
239 print STDERR "Couldn't read config file!\n";
240 }
241 } else {
242 $cfg = Config::IniFiles->new() ;
243 }
244 foreach my $section (keys %cfg_defaults) {
245 foreach my $param (keys %{$cfg_defaults{ $section }}) {
246 my $pinfo = $cfg_defaults{ $section }{ $param };
247 ${@$pinfo[ 0 ]} = $cfg->val( $section, $param, @$pinfo[ 1 ] );
248 }
249 }
250 }
253 #=== FUNCTION ================================================================
254 # NAME: logging
255 # PARAMETERS: level - string - default 'info'
256 # msg - string -
257 # facility - string - default 'LOG_DAEMON'
258 # RETURNS: nothing
259 # DESCRIPTION: function for logging
260 #===============================================================================
261 sub daemon_log {
262 # log into log_file
263 my( $msg, $level ) = @_;
264 if(not defined $msg) { return }
265 if(not defined $level) { $level = 1 }
266 if(defined $log_file){
267 open(LOG_HANDLE, ">>$log_file");
268 if(not defined open( LOG_HANDLE, ">>$log_file" )) {
269 print STDERR "cannot open $log_file: $!";
270 return }
271 chomp($msg);
272 if($level <= $verbose){
273 my ($seconds, $minutes, $hours, $monthday, $month,
274 $year, $weekday, $yearday, $sommertime) = localtime(time);
275 $hours = $hours < 10 ? $hours = "0".$hours : $hours;
276 $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
277 $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
278 my @monthnames = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
279 $month = $monthnames[$month];
280 $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
281 $year+=1900;
282 my $name = $prg;
284 my $log_msg = "$month $monthday $hours:$minutes:$seconds $name $msg\n";
285 print LOG_HANDLE $log_msg;
286 if( $foreground ) {
287 print STDERR $log_msg;
288 }
289 }
290 close( LOG_HANDLE );
291 }
292 }
295 #=== FUNCTION ================================================================
296 # NAME: check_cmdline_param
297 # PARAMETERS: nothing
298 # RETURNS: nothing
299 # DESCRIPTION: validates commandline parameter
300 #===============================================================================
301 sub check_cmdline_param () {
302 my $err_config;
303 my $err_counter = 0;
304 if(not defined($cfg_file)) {
305 $cfg_file = "/etc/gosa-si/server.conf";
306 if(! -r $cfg_file) {
307 $err_config = "please specify a config file";
308 $err_counter += 1;
309 }
310 }
311 if( $err_counter > 0 ) {
312 &usage( "", 1 );
313 if( defined( $err_config)) { print STDERR "$err_config\n"}
314 print STDERR "\n";
315 exit( -1 );
316 }
317 }
320 #=== FUNCTION ================================================================
321 # NAME: check_pid
322 # PARAMETERS: nothing
323 # RETURNS: nothing
324 # DESCRIPTION: handels pid processing
325 #===============================================================================
326 sub check_pid {
327 $pid = -1;
328 # Check, if we are already running
329 if( open(LOCK_FILE, "<$pid_file") ) {
330 $pid = <LOCK_FILE>;
331 if( defined $pid ) {
332 chomp( $pid );
333 if( -f "/proc/$pid/stat" ) {
334 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
335 if( $stat ) {
336 daemon_log("ERROR: Already running",1);
337 close( LOCK_FILE );
338 exit -1;
339 }
340 }
341 }
342 close( LOCK_FILE );
343 unlink( $pid_file );
344 }
346 # create a syslog msg if it is not to possible to open PID file
347 if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
348 my($msg) = "Couldn't obtain lockfile '$pid_file' ";
349 if (open(LOCK_FILE, '<', $pid_file)
350 && ($pid = <LOCK_FILE>))
351 {
352 chomp($pid);
353 $msg .= "(PID $pid)\n";
354 } else {
355 $msg .= "(unable to read PID)\n";
356 }
357 if( ! ($foreground) ) {
358 openlog( $0, "cons,pid", "daemon" );
359 syslog( "warning", $msg );
360 closelog();
361 }
362 else {
363 print( STDERR " $msg " );
364 }
365 exit( -1 );
366 }
367 }
369 #=== FUNCTION ================================================================
370 # NAME: import_modules
371 # PARAMETERS: module_path - string - abs. path to the directory the modules
372 # are stored
373 # RETURNS: nothing
374 # DESCRIPTION: each file in module_path which ends with '.pm' and activation
375 # state is on is imported by "require 'file';"
376 #===============================================================================
377 sub import_modules {
378 daemon_log(" ", 1);
380 if (not -e $modules_path) {
381 daemon_log("ERROR: cannot find directory or directory is not readable: $modules_path", 1);
382 }
384 opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
385 while (defined (my $file = readdir (DIR))) {
386 if (not $file =~ /(\S*?).pm$/) {
387 next;
388 }
389 my $mod_name = $1;
391 if( $file =~ /ArpHandler.pm/ ) {
392 if( $no_arp > 0 ) {
393 next;
394 }
395 }
397 eval { require $file; };
398 if ($@) {
399 daemon_log("ERROR: gosa-si-server could not load module $file", 1);
400 daemon_log("$@", 5);
401 } else {
402 my $info = eval($mod_name.'::get_module_info()');
403 # Only load module if get_module_info() returns a non-null object
404 if( $info ) {
405 my ($input_address, $input_key, $input, $input_active, $input_type) = @{$info};
406 $known_modules->{$mod_name} = $info;
407 daemon_log("INFO: module $mod_name loaded", 5);
408 }
409 }
410 }
411 close (DIR);
412 }
415 #=== FUNCTION ================================================================
416 # NAME: sig_int_handler
417 # PARAMETERS: signal - string - signal arose from system
418 # RETURNS: noting
419 # DESCRIPTION: handels tasks to be done befor signal becomes active
420 #===============================================================================
421 sub sig_int_handler {
422 my ($signal) = @_;
424 if(defined($ldap_handle)) {
425 $ldap_handle->disconnect;
426 }
428 daemon_log("shutting down gosa-si-server", 1);
429 system("killall gosa-si-server");
430 }
431 $SIG{INT} = \&sig_int_handler;
434 sub check_key_and_xml_validity {
435 my ($crypted_msg, $module_key, $session_id) = @_;
436 my $msg;
437 my $msg_hash;
438 my $error_string;
439 eval{
440 $msg = &decrypt_msg($crypted_msg, $module_key);
442 if ($msg =~ /<xml>/i){
443 $msg =~ s/\s+/ /g; # just for better daemon_log
444 daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 8);
445 $msg_hash = $xml->XMLin($msg, ForceArray=>1);
447 ##############
448 # check header
449 if( not exists $msg_hash->{'header'} ) { die "no header specified"; }
450 my $header_l = $msg_hash->{'header'};
451 if( 1 > @{$header_l} ) { die 'empty header tag'; }
452 if( 1 < @{$header_l} ) { die 'more than one header specified'; }
453 my $header = @{$header_l}[0];
454 if( 0 == length $header) { die 'empty string in header tag'; }
456 ##############
457 # check source
458 if( not exists $msg_hash->{'source'} ) { die "no source specified"; }
459 my $source_l = $msg_hash->{'source'};
460 if( 1 > @{$source_l} ) { die 'empty source tag'; }
461 if( 1 < @{$source_l} ) { die 'more than one source specified'; }
462 my $source = @{$source_l}[0];
463 if( 0 == length $source) { die 'source error'; }
465 ##############
466 # check target
467 if( not exists $msg_hash->{'target'} ) { die "no target specified"; }
468 my $target_l = $msg_hash->{'target'};
469 if( 1 > @{$target_l} ) { die 'empty target tag'; }
470 }
471 };
472 if($@) {
473 daemon_log("$session_id DEBUG: do not understand the message: $@", 7);
474 $msg = undef;
475 $msg_hash = undef;
476 }
478 return ($msg, $msg_hash);
479 }
482 sub check_outgoing_xml_validity {
483 my ($msg) = @_;
485 my $msg_hash;
486 eval{
487 $msg_hash = $xml->XMLin($msg, ForceArray=>1);
489 ##############
490 # check header
491 my $header_l = $msg_hash->{'header'};
492 if( 1 != @{$header_l} ) {
493 die 'no or more than one headers specified';
494 }
495 my $header = @{$header_l}[0];
496 if( 0 == length $header) {
497 die 'header has length 0';
498 }
500 ##############
501 # check source
502 my $source_l = $msg_hash->{'source'};
503 if( 1 != @{$source_l} ) {
504 die 'no or more than 1 sources specified';
505 }
506 my $source = @{$source_l}[0];
507 if( 0 == length $source) {
508 die 'source has length 0';
509 }
510 unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
511 $source =~ /^GOSA$/i ) {
512 die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
513 }
515 ##############
516 # check target
517 my $target_l = $msg_hash->{'target'};
518 if( 0 == @{$target_l} ) {
519 die "no targets specified";
520 }
521 foreach my $target (@$target_l) {
522 if( 0 == length $target) {
523 die "target has length 0";
524 }
525 unless( $target =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
526 $target =~ /^GOSA$/i ||
527 $target =~ /^\*$/ ||
528 $target =~ /KNOWN_SERVER/i ||
529 $target =~ /JOBDB/i ||
530 $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 ){
531 die "target '$target' is not a complete ip-address with port or a valid target name or a mac-address";
532 }
533 }
534 };
535 if($@) {
536 daemon_log("WARNING: outgoing msg is not gosa-si envelope conform", 5);
537 daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 8);
538 $msg_hash = undef;
539 }
541 return ($msg_hash);
542 }
545 sub input_from_known_server {
546 my ($input, $remote_ip, $session_id) = @_ ;
547 my ($msg, $msg_hash, $module);
549 my $sql_statement= "SELECT * FROM known_server";
550 my $query_res = $known_server_db->select_dbentry( $sql_statement );
552 while( my ($hit_num, $hit) = each %{ $query_res } ) {
553 my $host_name = $hit->{hostname};
554 if( not $host_name =~ "^$remote_ip") {
555 next;
556 }
557 my $host_key = $hit->{hostkey};
558 daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 7);
559 daemon_log("DEBUG: input_from_known_server: host_key: $host_key", 7);
561 # check if module can open msg envelope with module key
562 my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
563 if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
564 daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 7);
565 daemon_log("$@", 8);
566 next;
567 }
568 else {
569 $msg = $tmp_msg;
570 $msg_hash = $tmp_msg_hash;
571 $module = "SIPackages";
572 last;
573 }
574 }
576 if( (!$msg) || (!$msg_hash) || (!$module) ) {
577 daemon_log("$session_id DEBUG: Incoming message is not from a known server", 7);
578 }
580 return ($msg, $msg_hash, $module);
581 }
584 sub input_from_known_client {
585 my ($input, $remote_ip, $session_id) = @_ ;
586 my ($msg, $msg_hash, $module);
588 my $sql_statement= "SELECT * FROM known_clients";
589 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
590 while( my ($hit_num, $hit) = each %{ $query_res } ) {
591 my $host_name = $hit->{hostname};
592 if( not $host_name =~ /^$remote_ip:\d*$/) {
593 next;
594 }
595 my $host_key = $hit->{hostkey};
596 &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 7);
597 &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 7);
599 # check if module can open msg envelope with module key
600 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
602 if( (!$msg) || (!$msg_hash) ) {
603 &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 7);
604 &daemon_log("$@", 8);
605 next;
606 }
607 else {
608 $module = "SIPackages";
609 last;
610 }
611 }
613 if( (!$msg) || (!$msg_hash) || (!$module) ) {
614 &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 7);
615 }
617 return ($msg, $msg_hash, $module);
618 }
621 sub input_from_unknown_host {
622 no strict "refs";
623 my ($input, $session_id) = @_ ;
624 my ($msg, $msg_hash, $module);
625 my $error_string;
627 my %act_modules = %$known_modules;
629 while( my ($mod, $info) = each(%act_modules)) {
631 # check a key exists for this module
632 my $module_key = ${$mod."_key"};
633 if( not defined $module_key ) {
634 if( $mod eq 'ArpHandler' ) {
635 next;
636 }
637 daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
638 next;
639 }
640 daemon_log("$session_id DEBUG: $mod: $module_key", 7);
642 # check if module can open msg envelope with module key
643 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
644 if( (not defined $msg) || (not defined $msg_hash) ) {
645 next;
646 }
647 else {
648 $module = $mod;
649 last;
650 }
651 }
653 if( (!$msg) || (!$msg_hash) || (!$module)) {
654 daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 7);
655 }
657 return ($msg, $msg_hash, $module);
658 }
661 sub create_ciphering {
662 my ($passwd) = @_;
663 if((!defined($passwd)) || length($passwd)==0) {
664 $passwd = "";
665 }
666 $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
667 my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
668 my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
669 $my_cipher->set_iv($iv);
670 return $my_cipher;
671 }
674 sub encrypt_msg {
675 my ($msg, $key) = @_;
676 my $my_cipher = &create_ciphering($key);
677 my $len;
678 {
679 use bytes;
680 $len= 16-length($msg)%16;
681 }
682 $msg = "\0"x($len).$msg;
683 $msg = $my_cipher->encrypt($msg);
684 chomp($msg = &encode_base64($msg));
685 # there are no newlines allowed inside msg
686 $msg=~ s/\n//g;
687 return $msg;
688 }
691 sub decrypt_msg {
693 my ($msg, $key) = @_ ;
694 $msg = &decode_base64($msg);
695 my $my_cipher = &create_ciphering($key);
696 $msg = $my_cipher->decrypt($msg);
697 $msg =~ s/\0*//g;
698 return $msg;
699 }
702 sub get_encrypt_key {
703 my ($target) = @_ ;
704 my $encrypt_key;
705 my $error = 0;
707 # target can be in known_server
708 if( not defined $encrypt_key ) {
709 my $sql_statement= "SELECT * FROM known_server WHERE hostname='$target'";
710 my $query_res = $known_server_db->select_dbentry( $sql_statement );
711 while( my ($hit_num, $hit) = each %{ $query_res } ) {
712 my $host_name = $hit->{hostname};
713 if( $host_name ne $target ) {
714 next;
715 }
716 $encrypt_key = $hit->{hostkey};
717 last;
718 }
719 }
721 # target can be in known_client
722 if( not defined $encrypt_key ) {
723 my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$target'";
724 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
725 while( my ($hit_num, $hit) = each %{ $query_res } ) {
726 my $host_name = $hit->{hostname};
727 if( $host_name ne $target ) {
728 next;
729 }
730 $encrypt_key = $hit->{hostkey};
731 last;
732 }
733 }
735 return $encrypt_key;
736 }
739 #=== FUNCTION ================================================================
740 # NAME: open_socket
741 # PARAMETERS: PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
742 # [PeerPort] string necessary if port not appended by PeerAddr
743 # RETURNS: socket IO::Socket::INET
744 # DESCRIPTION: open a socket to PeerAddr
745 #===============================================================================
746 sub open_socket {
747 my ($PeerAddr, $PeerPort) = @_ ;
748 if(defined($PeerPort)){
749 $PeerAddr = $PeerAddr.":".$PeerPort;
750 }
751 my $socket;
752 $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
753 Porto => "tcp",
754 Type => SOCK_STREAM,
755 Timeout => 5,
756 );
757 if(not defined $socket) {
758 return;
759 }
760 # &daemon_log("DEBUG: open_socket: $PeerAddr", 7);
761 return $socket;
762 }
765 #=== FUNCTION ================================================================
766 # NAME: get_ip
767 # PARAMETERS: interface name (i.e. eth0)
768 # RETURNS: (ip address)
769 # DESCRIPTION: Uses ioctl to get ip address directly from system.
770 #===============================================================================
771 sub get_ip {
772 my $ifreq= shift;
773 my $result= "";
774 my $SIOCGIFADDR= 0x8915; # man 2 ioctl_list
775 my $proto= getprotobyname('ip');
777 socket SOCKET, PF_INET, SOCK_DGRAM, $proto
778 or die "socket: $!";
780 if(ioctl SOCKET, $SIOCGIFADDR, $ifreq) {
781 my ($if, $sin) = unpack 'a16 a16', $ifreq;
782 my ($port, $addr) = sockaddr_in $sin;
783 my $ip = inet_ntoa $addr;
785 if ($ip && length($ip) > 0) {
786 $result = $ip;
787 }
788 }
790 return $result;
791 }
794 sub get_local_ip_for_remote_ip {
795 my $remote_ip= shift;
796 my $result="0.0.0.0";
798 if($remote_ip =~ /^(\d\d?\d?\.){3}\d\d?\d?$/) {
799 if($remote_ip eq "127.0.0.1") {
800 $result = "127.0.0.1";
801 } else {
802 my $PROC_NET_ROUTE= ('/proc/net/route');
804 open(PROC_NET_ROUTE, "<$PROC_NET_ROUTE")
805 or die "Could not open $PROC_NET_ROUTE";
807 my @ifs = <PROC_NET_ROUTE>;
809 close(PROC_NET_ROUTE);
811 # Eat header line
812 shift @ifs;
813 chomp @ifs;
814 foreach my $line(@ifs) {
815 my ($Iface,$Destination,$Gateway,$Flags,$RefCnt,$Use,$Metric,$Mask,$MTU,$Window,$IRTT)=split(/\s/, $line);
816 my $destination;
817 my $mask;
818 my ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Destination);
819 $destination= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
820 ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Mask);
821 $mask= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
822 if(new NetAddr::IP($remote_ip)->within(new NetAddr::IP($destination, $mask))) {
823 # destination matches route, save mac and exit
824 $result= &get_ip($Iface);
825 last;
826 }
827 }
828 }
829 } else {
830 daemon_log("get_local_ip_for_remote_ip was called with a non-ip parameter: $remote_ip", 1);
831 }
832 return $result;
833 }
836 sub send_msg_to_target {
837 my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
838 my $error = 0;
839 my $header;
840 my $new_status;
841 my $act_status;
842 my ($sql_statement, $res);
844 if( $msg_header ) {
845 $header = "'$msg_header'-";
846 } else {
847 $header = "";
848 }
850 # Patch the source ip
851 if($msg =~ /<source>0\.0\.0\.0:\d*?<\/source>/) {
852 my $remote_ip = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
853 $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$remote_ip:$2<\/source>/s;
854 }
856 # encrypt xml msg
857 my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
859 # opensocket
860 my $socket = &open_socket($address);
861 if( !$socket ) {
862 daemon_log("$session_id ERROR: cannot send ".$header."msg to $address , host not reachable", 1);
863 $error++;
864 }
866 if( $error == 0 ) {
867 # send xml msg
868 print $socket $crypted_msg."\n";
870 daemon_log("$session_id INFO: send ".$header."msg to $address", 5);
871 #daemon_log("DEBUG: message:\n$msg", 9);
873 }
875 # close socket in any case
876 if( $socket ) {
877 close $socket;
878 }
880 if( $error > 0 ) { $new_status = "down"; }
881 else { $new_status = $msg_header; }
884 # known_clients
885 $sql_statement = "SELECT * FROM known_clients WHERE hostname='$address'";
886 $res = $known_clients_db->select_dbentry($sql_statement);
887 if( keys(%$res) > 0) {
888 $act_status = $res->{1}->{'status'};
889 if( $act_status eq "down" ) {
890 $sql_statement = "DELETE FROM known_clients WHERE hostname='$address'";
891 $res = $known_clients_db->del_dbentry($sql_statement);
892 daemon_log("$session_id WARNING: failed 2x to send msg to host '$address', delete host from known_clients", 3);
893 } else {
894 $sql_statement = "UPDATE known_clients SET status='$new_status' WHERE hostname='$address'";
895 $res = $known_clients_db->update_dbentry($sql_statement);
896 if($new_status eq "down"){
897 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
898 } else {
899 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
900 }
901 }
902 }
904 # known_server
905 $sql_statement = "SELECT * FROM known_server WHERE hostname='$address'";
906 $res = $known_server_db->select_dbentry($sql_statement);
907 if( keys(%$res) > 0 ) {
908 $act_status = $res->{1}->{'status'};
909 if( $act_status eq "down" ) {
910 $sql_statement = "DELETE FROM known_server WHERE hostname='$address'";
911 $res = $known_server_db->del_dbentry($sql_statement);
912 daemon_log("$session_id WARNING: failed 2x to a send msg to host '$address', delete host from known_server", 3);
913 }
914 else {
915 $sql_statement = "UPDATE known_server SET status='$new_status' WHERE hostname='$address'";
916 $res = $known_server_db->update_dbentry($sql_statement);
917 if($new_status eq "down"){
918 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
919 }
920 else {
921 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
922 }
923 }
924 }
925 return $error;
926 }
929 sub update_jobdb_status_for_send_msgs {
930 my ($answer, $error) = @_;
931 if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
932 my $jobdb_id = $1;
934 # sending msg faild
935 if( $error ) {
936 if (not $answer =~ /<header>trigger_action_reinstall<\/header>/) {
937 my $sql_statement = "UPDATE $job_queue_tn ".
938 "SET status='error', result='can not deliver msg, please consult log file' ".
939 "WHERE id='$jobdb_id'";
940 my $res = $job_db->update_dbentry($sql_statement);
941 }
943 # sending msg was successful
944 } else {
945 my $sql_statement = "UPDATE $job_queue_tn ".
946 "SET status='done' ".
947 "WHERE id='$jobdb_id' AND status='processed'";
948 my $res = $job_db->update_dbentry($sql_statement);
949 }
950 }
951 }
953 sub _start {
954 my ($kernel) = $_[KERNEL];
955 &trigger_db_loop($kernel);
956 $kernel->yield('create_fai_server_db', $fai_server_tn );
957 $kernel->yield('create_fai_release_db', $fai_release_tn );
958 $kernel->sig(USR1 => "sig_handler");
959 }
961 sub sig_handler {
962 my ($kernel, $signal) = @_[KERNEL, ARG0] ;
963 daemon_log("0 INFO got signal '$signal'", 1);
964 $kernel->sig_handled();
965 return;
966 }
968 sub next_task {
969 my ($session, $heap) = @_[SESSION, HEAP];
971 while ( keys( %{ $heap->{task} } ) < $max_children ) {
972 my $next_task = shift @tasks;
973 last unless defined $next_task;
975 my $task = POE::Wheel::Run->new(
976 Program => sub { process_task($session, $heap, $next_task) },
977 StdioFilter => POE::Filter::Reference->new(),
978 StdoutEvent => "task_result",
979 StderrEvent => "task_debug",
980 CloseEvent => "task_done",
981 );
983 $heap->{task}->{ $task->ID } = $task;
984 }
985 }
987 sub handle_task_result {
988 my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
989 my $client_answer = $result->{'answer'};
990 if( $client_answer =~ s/session_id=(\d+)$// ) {
991 my $session_id = $1;
992 if( defined $session_id ) {
993 my $session_reference = $kernel->ID_id_to_session($session_id);
994 if( defined $session_reference ) {
995 $heap = $session_reference->get_heap();
996 }
997 }
999 if(exists $heap->{'client'}) {
1000 $heap->{'client'}->put($client_answer);
1001 }
1002 }
1003 $kernel->sig(CHLD => "child_reap");
1004 }
1006 sub handle_task_debug {
1007 my $result = $_[ARG0];
1008 print STDERR "$result\n";
1009 }
1011 sub handle_task_done {
1012 my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1013 delete $heap->{task}->{$task_id};
1014 $kernel->yield("next_task");
1015 }
1017 sub process_task {
1018 no strict "refs";
1019 my ($session, $heap, $input) = @_;
1020 my $session_id = $session->ID;
1021 my ($msg, $msg_hash, $module);
1022 my $error = 0;
1023 my $answer_l;
1024 my ($answer_header, @answer_target_l, $answer_source);
1025 my $client_answer = "";
1027 daemon_log("", 5);
1028 daemon_log("$session_id INFO: Incoming msg with session ID $session_id from '".$heap->{'remote_ip'}."'", 5);
1029 daemon_log("$session_id DEBUG: Incoming msg:\n$input", 9);
1031 ####################
1032 # check incoming msg
1033 # msg is from a new client or gosa
1034 ($msg, $msg_hash, $module) = &input_from_unknown_host($input, $session_id);
1035 # msg is from a gosa-si-server or gosa-si-bus
1036 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1037 ($msg, $msg_hash, $module) = &input_from_known_server($input, $heap->{'remote_ip'}, $session_id);
1038 }
1039 # msg is from a gosa-si-client
1040 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1041 ($msg, $msg_hash, $module) = &input_from_known_client($input, $heap->{'remote_ip'}, $session_id);
1042 }
1043 # an error occurred
1044 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1045 # if an incoming msg could not be decrypted (maybe a wrong key), send client a ping. If the client
1046 # could not understand a msg from its server the client cause a re-registering process
1047 my $sql_statement = "SELECT * FROM $main::known_clients_tn WHERE (hostname LIKE '".$heap->{'remote_ip'}."%')";
1048 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1049 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1050 my $host_name = $hit->{'hostname'};
1051 my $host_key = $hit->{'hostkey'};
1052 my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source> <target>$host_name</target></xml>";
1053 my $error = &send_msg_to_target($ping_msg, $host_name, $host_key, "gosa_ping", $session_id);
1054 &update_jobdb_status_for_send_msgs($ping_msg, $error);
1055 }
1056 $error++;
1057 }
1059 ######################
1060 # process incoming msg
1061 if( $error == 0) {
1062 daemon_log("$session_id INFO: Incoming msg with header '".@{$msg_hash->{'header'}}[0]."'", 5);
1063 daemon_log("$session_id DEBUG: Processing module ".$module, 7);
1064 $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1066 if ( 0 < @{$answer_l} ) {
1067 my $answer_str = join("\n", @{$answer_l});
1068 daemon_log("$session_id DEBUG: $module: Got answer from module: \n".$answer_str,8);
1069 }
1070 }
1071 if( !$answer_l ) { $error++ };
1073 ########
1074 # answer
1075 if( $error == 0 ) {
1077 foreach my $answer ( @{$answer_l} ) {
1078 # for each answer in answer list
1080 # check outgoing msg to xml validity
1081 my $answer_hash = &check_outgoing_xml_validity($answer);
1082 if( not defined $answer_hash ) {
1083 next;
1084 }
1086 $answer_header = @{$answer_hash->{'header'}}[0];
1087 @answer_target_l = @{$answer_hash->{'target'}};
1088 $answer_source = @{$answer_hash->{'source'}}[0];
1090 # deliver msg to all targets
1091 foreach my $answer_target ( @answer_target_l ) {
1093 # targets of msg are all gosa-si-clients in known_clients_db
1094 if( $answer_target eq "*" ) {
1095 # answer is for all clients
1096 my $sql_statement= "SELECT * FROM known_clients";
1097 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1098 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1099 my $host_name = $hit->{hostname};
1100 my $host_key = $hit->{hostkey};
1101 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1102 &update_jobdb_status_for_send_msgs($answer, $error);
1103 }
1104 }
1106 # targets of msg are all gosa-si-server in known_server_db
1107 elsif( $answer_target eq "KNOWN_SERVER" ) {
1108 # answer is for all server in known_server
1109 my $sql_statement= "SELECT * FROM known_server";
1110 my $query_res = $known_server_db->select_dbentry( $sql_statement );
1111 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1112 my $host_name = $hit->{hostname};
1113 my $host_key = $hit->{hostkey};
1114 $answer =~ s/KNOWN_SERVER/$host_name/g;
1115 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1116 &update_jobdb_status_for_send_msgs($answer, $error);
1117 }
1118 }
1120 # target of msg is GOsa
1121 elsif( $answer_target eq "GOSA" ) {
1122 my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1123 my $add_on = "";
1124 if( defined $session_id ) {
1125 $add_on = ".session_id=$session_id";
1126 }
1127 # answer is for GOSA and has to returned to connected client
1128 my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1129 $client_answer = $gosa_answer.$add_on;
1130 }
1132 # target of msg is job queue at this host
1133 elsif( $answer_target eq "JOBDB") {
1134 $answer =~ /<header>(\S+)<\/header>/;
1135 my $header;
1136 if( defined $1 ) { $header = $1; }
1137 my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1138 &update_jobdb_status_for_send_msgs($answer, $error);
1139 }
1141 # target of msg is a mac address
1142 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 ) {
1143 daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients", 5);
1144 my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1145 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1146 my $found_ip_flag = 0;
1147 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1148 my $host_name = $hit->{hostname};
1149 my $host_key = $hit->{hostkey};
1150 $answer =~ s/$answer_target/$host_name/g;
1151 daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1152 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1153 &update_jobdb_status_for_send_msgs($answer, $error);
1154 $found_ip_flag++ ;
1155 }
1156 if( $found_ip_flag == 0) {
1157 daemon_log("$session_id WARNING: no host found in known_clients with mac address '$answer_target'", 3);
1158 if( $bus_activ eq "true" ) {
1159 daemon_log("$session_id INFO: try to forward msg '$answer_header' to bus '$bus_address'", 5);
1160 my $sql_statement = "SELECT * FROM known_server WHERE hostname='$bus_address'";
1161 my $query_res = $known_server_db->select_dbentry( $sql_statement );
1162 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1163 my $bus_address = $hit->{hostname};
1164 my $bus_key = $hit->{hostkey};
1165 my $error = &send_msg_to_target($answer, $bus_address, $bus_key, $answer_header, $session_id);
1166 &update_jobdb_status_for_send_msgs($answer, $error);
1167 last;
1168 }
1169 }
1171 }
1173 # answer is for one specific host
1174 } else {
1175 # get encrypt_key
1176 my $encrypt_key = &get_encrypt_key($answer_target);
1177 if( not defined $encrypt_key ) {
1178 # unknown target, forward msg to bus
1179 daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1180 if( $bus_activ eq "true" ) {
1181 daemon_log("$session_id INFO: try to forward msg '$answer_header' to bus '$bus_address'", 5);
1182 my $sql_statement = "SELECT * FROM known_server WHERE hostname='$bus_address'";
1183 my $query_res = $known_server_db->select_dbentry( $sql_statement );
1184 my $res_length = keys( %{$query_res} );
1185 if( $res_length == 0 ){
1186 daemon_log("$session_id WARNING: send '$answer_header' to '$bus_address' failed, ".
1187 "no bus found in known_server", 3);
1188 }
1189 else {
1190 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1191 my $bus_key = $hit->{hostkey};
1192 my $error = &send_msg_to_target($answer, $bus_address, $bus_key, $answer_header,$session_id );
1193 &update_jobdb_status_for_send_msgs($answer, $error);
1194 }
1195 }
1196 }
1197 next;
1198 }
1199 my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1200 &update_jobdb_status_for_send_msgs($answer, $error);
1201 }
1202 }
1203 }
1204 }
1206 my $filter = POE::Filter::Reference->new();
1207 my %result = (
1208 status => "seems ok to me",
1209 answer => $client_answer,
1210 );
1212 my $output = $filter->put( [ \%result ] );
1213 print @$output;
1216 }
1219 sub trigger_db_loop {
1220 my ($kernel) = @_ ;
1221 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1222 $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay);
1223 }
1225 sub watch_for_done_jobs {
1226 my ($kernel,$heap) = @_[KERNEL, HEAP];
1228 my $sql_statement = "SELECT * FROM ".$job_queue_tn.
1229 " WHERE status='done'";
1230 my $res = $job_db->select_dbentry( $sql_statement );
1232 while( my ($id, $hit) = each %{$res} ) {
1233 my $jobdb_id = $hit->{id};
1234 my $sql_statement = "DELETE FROM $job_queue_tn WHERE id='$jobdb_id'";
1235 my $res = $job_db->del_dbentry($sql_statement);
1236 }
1238 $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1239 }
1241 sub watch_for_new_jobs {
1242 my ($kernel,$heap) = @_[KERNEL, HEAP];
1244 # check gosa job queue for jobs with executable timestamp
1245 my $timestamp = &get_time();
1246 my $sql_statement = "SELECT * FROM ".$job_queue_tn.
1247 " WHERE status='waiting' AND timestamp<'$timestamp'";
1248 my $res = $job_db->select_dbentry( $sql_statement );
1250 while( my ($id, $hit) = each %{$res} ) {
1251 my $jobdb_id = $hit->{id};
1252 my $macaddress = $hit->{'macaddress'};
1253 my $job_msg = $hit->{'xmlmessage'};
1254 daemon_log("J DEBUG: its time to execute $job_msg", 7);
1255 my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1256 my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1257 # expect macaddress is unique!!!!!!
1258 my $target = $res_hash->{1}->{hostname};
1260 # if (not defined $target) {
1261 # &daemon_log("ERROR: no host found for mac address: $macaddress", 1);
1262 # &daemon_log("$hit->{xmlmessage}", 8);
1263 # my $sql_statement = "UPDATE $job_queue_tn ".
1264 # "SET status='error', result='no host found for mac address' ".
1265 # "WHERE id='$jobdb_id'";
1266 # my $res = $job_db->update_dbentry($sql_statement);
1267 # next;
1268 # }
1269 #
1270 # change header
1271 $job_msg =~ s/<header>job_/<header>gosa_/;
1273 # add sqlite_id
1274 $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1276 $job_msg =~ /<header>(\S+)<\/header>/;
1277 my $header = $1 ;
1278 my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");
1280 # update status in job queue to 'processing'
1281 $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id='$jobdb_id'";
1282 my $res = $job_db->update_dbentry($sql_statement);
1283 }
1285 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1286 }
1289 sub refresh_ldap_handle {
1290 my $mesg;
1292 daemon_log("DEBUG: Trying to create a connection to URI $ldap_uri", 5);
1293 # Get an ldap handle, if we don't have one
1294 if( ! defined $ldap_handle ){
1295 $ldap_handle = Net::LDAP->new( $ldap_uri );
1296 }
1297 # Still not defined?
1298 if( ! defined $ldap_handle ) {
1299 daemon_log( "ch $$: Net::LDAP constructor failed: $!\n" );
1300 return 0;
1301 }
1303 # Bind to ldap server - eventually authenticate
1304 if( defined $ldap_admin_dn ) {
1305 if( defined $ldap_admin_password ) {
1306 $mesg = $ldap_handle->bind( $ldap_admin_dn, password => $ldap_admin_password );
1307 } else {
1308 $mesg = $ldap_handle->bind( $ldap_admin_dn );
1309 }
1310 } else {
1311 $mesg = $ldap_handle->bind();
1312 }
1314 if( 0 != $mesg->code ) {
1315 undef( $ldap_handle ) if( 81 == $mesg->code );
1316 daemon_log( "ch $$: LDAP bind: error (". $mesg->code . ') - ' . $mesg->error . "\n", 1);
1317 return 0;
1318 }
1320 return 1;
1321 }
1324 sub change_fai_state {
1325 my ($st, $targets, $session_id) = @_;
1326 $session_id = 0 if not defined $session_id;
1327 # Set FAI state to localboot
1328 my %mapActions= (
1329 reboot => '',
1330 update => 'softupdate',
1331 localboot => 'localboot',
1332 reinstall => 'install',
1333 rescan => '',
1334 wake => '',
1335 memcheck => 'memcheck',
1336 sysinfo => 'sysinfo',
1337 install => 'install',
1338 );
1340 # Return if this is unknown
1341 if (!exists $mapActions{ $st }){
1342 daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1);
1343 return;
1344 }
1346 my $state= $mapActions{ $st };
1348 &refresh_ldap_handle();
1349 if( defined($ldap_handle) ) {
1351 # Build search filter for hosts
1352 my $search= "(&(objectClass=GOhard)";
1353 foreach (@{$targets}){
1354 $search.= "(macAddress=$_)";
1355 }
1356 $search.= ")";
1358 # If there's any host inside of the search string, procress them
1359 if (!($search =~ /macAddress/)){
1360 daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);
1361 return;
1362 }
1364 # Perform search for Unit Tag
1365 my $mesg = $ldap_handle->search(
1366 base => $ldap_base,
1367 scope => 'sub',
1368 attrs => ['dn', 'FAIstate', 'objectClass'],
1369 filter => "$search"
1370 );
1372 if ($mesg->count) {
1373 my @entries = $mesg->entries;
1374 foreach my $entry (@entries) {
1375 # Only modify entry if it is not set to '$state'
1376 if ($entry->get_value("FAIstate") ne "$state"){
1377 daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
1378 my $result;
1379 my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
1380 if (exists $tmp{'FAIobject'}){
1381 if ($state eq ''){
1382 $result= $ldap_handle->modify($entry->dn, changes => [
1383 delete => [ FAIstate => [] ] ]);
1384 } else {
1385 $result= $ldap_handle->modify($entry->dn, changes => [
1386 replace => [ FAIstate => $state ] ]);
1387 }
1388 } elsif ($state ne ''){
1389 $result= $ldap_handle->modify($entry->dn, changes => [
1390 add => [ objectClass => 'FAIobject' ],
1391 add => [ FAIstate => $state ] ]);
1392 }
1394 # Errors?
1395 if ($result->code){
1396 daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
1397 }
1399 } else {
1400 daemon_log("$session_id DEBUG FAIstate at host found with filter statement '$search' already at state '$st'", 7);
1401 }
1402 }
1403 }
1404 # if no ldap handle defined
1405 } else {
1406 daemon_log("$session_id ERROR: no LDAP handle defined for update FAIstate", 1);
1407 }
1408 }
1411 sub change_goto_state {
1412 my ($st, $targets, $session_id) = @_;
1413 $session_id = 0 if not defined $session_id;
1415 # Switch on or off?
1416 my $state= $st eq 'active' ? 'active': 'locked';
1418 &refresh_ldap_handle();
1419 if( defined($ldap_handle) ) {
1421 # Build search filter for hosts
1422 my $search= "(&(objectClass=GOhard)";
1423 foreach (@{$targets}){
1424 $search.= "(macAddress=$_)";
1425 }
1426 $search.= ")";
1428 # If there's any host inside of the search string, procress them
1429 if (!($search =~ /macAddress/)){
1430 return;
1431 }
1433 # Perform search for Unit Tag
1434 my $mesg = $ldap_handle->search(
1435 base => $ldap_base,
1436 scope => 'sub',
1437 attrs => ['dn', 'gotoMode'],
1438 filter => "$search"
1439 );
1441 if ($mesg->count) {
1442 my @entries = $mesg->entries;
1443 foreach my $entry (@entries) {
1445 # Only modify entry if it is not set to '$state'
1446 if ($entry->get_value("gotoMode") ne $state){
1448 daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
1449 my $result;
1450 $result= $ldap_handle->modify($entry->dn, changes => [
1451 replace => [ gotoMode => $state ] ]);
1453 # Errors?
1454 if ($result->code){
1455 &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
1456 }
1458 }
1459 }
1460 }
1462 }
1463 }
1466 sub create_fai_server_db {
1467 my ($table_name, $kernel) = @_;
1468 my $result;
1470 if(defined($ldap_handle)) {
1471 daemon_log("INFO: create_fai_server_db: start", 5);
1472 my $mesg= $ldap_handle->search(
1473 base => $ldap_base,
1474 scope => 'sub',
1475 attrs => ['FAIrepository', 'gosaUnitTag'],
1476 filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
1477 );
1478 if($mesg->{'resultCode'} == 0 &&
1479 $mesg->count != 0) {
1480 foreach my $entry (@{$mesg->{entries}}) {
1481 if($entry->exists('FAIrepository')) {
1482 # Add an entry for each Repository configured for server
1483 foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
1484 my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
1485 my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
1486 $result= $fai_server_db->add_dbentry( {
1487 table => $table_name,
1488 primkey => ['server', 'release', 'tag'],
1489 server => $tmp_url,
1490 release => $tmp_release,
1491 sections => $tmp_sections,
1492 tag => (length($tmp_tag)>0)?$tmp_tag:"",
1493 } );
1494 }
1495 }
1496 }
1497 }
1498 daemon_log("INFO: create_fai_server_db: finished", 5);
1500 # TODO: Find a way to post the 'create_packages_list_db' event
1501 &create_packages_list_db();
1502 }
1504 return $result;
1505 }
1507 sub run_create_fai_server_db {
1508 my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
1509 my $task = POE::Wheel::Run->new(
1510 Program => sub { &create_fai_server_db($table_name,$kernel) },
1511 StdoutEvent => "session_run_result",
1512 StderrEvent => "session_run_debug",
1513 CloseEvent => "session_run_done",
1514 );
1516 $heap->{task}->{ $task->ID } = $task;
1517 return;
1518 }
1521 sub create_fai_release_db {
1522 my ($table_name) = @_;
1523 my $result;
1525 if(defined($ldap_handle)) {
1526 daemon_log("INFO: create_fai_release_db: start",5);
1527 my $mesg= $ldap_handle->search(
1528 base => $ldap_base,
1529 scope => 'sub',
1530 attrs => [],
1531 filter => "(&(objectClass=organizationalUnit)(ou=fai))",
1532 );
1533 if($mesg->{'resultCode'} == 0 &&
1534 $mesg->count != 0) {
1535 # Walk through all possible FAI container ou's
1536 my @sql_list;
1537 my $timestamp= &get_time();
1538 foreach my $ou (@{$mesg->{entries}}) {
1539 my $tmp_classes= resolve_fai_classes($ou->dn);
1540 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
1541 my @tmp_array=get_fai_release_entries($tmp_classes);
1542 if(@tmp_array) {
1543 foreach my $entry (@tmp_array) {
1544 if(defined($entry) && ref($entry) eq 'HASH') {
1545 my $sql=
1546 "INSERT INTO $table_name "
1547 ."(timestamp, release, class, type, state) VALUES ("
1548 .$timestamp.","
1549 ."'".$entry->{'release'}."',"
1550 ."'".$entry->{'class'}."',"
1551 ."'".$entry->{'type'}."',"
1552 ."'".$entry->{'state'}."')";
1553 push @sql_list, $sql;
1554 }
1555 }
1556 }
1557 }
1558 }
1559 daemon_log("DEBUG: Inserting ".scalar @sql_list." entries to DB",6);
1560 if(@sql_list) {
1561 unshift @sql_list, "DELETE FROM $table_name";
1562 $fai_server_db->exec_statementlist(\@sql_list);
1563 }
1564 daemon_log("DEBUG: Done with inserting",6);
1565 }
1566 daemon_log("INFO: create_fai_release_db: finished",5);
1567 }
1569 return $result;
1570 }
1571 sub run_create_fai_release_db {
1572 my ($session, $heap, $table_name) = @_[SESSION, HEAP, ARG0];
1573 my $task = POE::Wheel::Run->new(
1574 Program => sub { &create_fai_release_db($table_name) },
1575 StdoutEvent => "session_run_result",
1576 StderrEvent => "session_run_debug",
1577 CloseEvent => "session_run_done",
1578 );
1580 $heap->{task}->{ $task->ID } = $task;
1581 return;
1582 }
1584 sub get_fai_types {
1585 my $tmp_classes = shift || return undef;
1586 my @result;
1588 foreach my $type(keys %{$tmp_classes}) {
1589 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
1590 my $entry = {
1591 type => $type,
1592 state => $tmp_classes->{$type}[0],
1593 };
1594 push @result, $entry;
1595 }
1596 }
1598 return @result;
1599 }
1601 sub get_fai_state {
1602 my $result = "";
1603 my $tmp_classes = shift || return $result;
1605 foreach my $type(keys %{$tmp_classes}) {
1606 if(defined($tmp_classes->{$type}[0])) {
1607 $result = $tmp_classes->{$type}[0];
1609 # State is equal for all types in class
1610 last;
1611 }
1612 }
1614 return $result;
1615 }
1617 sub resolve_fai_classes {
1618 my $result;
1619 my $fai_base= shift;
1620 my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
1621 my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
1622 my $fai_classes;
1624 daemon_log("DEBUG: Searching for FAI entries in base $fai_base",6);
1625 my $mesg= $ldap_handle->search(
1626 base => $fai_base,
1627 scope => 'sub',
1628 attrs => ['cn','objectClass','FAIstate'],
1629 filter => $fai_filter,
1630 );
1631 daemon_log("DEBUG: Found ".$mesg->count()." FAI entries",6);
1633 if($mesg->{'resultCode'} == 0 &&
1634 $mesg->count != 0) {
1635 foreach my $entry (@{$mesg->{entries}}) {
1636 if($entry->exists('cn')) {
1637 my $tmp_dn= $entry->dn();
1639 # Skip classname and ou dn parts for class
1640 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?),$fai_base$/;
1642 # Skip classes without releases
1643 if((!defined($tmp_release)) || length($tmp_release)==0) {
1644 next;
1645 }
1647 my $tmp_cn= $entry->get_value('cn');
1648 my $tmp_state= $entry->get_value('FAIstate');
1650 my $tmp_type;
1651 # Get FAI type
1652 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
1653 if(grep $_ eq $oclass, @possible_fai_classes) {
1654 $tmp_type= $oclass;
1655 last;
1656 }
1657 }
1659 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
1660 # A Subrelease
1661 my @sub_releases = split(/,/, $tmp_release);
1663 # Walk through subreleases and build hash tree
1664 my $hash;
1665 while(my $tmp_sub_release = pop @sub_releases) {
1666 $hash .= "\{'$tmp_sub_release'\}->";
1667 }
1668 eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
1669 } else {
1670 # A branch, no subrelease
1671 push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
1672 }
1673 } elsif (!$entry->exists('cn')) {
1674 my $tmp_dn= $entry->dn();
1675 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?),$fai_base$/;
1677 # Skip classes without releases
1678 if((!defined($tmp_release)) || length($tmp_release)==0) {
1679 next;
1680 }
1682 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
1683 # A Subrelease
1684 my @sub_releases= split(/,/, $tmp_release);
1686 # Walk through subreleases and build hash tree
1687 my $hash;
1688 while(my $tmp_sub_release = pop @sub_releases) {
1689 $hash .= "\{'$tmp_sub_release'\}->";
1690 }
1691 # Remove the last two characters
1692 chop($hash);
1693 chop($hash);
1695 eval('$fai_classes->'.$hash.'= {}');
1696 } else {
1697 # A branch, no subrelease
1698 if(!exists($fai_classes->{$tmp_release})) {
1699 $fai_classes->{$tmp_release} = {};
1700 }
1701 }
1702 }
1703 }
1705 # The hash is complete, now we can honor the copy-on-write based missing entries
1706 foreach my $release (keys %$fai_classes) {
1707 $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
1708 }
1709 }
1710 return $result;
1711 }
1713 sub apply_fai_inheritance {
1714 my $fai_classes = shift || return {};
1715 my $tmp_classes;
1717 # Get the classes from the branch
1718 foreach my $class (keys %{$fai_classes}) {
1719 # Skip subreleases
1720 if($class =~ /^ou=.*$/) {
1721 next;
1722 } else {
1723 $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
1724 }
1725 }
1727 # Apply to each subrelease
1728 foreach my $subrelease (keys %{$fai_classes}) {
1729 if($subrelease =~ /ou=/) {
1730 foreach my $tmp_class (keys %{$tmp_classes}) {
1731 if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
1732 $fai_classes->{$subrelease}->{$tmp_class} =
1733 deep_copy($tmp_classes->{$tmp_class});
1734 } else {
1735 foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
1736 if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
1737 $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
1738 deep_copy($tmp_classes->{$tmp_class}->{$type});
1739 }
1740 }
1741 }
1742 }
1743 }
1744 }
1746 # Find subreleases in deeper levels
1747 foreach my $subrelease (keys %{$fai_classes}) {
1748 if($subrelease =~ /ou=/) {
1749 foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
1750 if($subsubrelease =~ /ou=/) {
1751 apply_fai_inheritance($fai_classes->{$subrelease});
1752 }
1753 }
1754 }
1755 }
1757 return $fai_classes;
1758 }
1760 sub get_fai_release_entries {
1761 my $tmp_classes = shift || return;
1762 my $parent = shift || "";
1763 my @result = shift || ();
1765 foreach my $entry (keys %{$tmp_classes}) {
1766 if(defined($entry)) {
1767 if($entry =~ /^ou=.*$/) {
1768 my $release_name = $entry;
1769 $release_name =~ s/ou=//g;
1770 if(length($parent)>0) {
1771 $release_name = $parent."/".$release_name;
1772 }
1773 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
1774 foreach my $bufentry(@bufentries) {
1775 push @result, $bufentry;
1776 }
1777 } else {
1778 my @types = get_fai_types($tmp_classes->{$entry});
1779 foreach my $type (@types) {
1780 push @result,
1781 {
1782 'class' => $entry,
1783 'type' => $type->{'type'},
1784 'release' => $parent,
1785 'state' => $type->{'state'},
1786 };
1787 }
1788 }
1789 }
1790 }
1792 return @result;
1793 }
1795 sub deep_copy {
1796 my $this = shift;
1797 if (not ref $this) {
1798 $this;
1799 } elsif (ref $this eq "ARRAY") {
1800 [map deep_copy($_), @$this];
1801 } elsif (ref $this eq "HASH") {
1802 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
1803 } else { die "what type is $_?" }
1804 }
1807 sub session_run_result {
1808 my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];
1809 $kernel->sig(CHLD => "child_reap");
1810 }
1812 sub session_run_debug {
1813 my $result = $_[ARG0];
1814 print STDERR "$result\n";
1815 }
1817 sub session_run_done {
1818 my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1819 delete $heap->{task}->{$task_id};
1820 }
1822 sub create_sources_list {
1823 my $result="/tmp/gosa_si_tmp_sources_list";
1825 # Remove old file
1826 if(stat($result)) {
1827 unlink($result);
1828 }
1830 my $fh;
1831 open($fh, ">$result") or return undef;
1832 if(defined($ldap_server_dn) and length($ldap_server_dn) > 0) {
1833 my $mesg=$ldap_handle->search(
1834 base => $ldap_server_dn,
1835 scope => 'base',
1836 attrs => 'FAIrepository',
1837 filter => 'objectClass=FAIrepositoryServer'
1838 );
1839 if($mesg->count) {
1840 foreach my $entry(@{$mesg->{'entries'}}) {
1841 my ($server, $tag, $release, $sections)= split /\|/, $entry->get_value('FAIrepository');
1842 my $line = "deb $server $release";
1843 $sections =~ s/,/ /g;
1844 $line.= " $sections";
1845 print $fh $line."\n";
1846 }
1847 }
1848 }
1849 close($fh);
1851 return $result;
1852 }
1854 sub create_packages_list_db {
1855 my ($sources_file) = @_ || &create_sources_list;
1856 my $line;
1857 daemon_log("INFO: create_packages_list_db: start", 5);
1859 open(CONFIG, "<$sources_file") or do {
1860 daemon_log( "ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
1861 return;
1862 };
1864 # Read lines
1865 while ($line = <CONFIG>){
1866 # Unify
1867 chop($line);
1868 $line =~ s/^\s+//;
1869 $line =~ s/^\s+/ /;
1871 # Strip comments
1872 $line =~ s/#.*$//g;
1874 # Skip empty lines
1875 if ($line =~ /^\s*$/){
1876 next;
1877 }
1879 # Interpret deb line
1880 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
1881 my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
1882 my $section;
1883 foreach $section (split(' ', $sections)){
1884 &parse_package_info( $baseurl, $dist, $section );
1885 }
1886 }
1887 }
1889 close (CONFIG);
1891 daemon_log("INFO: create_packages_list_db: finished", 5);
1892 return;
1893 }
1895 sub run_create_packages_list_db {
1896 my ($session, $heap) = @_[SESSION, HEAP];
1897 my $task = POE::Wheel::Run->new(
1898 Program => sub {&create_packages_list_db},
1899 StdoutEvent => "session_run_result",
1900 StderrEvent => "session_run_debug",
1901 CloseEvent => "session_run_done",
1902 );
1903 $heap->{task}->{ $task->ID } = $task;
1904 }
1906 sub parse_package_info {
1907 my ($baseurl, $dist, $section)= @_;
1908 my ($package);
1910 my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
1911 $repo_dirs{ "${repo_path}/pool" } = 1;
1913 foreach $package ("Packages.gz"){
1914 daemon_log("DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
1915 get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section" );
1916 parse_package( "$outdir/$dist/$section", $dist, $path );
1917 }
1918 find(\&cleanup_and_extract, keys( %repo_dirs ) );
1919 }
1921 sub get_package {
1922 my ($url, $dest)= @_;
1924 my $tpath = dirname($dest);
1925 -d "$tpath" || mkpath "$tpath";
1927 # This is ugly, but I've no time to take a look at "how it works in perl"
1928 if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
1929 system("gunzip -cd '$dest' > '$dest.in'");
1930 unlink($dest);
1931 } else {
1932 daemon_log("ERROR: create_packages_list_db: get_packages: fetching '$url' failed!", 1);
1933 }
1934 return 0;
1935 }
1937 sub parse_package {
1938 my ($path, $dist, $srv_path)= @_;
1939 my ($package, $version, $section, $description);
1940 my @sql_list;
1941 my $PACKAGES;
1943 if(not stat("$path.in")) {
1944 daemon_log("ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
1945 return;
1946 }
1948 open($PACKAGES, "<$path.in");
1949 if(not defined($PACKAGES)) {
1950 daemon_log("ERROR: create_packages_list_db: parse_package: can not open '$path.in'",1);
1951 return;
1952 }
1954 # Read lines
1955 while (<$PACKAGES>){
1956 my $line = $_;
1957 # Unify
1958 chop($line);
1960 # Use empty lines as a trigger
1961 if ($line =~ /^\s*$/){
1962 my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '', 'none', '0')";
1963 push(@sql_list, $sql);
1964 $package = "none";
1965 $version = "none";
1966 $section = "none";
1967 $description = "none";
1968 next;
1969 }
1971 # Trigger for package name
1972 if ($line =~ /^Package:\s/){
1973 ($package)= ($line =~ /^Package: (.*)$/);
1974 next;
1975 }
1977 # Trigger for version
1978 if ($line =~ /^Version:\s/){
1979 ($version)= ($line =~ /^Version: (.*)$/);
1980 next;
1981 }
1983 # Trigger for description
1984 if ($line =~ /^Description:\s/){
1985 ($description)= ($line =~ /^Description: (.*)$/);
1986 next;
1987 }
1989 # Trigger for section
1990 if ($line =~ /^Section:\s/){
1991 ($section)= ($line =~ /^Section: (.*)$/);
1992 next;
1993 }
1995 # Trigger for filename
1996 if ($line =~ /^Filename:\s/){
1997 my ($filename) = ($line =~ /^Filename: (.*)$/);
1998 store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
1999 next;
2000 }
2001 }
2003 close( $PACKAGES );
2004 unlink( "$path.in" );
2006 $packages_list_db->exec_statementlist(\@sql_list);
2007 }
2009 sub store_fileinfo {
2010 my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
2012 my %fileinfo = (
2013 'package' => $package,
2014 'dist' => $dist,
2015 'version' => $vers,
2016 );
2018 $repo_files{ "${srvdir}/$file" } = \%fileinfo;
2019 }
2021 sub cleanup_and_extract {
2022 my $fileinfo = $repo_files{ $File::Find::name };
2024 if( defined $fileinfo ) {
2026 my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
2027 my $sql;
2028 my $package = $fileinfo->{ 'package' };
2029 my $newver = $fileinfo->{ 'version' };
2031 mkpath($dir);
2032 system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
2034 if( -f "$dir/DEBIAN/templates" ) {
2036 daemon_log("DEBUG: Found debconf templates in '$package' - $newver", 5);
2038 my $tmpl= "";
2039 {
2040 local $/=undef;
2041 open FILE, "$dir/DEBIAN/templates";
2042 $tmpl = &encode_base64(<FILE>);
2043 close FILE;
2044 }
2045 rmtree("$dir/DEBIAN/templates");
2047 $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
2049 } else {
2050 $sql= "update $main::packages_list_tn set template = '' where package = '$package' and version = '$newver';";
2051 }
2053 my $res= $main::packages_list_db->update_dbentry($sql);
2054 }
2055 }
2058 #==== MAIN = main ==============================================================
2059 # parse commandline options
2060 Getopt::Long::Configure( "bundling" );
2061 GetOptions("h|help" => \&usage,
2062 "c|config=s" => \$cfg_file,
2063 "f|foreground" => \$foreground,
2064 "v|verbose+" => \$verbose,
2065 "no-bus+" => \$no_bus,
2066 "no-arp+" => \$no_arp,
2067 );
2069 # read and set config parameters
2070 &check_cmdline_param ;
2071 &read_configfile;
2072 &check_pid;
2074 $SIG{CHLD} = 'IGNORE';
2076 # forward error messages to logfile
2077 if( ! $foreground ) {
2078 open( STDIN, '+>/dev/null' );
2079 open( STDOUT, '+>&STDIN' );
2080 open( STDERR, '+>&STDIN' );
2081 }
2083 # Just fork, if we are not in foreground mode
2084 if( ! $foreground ) {
2085 chdir '/' or die "Can't chdir to /: $!";
2086 $pid = fork;
2087 setsid or die "Can't start a new session: $!";
2088 umask 0;
2089 } else {
2090 $pid = $$;
2091 }
2093 # Do something useful - put our PID into the pid_file
2094 if( 0 != $pid ) {
2095 open( LOCK_FILE, ">$pid_file" );
2096 print LOCK_FILE "$pid\n";
2097 close( LOCK_FILE );
2098 if( !$foreground ) {
2099 exit( 0 )
2100 };
2101 }
2103 daemon_log(" ", 1);
2104 daemon_log("$0 started!", 1);
2106 if ($no_bus > 0) {
2107 $bus_activ = "false"
2108 }
2112 # delete old DBsqlite lock files
2113 #unlink('/tmp/gosa_si_lock*');
2115 # connect to gosa-si job queue
2116 $job_db = GOSA::DBsqlite->new($job_queue_file_name);
2117 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
2119 # connect to known_clients_db
2120 $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
2121 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
2123 # connect to known_server_db
2124 $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
2125 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
2127 # connect to login_usr_db
2128 $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
2129 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
2131 # connect to fai_server_db and fai_release_db
2132 $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
2133 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
2134 $fai_server_db->create_table($fai_release_tn, \@fai_release_col_names);
2136 # connect to packages_list_db
2137 unlink($packages_list_file_name);
2138 $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
2139 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
2141 # connect to messaging_db
2142 $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
2143 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
2146 # create xml object used for en/decrypting
2147 $xml = new XML::Simple();
2149 # create socket for incoming xml messages
2151 POE::Component::Server::TCP->new(
2152 Port => $server_port,
2153 ClientInput => sub {
2154 my ($kernel, $input) = @_[KERNEL, ARG0];
2155 push(@tasks, $input);
2156 $kernel->yield("next_task");
2157 },
2158 InlineStates => {
2159 next_task => \&next_task,
2160 task_result => \&handle_task_result,
2161 task_done => \&handle_task_done,
2162 task_debug => \&handle_task_debug,
2163 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!" },
2164 }
2165 );
2167 daemon_log("start socket for incoming xml messages at port '$server_port' ", 1);
2169 # create session for repeatedly checking the job queue for jobs
2170 POE::Session->create(
2171 inline_states => {
2172 _start => \&_start,
2173 sig_handler => \&sig_handler,
2174 watch_for_new_jobs => \&watch_for_new_jobs,
2175 watch_for_done_jobs => \&watch_for_done_jobs,
2176 create_packages_list_db => \&run_create_packages_list_db,
2177 create_fai_server_db => \&run_create_fai_server_db,
2178 create_fai_release_db => \&run_create_fai_release_db,
2179 session_run_result => \&session_run_result,
2180 session_run_debug => \&session_run_debug,
2181 session_run_done => \&session_run_done,
2182 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!" },
2183 }
2184 );
2187 # import all modules
2188 &import_modules;
2190 # check wether all modules are gosa-si valid passwd check
2192 POE::Kernel->run();
2193 exit;