e69d6e620c5469888e7b95801477d075b63764a2
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].
1063 "' from '".$heap->{'remote_ip'}."'", 5);
1064 daemon_log("$session_id DEBUG: Processing module ".$module, 7);
1065 $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1067 if ( 0 < @{$answer_l} ) {
1068 my $answer_str = join("\n", @{$answer_l});
1069 daemon_log("$session_id DEBUG: $module: Got answer from module: \n".$answer_str,8);
1070 }
1071 }
1072 if( !$answer_l ) { $error++ };
1074 ########
1075 # answer
1076 if( $error == 0 ) {
1078 foreach my $answer ( @{$answer_l} ) {
1079 # for each answer in answer list
1081 # check outgoing msg to xml validity
1082 my $answer_hash = &check_outgoing_xml_validity($answer);
1083 if( not defined $answer_hash ) {
1084 next;
1085 }
1087 $answer_header = @{$answer_hash->{'header'}}[0];
1088 @answer_target_l = @{$answer_hash->{'target'}};
1089 $answer_source = @{$answer_hash->{'source'}}[0];
1091 # deliver msg to all targets
1092 foreach my $answer_target ( @answer_target_l ) {
1094 # targets of msg are all gosa-si-clients in known_clients_db
1095 if( $answer_target eq "*" ) {
1096 # answer is for all clients
1097 my $sql_statement= "SELECT * FROM known_clients";
1098 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1099 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1100 my $host_name = $hit->{hostname};
1101 my $host_key = $hit->{hostkey};
1102 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1103 &update_jobdb_status_for_send_msgs($answer, $error);
1104 }
1105 }
1107 # targets of msg are all gosa-si-server in known_server_db
1108 elsif( $answer_target eq "KNOWN_SERVER" ) {
1109 # answer is for all server in known_server
1110 my $sql_statement= "SELECT * FROM known_server";
1111 my $query_res = $known_server_db->select_dbentry( $sql_statement );
1112 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1113 my $host_name = $hit->{hostname};
1114 my $host_key = $hit->{hostkey};
1115 $answer =~ s/KNOWN_SERVER/$host_name/g;
1116 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1117 &update_jobdb_status_for_send_msgs($answer, $error);
1118 }
1119 }
1121 # target of msg is GOsa
1122 elsif( $answer_target eq "GOSA" ) {
1123 my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1124 my $add_on = "";
1125 if( defined $session_id ) {
1126 $add_on = ".session_id=$session_id";
1127 }
1128 # answer is for GOSA and has to returned to connected client
1129 my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1130 $client_answer = $gosa_answer.$add_on;
1131 }
1133 # target of msg is job queue at this host
1134 elsif( $answer_target eq "JOBDB") {
1135 $answer =~ /<header>(\S+)<\/header>/;
1136 my $header;
1137 if( defined $1 ) { $header = $1; }
1138 my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1139 &update_jobdb_status_for_send_msgs($answer, $error);
1140 }
1142 # target of msg is a mac address
1143 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 ) {
1144 daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients", 5);
1145 my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1146 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1147 my $found_ip_flag = 0;
1148 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1149 my $host_name = $hit->{hostname};
1150 my $host_key = $hit->{hostkey};
1151 $answer =~ s/$answer_target/$host_name/g;
1152 daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1153 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1154 &update_jobdb_status_for_send_msgs($answer, $error);
1155 $found_ip_flag++ ;
1156 }
1157 if( $found_ip_flag == 0) {
1158 daemon_log("$session_id WARNING: no host found in known_clients with mac address '$answer_target'", 3);
1159 if( $bus_activ eq "true" ) {
1160 daemon_log("$session_id INFO: try to forward msg '$answer_header' to bus '$bus_address'", 5);
1161 my $sql_statement = "SELECT * FROM known_server WHERE hostname='$bus_address'";
1162 my $query_res = $known_server_db->select_dbentry( $sql_statement );
1163 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1164 my $bus_address = $hit->{hostname};
1165 my $bus_key = $hit->{hostkey};
1166 my $error = &send_msg_to_target($answer, $bus_address, $bus_key, $answer_header, $session_id);
1167 &update_jobdb_status_for_send_msgs($answer, $error);
1168 last;
1169 }
1170 }
1172 }
1174 # answer is for one specific host
1175 } else {
1176 # get encrypt_key
1177 my $encrypt_key = &get_encrypt_key($answer_target);
1178 if( not defined $encrypt_key ) {
1179 # unknown target, forward msg to bus
1180 daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1181 if( $bus_activ eq "true" ) {
1182 daemon_log("$session_id INFO: try to forward msg '$answer_header' to bus '$bus_address'", 5);
1183 my $sql_statement = "SELECT * FROM known_server WHERE hostname='$bus_address'";
1184 my $query_res = $known_server_db->select_dbentry( $sql_statement );
1185 my $res_length = keys( %{$query_res} );
1186 if( $res_length == 0 ){
1187 daemon_log("$session_id WARNING: send '$answer_header' to '$bus_address' failed, ".
1188 "no bus found in known_server", 3);
1189 }
1190 else {
1191 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1192 my $bus_key = $hit->{hostkey};
1193 my $error = &send_msg_to_target($answer, $bus_address, $bus_key, $answer_header,$session_id );
1194 &update_jobdb_status_for_send_msgs($answer, $error);
1195 }
1196 }
1197 }
1198 next;
1199 }
1200 my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1201 &update_jobdb_status_for_send_msgs($answer, $error);
1202 }
1203 }
1204 }
1205 }
1207 my $filter = POE::Filter::Reference->new();
1208 my %result = (
1209 status => "seems ok to me",
1210 answer => $client_answer,
1211 );
1213 my $output = $filter->put( [ \%result ] );
1214 print @$output;
1217 }
1220 sub trigger_db_loop {
1221 my ($kernel) = @_ ;
1222 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1223 $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay);
1224 }
1226 sub watch_for_done_jobs {
1227 my ($kernel,$heap) = @_[KERNEL, HEAP];
1229 my $sql_statement = "SELECT * FROM ".$job_queue_tn.
1230 " WHERE status='done'";
1231 my $res = $job_db->select_dbentry( $sql_statement );
1233 while( my ($id, $hit) = each %{$res} ) {
1234 my $jobdb_id = $hit->{id};
1235 my $sql_statement = "DELETE FROM $job_queue_tn WHERE id='$jobdb_id'";
1236 my $res = $job_db->del_dbentry($sql_statement);
1237 }
1239 $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1240 }
1242 sub watch_for_new_jobs {
1243 my ($kernel,$heap) = @_[KERNEL, HEAP];
1245 # check gosa job queue for jobs with executable timestamp
1246 my $timestamp = &get_time();
1247 my $sql_statement = "SELECT * FROM ".$job_queue_tn.
1248 " WHERE status='waiting' AND timestamp<'$timestamp'";
1249 my $res = $job_db->select_dbentry( $sql_statement );
1251 while( my ($id, $hit) = each %{$res} ) {
1252 my $jobdb_id = $hit->{id};
1253 my $macaddress = $hit->{'macaddress'};
1254 my $job_msg = $hit->{'xmlmessage'};
1255 daemon_log("J DEBUG: its time to execute $job_msg", 7);
1256 my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1257 my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1258 # expect macaddress is unique!!!!!!
1259 my $target = $res_hash->{1}->{hostname};
1261 # if (not defined $target) {
1262 # &daemon_log("ERROR: no host found for mac address: $macaddress", 1);
1263 # &daemon_log("$hit->{xmlmessage}", 8);
1264 # my $sql_statement = "UPDATE $job_queue_tn ".
1265 # "SET status='error', result='no host found for mac address' ".
1266 # "WHERE id='$jobdb_id'";
1267 # my $res = $job_db->update_dbentry($sql_statement);
1268 # next;
1269 # }
1270 #
1271 # change header
1272 $job_msg =~ s/<header>job_/<header>gosa_/;
1274 # add sqlite_id
1275 $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1277 $job_msg =~ /<header>(\S+)<\/header>/;
1278 my $header = $1 ;
1279 my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");
1281 # update status in job queue to 'processing'
1282 $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id='$jobdb_id'";
1283 my $res = $job_db->update_dbentry($sql_statement);
1284 }
1286 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1287 }
1290 sub refresh_ldap_handle {
1291 my ($session_id) = @_ ;
1292 if (not defined $session_id) { $session_id = 0; }
1294 my $mesg;
1296 daemon_log("$session_id DEBUG: Trying to create a connection to URI '$ldap_uri'", 7);
1297 # Get an ldap handle, if we don't have one
1298 if( ! defined $ldap_handle ){
1299 $ldap_handle = Net::LDAP->new( $ldap_uri );
1300 }
1301 # Still not defined?
1302 if( ! defined $ldap_handle ) {
1303 daemon_log( "$session_id ERROR: ch $$: Net::LDAP constructor failed: $!\n" );
1304 return 0;
1305 }
1307 # Bind to ldap server - eventually authenticate
1308 if( defined $ldap_admin_dn ) {
1309 if( defined $ldap_admin_password ) {
1310 $mesg = $ldap_handle->bind( $ldap_admin_dn, password => $ldap_admin_password );
1311 } else {
1312 $mesg = $ldap_handle->bind( $ldap_admin_dn );
1313 }
1314 } else {
1315 $mesg = $ldap_handle->bind();
1316 }
1318 if( 0 != $mesg->code ) {
1319 undef( $ldap_handle ) if( 81 == $mesg->code );
1320 daemon_log( "$session_id ERROR: ch $$: LDAP bind: error (". $mesg->code . ') - ' . $mesg->error . "\n", 1);
1321 return 0;
1322 }
1323 daemon_log("$session_id DEBUG: create a new connection to URI '$ldap_uri'", 7);
1324 return 1;
1325 }
1328 sub change_fai_state {
1329 my ($st, $targets, $session_id) = @_;
1330 $session_id = 0 if not defined $session_id;
1331 # Set FAI state to localboot
1332 my %mapActions= (
1333 reboot => '',
1334 update => 'softupdate',
1335 localboot => 'localboot',
1336 reinstall => 'install',
1337 rescan => '',
1338 wake => '',
1339 memcheck => 'memcheck',
1340 sysinfo => 'sysinfo',
1341 install => 'install',
1342 );
1344 # Return if this is unknown
1345 if (!exists $mapActions{ $st }){
1346 daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1);
1347 return;
1348 }
1350 my $state= $mapActions{ $st };
1352 &refresh_ldap_handle();
1353 if( defined($ldap_handle) ) {
1355 # Build search filter for hosts
1356 my $search= "(&(objectClass=GOhard)";
1357 foreach (@{$targets}){
1358 $search.= "(macAddress=$_)";
1359 }
1360 $search.= ")";
1362 # If there's any host inside of the search string, procress them
1363 if (!($search =~ /macAddress/)){
1364 daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);
1365 return;
1366 }
1368 # Perform search for Unit Tag
1369 my $mesg = $ldap_handle->search(
1370 base => $ldap_base,
1371 scope => 'sub',
1372 attrs => ['dn', 'FAIstate', 'objectClass'],
1373 filter => "$search"
1374 );
1376 if ($mesg->count) {
1377 my @entries = $mesg->entries;
1378 foreach my $entry (@entries) {
1379 # Only modify entry if it is not set to '$state'
1380 if ($entry->get_value("FAIstate") ne "$state"){
1381 daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
1382 my $result;
1383 my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
1384 if (exists $tmp{'FAIobject'}){
1385 if ($state eq ''){
1386 $result= $ldap_handle->modify($entry->dn, changes => [
1387 delete => [ FAIstate => [] ] ]);
1388 } else {
1389 $result= $ldap_handle->modify($entry->dn, changes => [
1390 replace => [ FAIstate => $state ] ]);
1391 }
1392 } elsif ($state ne ''){
1393 $result= $ldap_handle->modify($entry->dn, changes => [
1394 add => [ objectClass => 'FAIobject' ],
1395 add => [ FAIstate => $state ] ]);
1396 }
1398 # Errors?
1399 if ($result->code){
1400 daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
1401 }
1403 } else {
1404 daemon_log("$session_id DEBUG FAIstate at host found with filter statement '$search' already at state '$st'", 7);
1405 }
1406 }
1407 }
1408 # if no ldap handle defined
1409 } else {
1410 daemon_log("$session_id ERROR: no LDAP handle defined for update FAIstate", 1);
1411 }
1412 }
1415 sub change_goto_state {
1416 my ($st, $targets, $session_id) = @_;
1417 $session_id = 0 if not defined $session_id;
1419 # Switch on or off?
1420 my $state= $st eq 'active' ? 'active': 'locked';
1422 &refresh_ldap_handle();
1423 if( defined($ldap_handle) ) {
1425 # Build search filter for hosts
1426 my $search= "(&(objectClass=GOhard)";
1427 foreach (@{$targets}){
1428 $search.= "(macAddress=$_)";
1429 }
1430 $search.= ")";
1432 # If there's any host inside of the search string, procress them
1433 if (!($search =~ /macAddress/)){
1434 return;
1435 }
1437 # Perform search for Unit Tag
1438 my $mesg = $ldap_handle->search(
1439 base => $ldap_base,
1440 scope => 'sub',
1441 attrs => ['dn', 'gotoMode'],
1442 filter => "$search"
1443 );
1445 if ($mesg->count) {
1446 my @entries = $mesg->entries;
1447 foreach my $entry (@entries) {
1449 # Only modify entry if it is not set to '$state'
1450 if ($entry->get_value("gotoMode") ne $state){
1452 daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
1453 my $result;
1454 $result= $ldap_handle->modify($entry->dn, changes => [
1455 replace => [ gotoMode => $state ] ]);
1457 # Errors?
1458 if ($result->code){
1459 &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
1460 }
1462 }
1463 }
1464 }
1466 }
1467 }
1470 sub create_fai_server_db {
1471 my ($table_name, $kernel) = @_;
1472 my $result;
1474 if(defined($ldap_handle)) {
1475 daemon_log("INFO: create_fai_server_db: start", 5);
1476 my $mesg= $ldap_handle->search(
1477 base => $ldap_base,
1478 scope => 'sub',
1479 attrs => ['FAIrepository', 'gosaUnitTag'],
1480 filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
1481 );
1482 if($mesg->{'resultCode'} == 0 &&
1483 $mesg->count != 0) {
1484 foreach my $entry (@{$mesg->{entries}}) {
1485 if($entry->exists('FAIrepository')) {
1486 # Add an entry for each Repository configured for server
1487 foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
1488 my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
1489 my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
1490 $result= $fai_server_db->add_dbentry( {
1491 table => $table_name,
1492 primkey => ['server', 'release', 'tag'],
1493 server => $tmp_url,
1494 release => $tmp_release,
1495 sections => $tmp_sections,
1496 tag => (length($tmp_tag)>0)?$tmp_tag:"",
1497 } );
1498 }
1499 }
1500 }
1501 }
1502 daemon_log("INFO: create_fai_server_db: finished", 5);
1504 # TODO: Find a way to post the 'create_packages_list_db' event
1505 &create_packages_list_db();
1506 }
1508 return $result;
1509 }
1511 sub run_create_fai_server_db {
1512 my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
1513 my $task = POE::Wheel::Run->new(
1514 Program => sub { &create_fai_server_db($table_name,$kernel) },
1515 StdoutEvent => "session_run_result",
1516 StderrEvent => "session_run_debug",
1517 CloseEvent => "session_run_done",
1518 );
1520 $heap->{task}->{ $task->ID } = $task;
1521 return;
1522 }
1525 sub create_fai_release_db {
1526 my ($table_name) = @_;
1527 my $result;
1529 if(defined($ldap_handle)) {
1530 daemon_log("INFO: create_fai_release_db: start",5);
1531 my $mesg= $ldap_handle->search(
1532 base => $ldap_base,
1533 scope => 'sub',
1534 attrs => [],
1535 filter => "(&(objectClass=organizationalUnit)(ou=fai))",
1536 );
1537 if($mesg->{'resultCode'} == 0 &&
1538 $mesg->count != 0) {
1539 # Walk through all possible FAI container ou's
1540 my @sql_list;
1541 my $timestamp= &get_time();
1542 foreach my $ou (@{$mesg->{entries}}) {
1543 my $tmp_classes= resolve_fai_classes($ou->dn);
1544 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
1545 my @tmp_array=get_fai_release_entries($tmp_classes);
1546 if(@tmp_array) {
1547 foreach my $entry (@tmp_array) {
1548 if(defined($entry) && ref($entry) eq 'HASH') {
1549 my $sql=
1550 "INSERT INTO $table_name "
1551 ."(timestamp, release, class, type, state) VALUES ("
1552 .$timestamp.","
1553 ."'".$entry->{'release'}."',"
1554 ."'".$entry->{'class'}."',"
1555 ."'".$entry->{'type'}."',"
1556 ."'".$entry->{'state'}."')";
1557 push @sql_list, $sql;
1558 }
1559 }
1560 }
1561 }
1562 }
1563 daemon_log("DEBUG: Inserting ".scalar @sql_list." entries to DB",6);
1564 if(@sql_list) {
1565 unshift @sql_list, "DELETE FROM $table_name";
1566 $fai_server_db->exec_statementlist(\@sql_list);
1567 }
1568 daemon_log("DEBUG: Done with inserting",6);
1569 }
1570 daemon_log("INFO: create_fai_release_db: finished",5);
1571 }
1573 return $result;
1574 }
1575 sub run_create_fai_release_db {
1576 my ($session, $heap, $table_name) = @_[SESSION, HEAP, ARG0];
1577 my $task = POE::Wheel::Run->new(
1578 Program => sub { &create_fai_release_db($table_name) },
1579 StdoutEvent => "session_run_result",
1580 StderrEvent => "session_run_debug",
1581 CloseEvent => "session_run_done",
1582 );
1584 $heap->{task}->{ $task->ID } = $task;
1585 return;
1586 }
1588 sub get_fai_types {
1589 my $tmp_classes = shift || return undef;
1590 my @result;
1592 foreach my $type(keys %{$tmp_classes}) {
1593 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
1594 my $entry = {
1595 type => $type,
1596 state => $tmp_classes->{$type}[0],
1597 };
1598 push @result, $entry;
1599 }
1600 }
1602 return @result;
1603 }
1605 sub get_fai_state {
1606 my $result = "";
1607 my $tmp_classes = shift || return $result;
1609 foreach my $type(keys %{$tmp_classes}) {
1610 if(defined($tmp_classes->{$type}[0])) {
1611 $result = $tmp_classes->{$type}[0];
1613 # State is equal for all types in class
1614 last;
1615 }
1616 }
1618 return $result;
1619 }
1621 sub resolve_fai_classes {
1622 my $result;
1623 my $fai_base= shift;
1624 my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
1625 my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
1626 my $fai_classes;
1628 daemon_log("DEBUG: Searching for FAI entries in base $fai_base",6);
1629 my $mesg= $ldap_handle->search(
1630 base => $fai_base,
1631 scope => 'sub',
1632 attrs => ['cn','objectClass','FAIstate'],
1633 filter => $fai_filter,
1634 );
1635 daemon_log("DEBUG: Found ".$mesg->count()." FAI entries",6);
1637 if($mesg->{'resultCode'} == 0 &&
1638 $mesg->count != 0) {
1639 foreach my $entry (@{$mesg->{entries}}) {
1640 if($entry->exists('cn')) {
1641 my $tmp_dn= $entry->dn();
1643 # Skip classname and ou dn parts for class
1644 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?),$fai_base$/;
1646 # Skip classes without releases
1647 if((!defined($tmp_release)) || length($tmp_release)==0) {
1648 next;
1649 }
1651 my $tmp_cn= $entry->get_value('cn');
1652 my $tmp_state= $entry->get_value('FAIstate');
1654 my $tmp_type;
1655 # Get FAI type
1656 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
1657 if(grep $_ eq $oclass, @possible_fai_classes) {
1658 $tmp_type= $oclass;
1659 last;
1660 }
1661 }
1663 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
1664 # A Subrelease
1665 my @sub_releases = split(/,/, $tmp_release);
1667 # Walk through subreleases and build hash tree
1668 my $hash;
1669 while(my $tmp_sub_release = pop @sub_releases) {
1670 $hash .= "\{'$tmp_sub_release'\}->";
1671 }
1672 eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
1673 } else {
1674 # A branch, no subrelease
1675 push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
1676 }
1677 } elsif (!$entry->exists('cn')) {
1678 my $tmp_dn= $entry->dn();
1679 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?),$fai_base$/;
1681 # Skip classes without releases
1682 if((!defined($tmp_release)) || length($tmp_release)==0) {
1683 next;
1684 }
1686 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
1687 # A Subrelease
1688 my @sub_releases= split(/,/, $tmp_release);
1690 # Walk through subreleases and build hash tree
1691 my $hash;
1692 while(my $tmp_sub_release = pop @sub_releases) {
1693 $hash .= "\{'$tmp_sub_release'\}->";
1694 }
1695 # Remove the last two characters
1696 chop($hash);
1697 chop($hash);
1699 eval('$fai_classes->'.$hash.'= {}');
1700 } else {
1701 # A branch, no subrelease
1702 if(!exists($fai_classes->{$tmp_release})) {
1703 $fai_classes->{$tmp_release} = {};
1704 }
1705 }
1706 }
1707 }
1709 # The hash is complete, now we can honor the copy-on-write based missing entries
1710 foreach my $release (keys %$fai_classes) {
1711 $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
1712 }
1713 }
1714 return $result;
1715 }
1717 sub apply_fai_inheritance {
1718 my $fai_classes = shift || return {};
1719 my $tmp_classes;
1721 # Get the classes from the branch
1722 foreach my $class (keys %{$fai_classes}) {
1723 # Skip subreleases
1724 if($class =~ /^ou=.*$/) {
1725 next;
1726 } else {
1727 $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
1728 }
1729 }
1731 # Apply to each subrelease
1732 foreach my $subrelease (keys %{$fai_classes}) {
1733 if($subrelease =~ /ou=/) {
1734 foreach my $tmp_class (keys %{$tmp_classes}) {
1735 if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
1736 $fai_classes->{$subrelease}->{$tmp_class} =
1737 deep_copy($tmp_classes->{$tmp_class});
1738 } else {
1739 foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
1740 if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
1741 $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
1742 deep_copy($tmp_classes->{$tmp_class}->{$type});
1743 }
1744 }
1745 }
1746 }
1747 }
1748 }
1750 # Find subreleases in deeper levels
1751 foreach my $subrelease (keys %{$fai_classes}) {
1752 if($subrelease =~ /ou=/) {
1753 foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
1754 if($subsubrelease =~ /ou=/) {
1755 apply_fai_inheritance($fai_classes->{$subrelease});
1756 }
1757 }
1758 }
1759 }
1761 return $fai_classes;
1762 }
1764 sub get_fai_release_entries {
1765 my $tmp_classes = shift || return;
1766 my $parent = shift || "";
1767 my @result = shift || ();
1769 foreach my $entry (keys %{$tmp_classes}) {
1770 if(defined($entry)) {
1771 if($entry =~ /^ou=.*$/) {
1772 my $release_name = $entry;
1773 $release_name =~ s/ou=//g;
1774 if(length($parent)>0) {
1775 $release_name = $parent."/".$release_name;
1776 }
1777 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
1778 foreach my $bufentry(@bufentries) {
1779 push @result, $bufentry;
1780 }
1781 } else {
1782 my @types = get_fai_types($tmp_classes->{$entry});
1783 foreach my $type (@types) {
1784 push @result,
1785 {
1786 'class' => $entry,
1787 'type' => $type->{'type'},
1788 'release' => $parent,
1789 'state' => $type->{'state'},
1790 };
1791 }
1792 }
1793 }
1794 }
1796 return @result;
1797 }
1799 sub deep_copy {
1800 my $this = shift;
1801 if (not ref $this) {
1802 $this;
1803 } elsif (ref $this eq "ARRAY") {
1804 [map deep_copy($_), @$this];
1805 } elsif (ref $this eq "HASH") {
1806 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
1807 } else { die "what type is $_?" }
1808 }
1811 sub session_run_result {
1812 my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];
1813 $kernel->sig(CHLD => "child_reap");
1814 }
1816 sub session_run_debug {
1817 my $result = $_[ARG0];
1818 print STDERR "$result\n";
1819 }
1821 sub session_run_done {
1822 my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1823 delete $heap->{task}->{$task_id};
1824 }
1826 sub create_sources_list {
1827 my $result="/tmp/gosa_si_tmp_sources_list";
1829 # Remove old file
1830 if(stat($result)) {
1831 unlink($result);
1832 }
1834 my $fh;
1835 open($fh, ">$result") or return undef;
1836 if(defined($ldap_server_dn) and length($ldap_server_dn) > 0) {
1837 my $mesg=$ldap_handle->search(
1838 base => $ldap_server_dn,
1839 scope => 'base',
1840 attrs => 'FAIrepository',
1841 filter => 'objectClass=FAIrepositoryServer'
1842 );
1843 if($mesg->count) {
1844 foreach my $entry(@{$mesg->{'entries'}}) {
1845 my ($server, $tag, $release, $sections)= split /\|/, $entry->get_value('FAIrepository');
1846 my $line = "deb $server $release";
1847 $sections =~ s/,/ /g;
1848 $line.= " $sections";
1849 print $fh $line."\n";
1850 }
1851 }
1852 }
1853 close($fh);
1855 return $result;
1856 }
1858 sub create_packages_list_db {
1859 my ($sources_file) = @_ || &create_sources_list;
1860 my $line;
1861 daemon_log("INFO: create_packages_list_db: start", 5);
1863 open(CONFIG, "<$sources_file") or do {
1864 daemon_log( "ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
1865 return;
1866 };
1868 # Read lines
1869 while ($line = <CONFIG>){
1870 # Unify
1871 chop($line);
1872 $line =~ s/^\s+//;
1873 $line =~ s/^\s+/ /;
1875 # Strip comments
1876 $line =~ s/#.*$//g;
1878 # Skip empty lines
1879 if ($line =~ /^\s*$/){
1880 next;
1881 }
1883 # Interpret deb line
1884 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
1885 my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
1886 my $section;
1887 foreach $section (split(' ', $sections)){
1888 &parse_package_info( $baseurl, $dist, $section );
1889 }
1890 }
1891 }
1893 close (CONFIG);
1895 daemon_log("INFO: create_packages_list_db: finished", 5);
1896 return;
1897 }
1899 sub run_create_packages_list_db {
1900 my ($session, $heap) = @_[SESSION, HEAP];
1901 my $task = POE::Wheel::Run->new(
1902 Program => sub {&create_packages_list_db},
1903 StdoutEvent => "session_run_result",
1904 StderrEvent => "session_run_debug",
1905 CloseEvent => "session_run_done",
1906 );
1907 $heap->{task}->{ $task->ID } = $task;
1908 }
1910 sub parse_package_info {
1911 my ($baseurl, $dist, $section)= @_;
1912 my ($package);
1914 my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
1915 $repo_dirs{ "${repo_path}/pool" } = 1;
1917 foreach $package ("Packages.gz"){
1918 daemon_log("DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
1919 get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section" );
1920 parse_package( "$outdir/$dist/$section", $dist, $path );
1921 }
1922 find(\&cleanup_and_extract, keys( %repo_dirs ) );
1923 }
1925 sub get_package {
1926 my ($url, $dest)= @_;
1928 my $tpath = dirname($dest);
1929 -d "$tpath" || mkpath "$tpath";
1931 # This is ugly, but I've no time to take a look at "how it works in perl"
1932 if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
1933 system("gunzip -cd '$dest' > '$dest.in'");
1934 unlink($dest);
1935 } else {
1936 daemon_log("ERROR: create_packages_list_db: get_packages: fetching '$url' failed!", 1);
1937 }
1938 return 0;
1939 }
1941 sub parse_package {
1942 my ($path, $dist, $srv_path)= @_;
1943 my ($package, $version, $section, $description);
1944 my @sql_list;
1945 my $PACKAGES;
1947 if(not stat("$path.in")) {
1948 daemon_log("ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
1949 return;
1950 }
1952 open($PACKAGES, "<$path.in");
1953 if(not defined($PACKAGES)) {
1954 daemon_log("ERROR: create_packages_list_db: parse_package: can not open '$path.in'",1);
1955 return;
1956 }
1958 # Read lines
1959 while (<$PACKAGES>){
1960 my $line = $_;
1961 # Unify
1962 chop($line);
1964 # Use empty lines as a trigger
1965 if ($line =~ /^\s*$/){
1966 my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '', 'none', '0')";
1967 push(@sql_list, $sql);
1968 $package = "none";
1969 $version = "none";
1970 $section = "none";
1971 $description = "none";
1972 next;
1973 }
1975 # Trigger for package name
1976 if ($line =~ /^Package:\s/){
1977 ($package)= ($line =~ /^Package: (.*)$/);
1978 next;
1979 }
1981 # Trigger for version
1982 if ($line =~ /^Version:\s/){
1983 ($version)= ($line =~ /^Version: (.*)$/);
1984 next;
1985 }
1987 # Trigger for description
1988 if ($line =~ /^Description:\s/){
1989 ($description)= ($line =~ /^Description: (.*)$/);
1990 next;
1991 }
1993 # Trigger for section
1994 if ($line =~ /^Section:\s/){
1995 ($section)= ($line =~ /^Section: (.*)$/);
1996 next;
1997 }
1999 # Trigger for filename
2000 if ($line =~ /^Filename:\s/){
2001 my ($filename) = ($line =~ /^Filename: (.*)$/);
2002 store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
2003 next;
2004 }
2005 }
2007 close( $PACKAGES );
2008 unlink( "$path.in" );
2010 $packages_list_db->exec_statementlist(\@sql_list);
2011 }
2013 sub store_fileinfo {
2014 my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
2016 my %fileinfo = (
2017 'package' => $package,
2018 'dist' => $dist,
2019 'version' => $vers,
2020 );
2022 $repo_files{ "${srvdir}/$file" } = \%fileinfo;
2023 }
2025 sub cleanup_and_extract {
2026 my $fileinfo = $repo_files{ $File::Find::name };
2028 if( defined $fileinfo ) {
2030 my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
2031 my $sql;
2032 my $package = $fileinfo->{ 'package' };
2033 my $newver = $fileinfo->{ 'version' };
2035 mkpath($dir);
2036 system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
2038 if( -f "$dir/DEBIAN/templates" ) {
2040 daemon_log("DEBUG: Found debconf templates in '$package' - $newver", 5);
2042 my $tmpl= "";
2043 {
2044 local $/=undef;
2045 open FILE, "$dir/DEBIAN/templates";
2046 $tmpl = &encode_base64(<FILE>);
2047 close FILE;
2048 }
2049 rmtree("$dir/DEBIAN/templates");
2051 $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
2053 } else {
2054 $sql= "update $main::packages_list_tn set template = '' where package = '$package' and version = '$newver';";
2055 }
2057 my $res= $main::packages_list_db->update_dbentry($sql);
2058 }
2059 }
2062 #==== MAIN = main ==============================================================
2063 # parse commandline options
2064 Getopt::Long::Configure( "bundling" );
2065 GetOptions("h|help" => \&usage,
2066 "c|config=s" => \$cfg_file,
2067 "f|foreground" => \$foreground,
2068 "v|verbose+" => \$verbose,
2069 "no-bus+" => \$no_bus,
2070 "no-arp+" => \$no_arp,
2071 );
2073 # read and set config parameters
2074 &check_cmdline_param ;
2075 &read_configfile;
2076 &check_pid;
2078 $SIG{CHLD} = 'IGNORE';
2080 # forward error messages to logfile
2081 if( ! $foreground ) {
2082 open( STDIN, '+>/dev/null' );
2083 open( STDOUT, '+>&STDIN' );
2084 open( STDERR, '+>&STDIN' );
2085 }
2087 # Just fork, if we are not in foreground mode
2088 if( ! $foreground ) {
2089 chdir '/' or die "Can't chdir to /: $!";
2090 $pid = fork;
2091 setsid or die "Can't start a new session: $!";
2092 umask 0;
2093 } else {
2094 $pid = $$;
2095 }
2097 # Do something useful - put our PID into the pid_file
2098 if( 0 != $pid ) {
2099 open( LOCK_FILE, ">$pid_file" );
2100 print LOCK_FILE "$pid\n";
2101 close( LOCK_FILE );
2102 if( !$foreground ) {
2103 exit( 0 )
2104 };
2105 }
2107 daemon_log(" ", 1);
2108 daemon_log("$0 started!", 1);
2110 if ($no_bus > 0) {
2111 $bus_activ = "false"
2112 }
2116 # delete old DBsqlite lock files
2117 #unlink('/tmp/gosa_si_lock*');
2119 # connect to gosa-si job queue
2120 $job_db = GOSA::DBsqlite->new($job_queue_file_name);
2121 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
2123 # connect to known_clients_db
2124 $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
2125 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
2127 # connect to known_server_db
2128 $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
2129 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
2131 # connect to login_usr_db
2132 $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
2133 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
2135 # connect to fai_server_db and fai_release_db
2136 $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
2137 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
2138 $fai_server_db->create_table($fai_release_tn, \@fai_release_col_names);
2140 # connect to packages_list_db
2141 unlink($packages_list_file_name);
2142 $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
2143 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
2145 # connect to messaging_db
2146 $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
2147 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
2150 # create xml object used for en/decrypting
2151 $xml = new XML::Simple();
2153 # create socket for incoming xml messages
2155 POE::Component::Server::TCP->new(
2156 Port => $server_port,
2157 ClientInput => sub {
2158 my ($kernel, $input) = @_[KERNEL, ARG0];
2159 push(@tasks, $input);
2160 $kernel->yield("next_task");
2161 },
2162 InlineStates => {
2163 next_task => \&next_task,
2164 task_result => \&handle_task_result,
2165 task_done => \&handle_task_done,
2166 task_debug => \&handle_task_debug,
2167 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!" },
2168 }
2169 );
2171 daemon_log("start socket for incoming xml messages at port '$server_port' ", 1);
2173 # create session for repeatedly checking the job queue for jobs
2174 POE::Session->create(
2175 inline_states => {
2176 _start => \&_start,
2177 sig_handler => \&sig_handler,
2178 watch_for_new_jobs => \&watch_for_new_jobs,
2179 watch_for_done_jobs => \&watch_for_done_jobs,
2180 create_packages_list_db => \&run_create_packages_list_db,
2181 create_fai_server_db => \&run_create_fai_server_db,
2182 create_fai_release_db => \&run_create_fai_release_db,
2183 session_run_result => \&session_run_result,
2184 session_run_debug => \&session_run_debug,
2185 session_run_done => \&session_run_done,
2186 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!" },
2187 }
2188 );
2191 # import all modules
2192 &import_modules;
2194 # check wether all modules are gosa-si valid passwd check
2196 POE::Kernel->run();
2197 exit;