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