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