be587d2700a65f0d7216157a71defd54aa17daa4
1 #!/usr/bin/perl
2 #===============================================================================
3 #
4 # FILE: gosa-sd
5 #
6 # USAGE: ./gosa-sd
7 #
8 # DESCRIPTION:
9 #
10 # OPTIONS: ---
11 # REQUIREMENTS: libconfig-inifiles-perl libcrypt-rijndael-perl libxml-simple-perl
12 # libdata-dumper-simple-perl libdbd-sqlite3-perl libnet-ldap-perl
13 # libpoe-perl
14 # BUGS: ---
15 # NOTES:
16 # AUTHOR: (Andreas Rettenberger), <rettenberger@gonicus.de>
17 # COMPANY:
18 # VERSION: 1.0
19 # CREATED: 12.09.2007 08:54:41 CEST
20 # REVISION: ---
21 #===============================================================================
23 my $server_version = '$HeadURL: https://oss.gonicus.de/repositories/gosa/trunk/gosa-si/gosa-si-server $:$Rev$';
25 use strict;
26 use warnings;
27 use Getopt::Long;
28 use Config::IniFiles;
29 use POSIX;
31 use Fcntl qw/:flock/;
32 use IO::Socket::INET;
33 use IO::Handle;
34 use IO::Select;
35 use Symbol qw(qualify_to_ref);
36 use Crypt::Rijndael;
37 use MIME::Base64;
38 use Digest::MD5 qw(md5 md5_hex md5_base64);
39 use XML::Simple;
40 use Data::Dumper;
41 use Sys::Syslog qw( :DEFAULT setlogsock);
42 use Time::HiRes qw( usleep);
43 use Cwd;
44 use File::Spec;
45 use File::Basename;
46 use File::Find;
47 use File::Copy;
48 use File::Path;
49 use GOSA::GosaSupportDaemon;
50 use POE qw(Component::Server::TCP Wheel::Run Filter::Reference);
51 use Net::LDAP;
52 use Net::LDAP::Util qw(:escape);
54 # revision number of server and program name
55 my $server_headURL;
56 my $server_revision;
57 my $server_status;
58 our $prg= basename($0);
59 our $verbose= 0;
61 my $db_module = "DBsqlite";
62 {
63 no strict "refs";
64 require ("GOSA/".$db_module.".pm");
65 ("GOSA/".$db_module)->import;
66 #daemon_log("0 INFO: importing database module '$db_module'", 1);
67 }
69 my $modules_path = "/usr/lib/gosa-si/modules";
70 use lib "/usr/lib/gosa-si/modules";
72 our $global_kernel;
73 my ($foreground, $ping_timeout);
74 my ($server);
75 my ($gosa_server, $job_queue_timeout, $job_queue_loop_delay);
76 my ($messaging_db_loop_delay);
77 my ($procid, $pid);
78 my $arp_fifo;
79 my $debug_parts = 0;
80 my $debug_parts_bitstring;
81 my ($xml);
82 my $sources_list;
83 my $max_clients;
84 my %repo_files=();
85 my $repo_path;
86 my %repo_dirs=();
88 # Variables declared in config file are always set to 'our'
89 our (%cfg_defaults, $log_file, $pid_file,
90 $server_ip, $server_port, $ClientPackages_key, $dns_lookup,
91 $arp_activ, $gosa_unit_tag,
92 $GosaPackages_key, $gosa_timeout,
93 $serverPackages_enabled, $foreign_server_string, $server_domain, $ServerPackages_key, $foreign_servers_register_delay,
94 $wake_on_lan_passwd, $job_synchronization, $modified_jobs_loop_delay,
95 $arp_enabled, $arp_interface,
96 $opsi_enabled, $opsi_server, $opsi_admin, $opsi_password,
97 $new_systems_ou,
98 );
100 # additional variable which should be globaly accessable
101 our $server_address;
102 our $server_mac_address;
103 our $gosa_address;
104 our $no_arp;
105 our $forground;
106 our $cfg_file;
107 our ($ldap_uri, $ldap_base, $ldap_admin_dn, $ldap_admin_password, $ldap_server_dn, $ldap_version);
108 our ($mysql_username, $mysql_password, $mysql_database, $mysql_host);
109 our $known_modules;
110 our $known_functions;
111 our $root_uid;
112 our $adm_gid;
114 # if foreground is not null, script will be not forked to background
115 $foreground = 0 ;
117 # specifies the timeout seconds while checking the online status of a registrating client
118 $ping_timeout = 5;
120 $no_arp = 0;
121 my $packages_list_under_construction = "/tmp/packages_list_creation_in_progress";
122 my @packages_list_statements;
123 my $watch_for_new_jobs_in_progress = 0;
125 # holds all incoming decrypted messages
126 our $incoming_db;
127 our $incoming_tn = 'incoming';
128 my $incoming_file_name;
129 my @incoming_col_names = ("id INTEGER PRIMARY KEY",
130 "timestamp VARCHAR(14) DEFAULT 'none'",
131 "headertag VARCHAR(255) DEFAULT 'none'",
132 "targettag VARCHAR(255) DEFAULT 'none'",
133 "xmlmessage TEXT",
134 "module VARCHAR(255) DEFAULT 'none'",
135 "sessionid VARCHAR(255) DEFAULT '0'",
136 );
138 # holds all gosa jobs
139 our $job_db;
140 our $job_queue_tn = 'jobs';
141 my $job_queue_file_name;
142 my @job_queue_col_names = ("id INTEGER PRIMARY KEY",
143 "timestamp VARCHAR(14) DEFAULT 'none'",
144 "status VARCHAR(255) DEFAULT 'none'",
145 "result TEXT",
146 "progress VARCHAR(255) DEFAULT 'none'",
147 "headertag VARCHAR(255) DEFAULT 'none'",
148 "targettag VARCHAR(255) DEFAULT 'none'",
149 "xmlmessage TEXT",
150 "macaddress VARCHAR(17) DEFAULT 'none'",
151 "plainname VARCHAR(255) DEFAULT 'none'",
152 "siserver VARCHAR(255) DEFAULT 'none'",
153 "modified INTEGER DEFAULT '0'",
154 );
156 # holds all other gosa-si-server
157 our $known_server_db;
158 our $known_server_tn = "known_server";
159 my $known_server_file_name;
160 my @known_server_col_names = ("hostname VARCHAR(255)", "macaddress VARCHAR(17)", "status VARCHAR(255)", "hostkey VARCHAR(255)", "loaded_modules TEXT", "timestamp VARCHAR(14)", "update_time VARCHAR(14)");
162 # holds all registrated clients
163 our $known_clients_db;
164 our $known_clients_tn = "known_clients";
165 my $known_clients_file_name;
166 my @known_clients_col_names = ("hostname VARCHAR(255)", "status VARCHAR(255)", "hostkey VARCHAR(255)", "timestamp VARCHAR(14)", "macaddress VARCHAR(17)", "events TEXT", "keylifetime VARCHAR(255)");
168 # holds all registered clients at a foreign server
169 our $foreign_clients_db;
170 our $foreign_clients_tn = "foreign_clients";
171 my $foreign_clients_file_name;
172 my @foreign_clients_col_names = ("hostname VARCHAR(255)", "macaddress VARCHAR(17)", "regserver VARCHAR(255)", "timestamp VARCHAR(14)");
174 # holds all logged in user at each client
175 our $login_users_db;
176 our $login_users_tn = "login_users";
177 my $login_users_file_name;
178 my @login_users_col_names = ("client VARCHAR(255)", "user VARCHAR(255)", "timestamp VARCHAR(14)", "regserver VARCHAR(255) DEFAULT 'localhost'");
180 # holds all fai server, the debian release and tag
181 our $fai_server_db;
182 our $fai_server_tn = "fai_server";
183 my $fai_server_file_name;
184 our @fai_server_col_names = ("timestamp VARCHAR(14)", "server VARCHAR(255)", "fai_release VARCHAR(255)", "sections VARCHAR(255)", "tag VARCHAR(255)");
186 our $fai_release_db;
187 our $fai_release_tn = "fai_release";
188 my $fai_release_file_name;
189 our @fai_release_col_names = ("timestamp VARCHAR(14)", "fai_release VARCHAR(255)", "class VARCHAR(255)", "type VARCHAR(255)", "state VARCHAR(255)");
191 # holds all packages available from different repositories
192 our $packages_list_db;
193 our $packages_list_tn = "packages_list";
194 my $packages_list_file_name;
195 our @packages_list_col_names = ("distribution VARCHAR(255)", "package VARCHAR(255)", "version VARCHAR(255)", "section VARCHAR(255)", "description TEXT", "template LONGBLOB", "timestamp VARCHAR(14)");
196 my $outdir = "/tmp/packages_list_db";
197 my $arch = "i386";
199 # holds all messages which should be delivered to a user
200 our $messaging_db;
201 our $messaging_tn = "messaging";
202 our @messaging_col_names = ("id INTEGER", "subject TEXT", "message_from VARCHAR(255)", "message_to VARCHAR(255)",
203 "flag VARCHAR(255)", "direction VARCHAR(255)", "delivery_time VARCHAR(255)", "message TEXT", "timestamp VARCHAR(14)" );
204 my $messaging_file_name;
206 # path to directory to store client install log files
207 our $client_fai_log_dir = "/var/log/fai";
209 # queue which stores taskes until one of the $max_children children are ready to process the task
210 #my @tasks = qw();
211 my @msgs_to_decrypt = qw();
212 my $max_children = 2;
215 # loop delay for job queue to look for opsi jobs
216 my $job_queue_opsi_delay = 10;
217 our $opsi_client;
218 our $opsi_url;
220 # Lifetime of logged in user information. If no update information comes after n seconds,
221 # the user is expeceted to be no longer logged in or the host is no longer running. Because
222 # of this, the user is deleted from login_users_db
223 our $logged_in_user_date_of_expiry = 600;
225 # List of month names, used in function daemon_log
226 my @monthnames = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
228 %cfg_defaults = (
229 "general" => {
230 "log-file" => [\$log_file, "/var/run/".$prg.".log"],
231 "pid-file" => [\$pid_file, "/var/run/".$prg.".pid"],
232 },
233 "server" => {
234 "ip" => [\$server_ip, "0.0.0.0"],
235 "port" => [\$server_port, "20081"],
236 "known-clients" => [\$known_clients_file_name, '/var/lib/gosa-si/clients.db' ],
237 "known-servers" => [\$known_server_file_name, '/var/lib/gosa-si/servers.db'],
238 "incoming" => [\$incoming_file_name, '/var/lib/gosa-si/incoming.db'],
239 "login-users" => [\$login_users_file_name, '/var/lib/gosa-si/users.db'],
240 "fai-server" => [\$fai_server_file_name, '/var/lib/gosa-si/fai_server.db'],
241 "fai-release" => [\$fai_release_file_name, '/var/lib/gosa-si/fai_release.db'],
242 "packages-list" => [\$packages_list_file_name, '/var/lib/gosa-si/packages.db'],
243 "messaging" => [\$messaging_file_name, '/var/lib/gosa-si/messaging.db'],
244 "foreign-clients" => [\$foreign_clients_file_name, '/var/lib/gosa-si/foreign_clients.db'],
245 "source-list" => [\$sources_list, '/etc/apt/sources.list'],
246 "repo-path" => [\$repo_path, '/srv/www/repository'],
247 "ldap-uri" => [\$ldap_uri, ""],
248 "ldap-base" => [\$ldap_base, ""],
249 "ldap-admin-dn" => [\$ldap_admin_dn, ""],
250 "ldap-admin-password" => [\$ldap_admin_password, ""],
251 "ldap-version" => [\$ldap_version, 3],
252 "gosa-unit-tag" => [\$gosa_unit_tag, ""],
253 "max-clients" => [\$max_clients, 10],
254 "wol-password" => [\$wake_on_lan_passwd, ""],
255 "mysql-username" => [\$mysql_username, "gosa_si"],
256 "mysql-password" => [\$mysql_password, ""],
257 "mysql-database" => [\$mysql_database, "gosa_si"],
258 "mysql-host" => [\$mysql_host, "127.0.0.1"],
259 },
260 "GOsaPackages" => {
261 "job-queue" => [\$job_queue_file_name, '/var/lib/gosa-si/jobs.db'],
262 "job-queue-loop-delay" => [\$job_queue_loop_delay, 3],
263 "messaging-db-loop-delay" => [\$messaging_db_loop_delay, 3],
264 "key" => [\$GosaPackages_key, "none"],
265 "new-systems-ou" => [\$new_systems_ou, 'ou=workstations,ou=systems'],
266 },
267 "ClientPackages" => {
268 "key" => [\$ClientPackages_key, "none"],
269 "user-date-of-expiry" => [\$logged_in_user_date_of_expiry, 600],
270 },
271 "ServerPackages"=> {
272 "enabled" => [\$serverPackages_enabled, "true"],
273 "address" => [\$foreign_server_string, ""],
274 "dns-lookup" => [\$dns_lookup, "true"],
275 "domain" => [\$server_domain, ""],
276 "key" => [\$ServerPackages_key, "none"],
277 "key-lifetime" => [\$foreign_servers_register_delay, 120],
278 "job-synchronization-enabled" => [\$job_synchronization, "true"],
279 "synchronization-loop" => [\$modified_jobs_loop_delay, 5],
280 },
281 "ArpHandler" => {
282 "enabled" => [\$arp_enabled, "true"],
283 "interface" => [\$arp_interface, "all"],
284 },
285 "Opsi" => {
286 "enabled" => [\$opsi_enabled, "false"],
287 "server" => [\$opsi_server, "localhost"],
288 "admin" => [\$opsi_admin, "opsi-admin"],
289 "password" => [\$opsi_password, "secret"],
290 },
292 );
295 #=== FUNCTION ================================================================
296 # NAME: usage
297 # PARAMETERS: nothing
298 # RETURNS: nothing
299 # DESCRIPTION: print out usage text to STDERR
300 #===============================================================================
301 sub usage {
302 print STDERR << "EOF" ;
303 usage: $prg [-hvf] [-c config] [-d number]
305 -h : this (help) message
306 -c <file> : config file
307 -f : foreground, process will not be forked to background
308 -v : be verbose (multiple to increase verbosity)
309 'v': error logs
310 'vvv': warning plus error logs
311 'vvvvv': info plus warning logs
312 'vvvvvvv': debug plus info logs
313 -no-arp : starts $prg without connection to arp module
314 -d <int> : if verbose level is higher than 7x 'v' specified parts can be debugged
315 1 : receiving messages
316 2 : sending messages
317 4 : encrypting/decrypting messages
318 8 : verification if a message complies gosa-si requirements
319 16 : message processing
320 32 : ldap connectivity
321 64 : database status and connectivity
322 128 : main process
323 EOF
324 exit(0);
325 }
328 #=== FUNCTION ================================================================
329 # NAME: logging
330 # PARAMETERS: level - string - default 'info'
331 # msg - string -
332 # facility - string - default 'LOG_DAEMON'
333 # RETURNS: nothing
334 # DESCRIPTION: function for logging
335 #===============================================================================
336 sub daemon_log {
337 my( $msg, $level ) = @_;
338 if (not defined $msg) { return }
339 if (not defined $level) { $level = 1 }
340 my $to_be_logged = 0;
342 # Write log line if line level is lower than verbosity given in commandline
343 if ($level <= $verbose)
344 {
345 $to_be_logged = 1 ;
346 }
348 # Write if debug flag is set and bitstring matches
349 if ($debug_parts > 0)
350 {
351 my $tmp_level = ($level - 10 >= 0) ? $level - 10 : 0 ;
352 my $tmp_level_bitstring = unpack("B32", pack("N", $tmp_level));
353 if (int($debug_parts_bitstring & $tmp_level_bitstring))
354 {
355 $to_be_logged = 1;
356 }
357 }
359 if ($to_be_logged)
360 {
361 if(defined $log_file){
362 my $open_log_fh = sysopen(LOG_HANDLE, $log_file, O_WRONLY | O_CREAT | O_APPEND , 0440);
363 if(not $open_log_fh) {
364 print STDERR "cannot open $log_file: $!";
365 return;
366 }
367 # Check owner and group of log_file and update settings if necessary
368 my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = stat($log_file);
369 if((not $uid eq $root_uid) || (not $gid eq $adm_gid)) {
370 chown($root_uid, $adm_gid, $log_file);
371 }
373 # Prepare time string for log message
374 my ($seconds,$minutes,$hours,$monthday,$month,$year,$weekday,$yearday,$sommertime) = localtime(time);
375 $hours = $hours < 10 ? $hours = "0".$hours : $hours;
376 $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
377 $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
378 $month = $monthnames[$month];
379 $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
380 $year+=1900;
383 # Build log message and write it to log file and commandline
384 chomp($msg);
385 my $log_msg = "$month $monthday $hours:$minutes:$seconds $prg $msg\n";
386 flock(LOG_HANDLE, LOCK_EX);
387 seek(LOG_HANDLE, 0, 2);
388 print LOG_HANDLE $log_msg;
389 flock(LOG_HANDLE, LOCK_UN);
390 if( $foreground )
391 {
392 print STDERR $log_msg;
393 }
394 close( LOG_HANDLE );
395 }
396 }
397 }
400 #=== FUNCTION ================================================================
401 # NAME: check_cmdline_param
402 # PARAMETERS: nothing
403 # RETURNS: nothing
404 # DESCRIPTION: validates commandline parameter
405 #===============================================================================
406 sub check_cmdline_param () {
407 my $err_counter = 0;
409 # Check configuration file
410 if(not defined($cfg_file)) {
411 $cfg_file = "/etc/gosa-si/server.conf";
412 if(! -r $cfg_file) {
413 print STDERR "Please specify a config file.\n";
414 $err_counter++;
415 }
416 }
418 # Prepare identification which gosa-si parts should be debugged and which not
419 if (defined $debug_parts)
420 {
421 if ($debug_parts =~ /^\d+$/)
422 {
423 $debug_parts_bitstring = unpack("B32", pack("N", $debug_parts));
424 }
425 else
426 {
427 print STDERR "Value '$debug_parts' invalid for option d (number expected)\n";
428 $err_counter++;
429 }
430 }
432 # Exit if an error occour
433 if( $err_counter > 0 ) { &usage( "", 1 ); }
434 }
437 #=== FUNCTION ================================================================
438 # NAME: check_pid
439 # PARAMETERS: nothing
440 # RETURNS: nothing
441 # DESCRIPTION: handels pid processing
442 #===============================================================================
443 sub check_pid {
444 $pid = -1;
445 # Check, if we are already running
446 if( open(LOCK_FILE, "<$pid_file") ) {
447 $pid = <LOCK_FILE>;
448 if( defined $pid ) {
449 chomp( $pid );
450 if( -f "/proc/$pid/stat" ) {
451 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
452 if( $stat ) {
453 print STDERR "\nERROR: Already running!\n";
454 close( LOCK_FILE );
455 exit -1;
456 }
457 }
458 }
459 close( LOCK_FILE );
460 unlink( $pid_file );
461 }
463 # create a syslog msg if it is not to possible to open PID file
464 if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
465 my($msg) = "Couldn't obtain lockfile '$pid_file' ";
466 if (open(LOCK_FILE, '<', $pid_file)
467 && ($pid = <LOCK_FILE>))
468 {
469 chomp($pid);
470 $msg .= "(PID $pid)\n";
471 } else {
472 $msg .= "(unable to read PID)\n";
473 }
474 if( ! ($foreground) ) {
475 openlog( $0, "cons,pid", "daemon" );
476 syslog( "warning", $msg );
477 closelog();
478 }
479 else {
480 print( STDERR " $msg " );
481 }
482 exit( -1 );
483 }
484 }
486 #=== FUNCTION ================================================================
487 # NAME: import_modules
488 # PARAMETERS: module_path - string - abs. path to the directory the modules
489 # are stored
490 # RETURNS: nothing
491 # DESCRIPTION: each file in module_path which ends with '.pm' and activation
492 # state is on is imported by "require 'file';"
493 #===============================================================================
494 sub import_modules {
495 if (not -e $modules_path) {
496 daemon_log("0 ERROR: cannot find directory or directory is not readable: $modules_path", 1);
497 }
499 opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
501 while (defined (my $file = readdir (DIR))) {
502 if (not $file =~ /(\S*?).pm$/) {
503 next;
504 }
505 my $mod_name = $1;
507 # ArpHandler switch
508 if( $file =~ /ArpHandler.pm/ ) {
509 if( $arp_enabled eq "false" ) { next; }
510 }
512 # ServerPackages switch
513 if ($file eq "ServerPackages.pm" && $serverPackages_enabled eq "false")
514 {
515 $dns_lookup = "false";
516 next;
517 }
519 eval { require $file; };
520 if ($@) {
521 daemon_log("0 ERROR: gosa-si-server could not load module $file", 1);
522 daemon_log("$@", 1);
523 exit;
524 } else {
525 my $info = eval($mod_name.'::get_module_info()');
526 # Only load module if get_module_info() returns a non-null object
527 if( $info ) {
528 my ($input_address, $input_key, $event_hash) = @{$info};
529 $known_modules->{$mod_name} = $info;
530 daemon_log("0 INFO: module $mod_name loaded", 5);
531 }
532 }
533 }
534 close (DIR);
535 }
537 #=== FUNCTION ================================================================
538 # NAME: password_check
539 # PARAMETERS: nothing
540 # RETURNS: nothing
541 # DESCRIPTION: escalates an critical error if two modules exist which are avaialable by
542 # the same password
543 #===============================================================================
544 sub password_check {
545 my $passwd_hash = {};
546 while (my ($mod_name, $mod_info) = each %$known_modules) {
547 my $mod_passwd = @$mod_info[1];
548 if (not defined $mod_passwd) { next; }
549 if (not exists $passwd_hash->{$mod_passwd}) {
550 $passwd_hash->{$mod_passwd} = $mod_name;
552 # escalates critical error
553 } else {
554 &daemon_log("0 ERROR: two loaded modules do have the same password. Please modify the 'key'-parameter in config file");
555 &daemon_log("0 ERROR: module='$mod_name' and module='".$passwd_hash->{$mod_passwd}."'");
556 exit( -1 );
557 }
558 }
560 }
563 #=== FUNCTION ================================================================
564 # NAME: sig_int_handler
565 # PARAMETERS: signal - string - signal arose from system
566 # RETURNS: nothing
567 # DESCRIPTION: handels tasks to be done befor signal becomes active
568 #===============================================================================
569 sub sig_int_handler {
570 my ($signal) = @_;
572 # if (defined($ldap_handle)) {
573 # $ldap_handle->disconnect;
574 # }
575 # TODO alle verbliebenden ldap verbindungen aus allen heaps beenden
578 daemon_log("shutting down gosa-si-server", 1);
579 system("kill `ps -C gosa-si-server -o pid=`");
580 }
581 $SIG{INT} = \&sig_int_handler;
584 sub check_key_and_xml_validity {
585 my ($crypted_msg, $module_key, $session_id) = @_;
586 my $msg;
587 my $msg_hash;
588 my $error_string;
589 eval{
590 $msg = &decrypt_msg($crypted_msg, $module_key);
592 if ($msg =~ /<xml>/i){
593 $msg =~ s/\s+/ /g; # just for better daemon_log
594 daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 18);
595 $msg_hash = $xml->XMLin($msg, ForceArray=>1);
597 ##############
598 # check header
599 if( not exists $msg_hash->{'header'} ) { die "no header specified"; }
600 my $header_l = $msg_hash->{'header'};
601 if( (1 > @{$header_l}) || ( ( 'HASH' eq ref @{$header_l}[0]) && (1 > keys %{@{$header_l}[0]}) ) ) { die 'empty header tag'; }
602 if( 1 < @{$header_l} ) { die 'more than one header specified'; }
603 my $header = @{$header_l}[0];
604 if( 0 == length $header) { die 'empty string in header tag'; }
606 ##############
607 # check source
608 if( not exists $msg_hash->{'source'} ) { die "no source specified"; }
609 my $source_l = $msg_hash->{'source'};
610 if( (1 > @{$source_l}) || ( ( 'HASH' eq ref @{$source_l}[0]) && (1 > keys %{@{$source_l}[0]}) ) ) { die 'empty source tag'; }
611 if( 1 < @{$source_l} ) { die 'more than one source specified'; }
612 my $source = @{$source_l}[0];
613 if( 0 == length $source) { die 'source error'; }
615 ##############
616 # check target
617 if( not exists $msg_hash->{'target'} ) { die "no target specified"; }
618 my $target_l = $msg_hash->{'target'};
619 if( (1 > @{$target_l}) || ( ('HASH' eq ref @{$target_l}[0]) && (1 > keys %{@{$target_l}[0]}) ) ) { die 'empty target tag'; }
620 }
621 };
622 if($@) {
623 daemon_log("$session_id ERROR: do not understand the message: $@", 1);
624 $msg = undef;
625 $msg_hash = undef;
626 }
628 return ($msg, $msg_hash);
629 }
632 sub check_outgoing_xml_validity {
633 my ($msg, $session_id) = @_;
635 my $msg_hash;
636 eval{
637 $msg_hash = $xml->XMLin($msg, ForceArray=>1);
639 ##############
640 # check header
641 my $header_l = $msg_hash->{'header'};
642 if( 1 != @{$header_l} ) {
643 die 'no or more than one headers specified';
644 }
645 my $header = @{$header_l}[0];
646 if( 0 == length $header) {
647 die 'header has length 0';
648 }
650 ##############
651 # check source
652 my $source_l = $msg_hash->{'source'};
653 if( 1 != @{$source_l} ) {
654 die 'no or more than 1 sources specified';
655 }
656 my $source = @{$source_l}[0];
657 if( 0 == length $source) {
658 die 'source has length 0';
659 }
661 # Check if source contains hostname instead of ip address
662 if($source =~ /^[a-z][\w\-\.]+:\d+$/i) {
663 my ($hostname,$port) = split(/:/, $source);
664 my $ip_address = inet_ntoa(scalar gethostbyname($hostname));
665 if(defined($ip_address) && $ip_address =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/ && $port =~ /^\d+$/) {
666 # Write ip address to $source variable
667 $source = "$ip_address:$port";
668 $msg_hash->{source}[0] = $source ;
669 $msg =~ s/<source>.*<\/source>/<source>$source<\/source>/;
670 }
671 }
672 unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
673 $source =~ /^GOSA$/i) {
674 die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
675 }
677 ##############
678 # check target
679 my $target_l = $msg_hash->{'target'};
680 if( 0 == @{$target_l} ) {
681 die "no targets specified";
682 }
683 foreach my $target (@$target_l) {
684 if( 0 == length $target) {
685 die "target has length 0";
686 }
687 unless( $target =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
688 $target =~ /^GOSA$/i ||
689 $target =~ /^\*$/ ||
690 $target =~ /KNOWN_SERVER/i ||
691 $target =~ /JOBDB/i ||
692 $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 ){
693 die "target '$target' is not a complete ip-address with port or a valid target name or a mac-address";
694 }
695 }
696 };
697 if($@) {
698 daemon_log("$session_id ERROR: outgoing msg is not gosa-si envelope conform: $@", 1);
699 daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 1);
700 $msg_hash = undef;
701 }
703 return ($msg, $msg_hash);
704 }
707 sub input_from_known_server {
708 my ($input, $remote_ip, $session_id) = @_ ;
709 my ($msg, $msg_hash, $module);
711 my $sql_statement= "SELECT * FROM known_server";
712 my $query_res = $known_server_db->select_dbentry( $sql_statement );
714 while( my ($hit_num, $hit) = each %{ $query_res } ) {
715 my $host_name = $hit->{hostname};
716 if( not $host_name =~ "^$remote_ip") {
717 next;
718 }
719 my $host_key = $hit->{hostkey};
720 daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 14);
721 daemon_log("$session_id DEBUG: input_from_known_server: host_key: $host_key", 14);
723 # check if module can open msg envelope with module key
724 my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
725 if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
726 daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 14);
727 daemon_log("$@", 14);
728 next;
729 }
730 else {
731 $msg = $tmp_msg;
732 $msg_hash = $tmp_msg_hash;
733 $module = "ServerPackages";
734 daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 14);
735 last;
736 }
737 }
739 if( (!$msg) || (!$msg_hash) || (!$module) ) {
740 daemon_log("$session_id DEBUG: Incoming message is not from a known server", 14);
741 }
743 return ($msg, $msg_hash, $module);
744 }
747 sub input_from_known_client {
748 my ($input, $remote_ip, $session_id) = @_ ;
749 my ($msg, $msg_hash, $module);
751 my $sql_statement= "SELECT * FROM known_clients";
752 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
753 while( my ($hit_num, $hit) = each %{ $query_res } ) {
754 my $host_name = $hit->{hostname};
755 if( not $host_name =~ /^$remote_ip/) {
756 next;
757 }
758 my $host_key = $hit->{hostkey};
759 &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 14);
760 &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 14);
762 # check if module can open msg envelope with module key
763 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
765 if( (!$msg) || (!$msg_hash) ) {
766 &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 14);
767 next;
768 }
769 else {
770 $module = "ClientPackages";
771 daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 14);
772 last;
773 }
774 }
776 if( (!$msg) || (!$msg_hash) || (!$module) ) {
777 &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 14);
778 }
780 return ($msg, $msg_hash, $module);
781 }
784 sub input_from_unknown_host {
785 no strict "refs";
786 my ($input, $session_id) = @_ ;
787 my ($msg, $msg_hash, $module);
788 my $error_string;
790 my %act_modules = %$known_modules;
792 while( my ($mod, $info) = each(%act_modules)) {
794 # check a key exists for this module
795 my $module_key = ${$mod."_key"};
796 if( not defined $module_key ) {
797 if( $mod eq 'ArpHandler' ) {
798 next;
799 }
800 daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
801 next;
802 }
803 daemon_log("$session_id DEBUG: $mod: $module_key", 14);
805 # check if module can open msg envelope with module key
806 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
807 if( (not defined $msg) || (not defined $msg_hash) ) {
808 next;
809 } else {
810 $module = $mod;
811 daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 18);
812 last;
813 }
814 }
816 if( (!$msg) || (!$msg_hash) || (!$module)) {
817 daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 14);
818 }
820 return ($msg, $msg_hash, $module);
821 }
824 sub create_ciphering {
825 my ($passwd) = @_;
826 if((!defined($passwd)) || length($passwd)==0) {
827 $passwd = "";
828 }
829 $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
830 my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
831 my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
832 $my_cipher->set_iv($iv);
833 return $my_cipher;
834 }
837 sub encrypt_msg {
838 my ($msg, $key) = @_;
839 my $my_cipher = &create_ciphering($key);
840 my $len;
841 {
842 use bytes;
843 $len= 16-length($msg)%16;
844 }
845 $msg = "\0"x($len).$msg;
846 $msg = $my_cipher->encrypt($msg);
847 chomp($msg = &encode_base64($msg));
848 # there are no newlines allowed inside msg
849 $msg=~ s/\n//g;
850 return $msg;
851 }
854 sub decrypt_msg {
856 my ($msg, $key) = @_ ;
857 $msg = &decode_base64($msg);
858 my $my_cipher = &create_ciphering($key);
859 $msg = $my_cipher->decrypt($msg);
860 $msg =~ s/\0*//g;
861 return $msg;
862 }
865 sub get_encrypt_key {
866 my ($target) = @_ ;
867 my $encrypt_key;
868 my $error = 0;
870 # target can be in known_server
871 if( not defined $encrypt_key ) {
872 my $sql_statement= "SELECT * FROM known_server WHERE hostname='$target'";
873 my $query_res = $known_server_db->select_dbentry( $sql_statement );
874 while( my ($hit_num, $hit) = each %{ $query_res } ) {
875 my $host_name = $hit->{hostname};
876 if( $host_name ne $target ) {
877 next;
878 }
879 $encrypt_key = $hit->{hostkey};
880 last;
881 }
882 }
884 # target can be in known_client
885 if( not defined $encrypt_key ) {
886 my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$target'";
887 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
888 while( my ($hit_num, $hit) = each %{ $query_res } ) {
889 my $host_name = $hit->{hostname};
890 if( $host_name ne $target ) {
891 next;
892 }
893 $encrypt_key = $hit->{hostkey};
894 last;
895 }
896 }
898 return $encrypt_key;
899 }
902 #=== FUNCTION ================================================================
903 # NAME: open_socket
904 # PARAMETERS: PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
905 # [PeerPort] string necessary if port not appended by PeerAddr
906 # RETURNS: socket IO::Socket::INET
907 # DESCRIPTION: open a socket to PeerAddr
908 #===============================================================================
909 sub open_socket {
910 my ($PeerAddr, $PeerPort) = @_ ;
911 if(defined($PeerPort)){
912 $PeerAddr = $PeerAddr.":".$PeerPort;
913 }
914 my $socket;
915 $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
916 Porto => "tcp",
917 Type => SOCK_STREAM,
918 Timeout => 5,
919 );
920 if(not defined $socket) {
921 return;
922 }
923 # &daemon_log("DEBUG: open_socket: $PeerAddr", 7);
924 return $socket;
925 }
928 sub send_msg_to_target {
929 my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
930 my $error = 0;
931 my $header;
932 my $timestamp = &get_time();
933 my $new_status;
934 my $act_status;
935 my ($sql_statement, $res);
937 if( $msg_header ) {
938 $header = "'$msg_header'-";
939 } else {
940 $header = "";
941 }
943 # Memorize own source address
944 my $own_source_address = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
945 $own_source_address .= ":".$server_port;
947 # Patch 0.0.0.0 source to real address
948 $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$own_source_address<\/source>/s;
949 # Patch GOSA source to real address and add forward_to_gosa tag
950 $msg =~ s/<source>GOSA<\/source>/<source>$own_source_address<\/source> <forward_to_gosa>$own_source_address,$session_id<\/forward_to_gosa>/ ;
952 # encrypt xml msg
953 my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
955 # opensocket
956 my $socket = &open_socket($address);
957 if( !$socket ) {
958 daemon_log("$session_id ERROR: Cannot open socket to host '$address'. Message processing aborted!", 1);
959 $error++;
960 }
962 if( $error == 0 ) {
963 # send xml msg
964 print $socket $crypted_msg.";$own_source_address\n";
965 daemon_log("$session_id INFO: send ".$header."msg to $address", 5);
966 daemon_log("$session_id DEBUG: message:\n$msg", 12);
968 }
970 # close socket in any case
971 if( $socket ) {
972 close $socket;
973 }
975 if( $error > 0 ) { $new_status = "down"; }
976 else { $new_status = $msg_header; }
979 # known_clients
980 $sql_statement = "SELECT * FROM $known_clients_tn WHERE hostname='$address'";
981 $res = $known_clients_db->select_dbentry($sql_statement);
982 if( keys(%$res) == 1) {
983 $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
984 if ($act_status eq "down" && $new_status eq "down") {
985 $sql_statement = "DELETE FROM known_clients WHERE hostname='$address'";
986 $res = $known_clients_db->del_dbentry($sql_statement);
987 daemon_log("$session_id WARNING: failed 2x to send msg to host '$address', delete host from known_clients", 3);
988 } else {
989 $sql_statement = "UPDATE known_clients SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
990 $res = $known_clients_db->update_dbentry($sql_statement);
991 if($new_status eq "down"){
992 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
993 } else {
994 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
995 }
996 }
997 }
999 # known_server
1000 $sql_statement = "SELECT * FROM $known_server_tn WHERE hostname='$address'";
1001 $res = $known_server_db->select_dbentry($sql_statement);
1002 if( keys(%$res) == 1) {
1003 $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
1004 if ($act_status eq "down" && $new_status eq "down") {
1005 $sql_statement = "DELETE FROM known_server WHERE hostname='$address'";
1006 $res = $known_server_db->del_dbentry($sql_statement);
1007 daemon_log("$session_id WARNING: failed 2x to send a message to host '$address', delete host from known_server", 3);
1008 }
1009 else {
1010 $sql_statement = "UPDATE known_server SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
1011 $res = $known_server_db->update_dbentry($sql_statement);
1012 if($new_status eq "down"){
1013 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
1014 } else {
1015 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
1016 }
1017 }
1018 }
1019 return $error;
1020 }
1023 sub update_jobdb_status_for_send_msgs {
1024 my ($session_id, $answer, $error) = @_;
1025 if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
1026 &daemon_log("$session_id DEBUG: try to update job status", 138);
1027 my $jobdb_id = $1;
1029 $answer =~ /<header>(.*)<\/header>/;
1030 my $job_header = $1;
1032 $answer =~ /<target>(.*)<\/target>/;
1033 my $job_target = $1;
1035 # Sending msg failed
1036 if( $error ) {
1038 # Set jobs to done, jobs do not need to deliver their message in any case
1039 if (($job_header eq "trigger_action_localboot")
1040 ||($job_header eq "trigger_action_lock")
1041 ||($job_header eq "trigger_action_halt")
1042 ) {
1043 my $sql_statement = "UPDATE $job_queue_tn SET status='done' WHERE id=$jobdb_id";
1044 my $res = $job_db->update_dbentry($sql_statement);
1046 # Reactivate jobs, jobs need to deliver their message
1047 } elsif (($job_header eq "trigger_action_activate")
1048 ||($job_header eq "trigger_action_update")
1049 ||($job_header eq "trigger_action_reinstall")
1050 ||($job_header eq "trigger_activate_new")
1051 ) {
1052 &reactivate_job_with_delay($session_id, $job_target, $job_header, 30 );
1054 # For all other messages
1055 } else {
1056 my $sql_statement = "UPDATE $job_queue_tn ".
1057 "SET status='error', result='can not deliver msg, please consult log file' ".
1058 "WHERE id=$jobdb_id";
1059 my $res = $job_db->update_dbentry($sql_statement);
1060 }
1062 # Sending msg was successful
1063 } else {
1064 # Set jobs localboot, lock, activate, halt, reboot and wake to done
1065 # jobs reinstall, update, inst_update do themself setting to done
1066 if (($job_header eq "trigger_action_localboot")
1067 ||($job_header eq "trigger_action_lock")
1068 ||($job_header eq "trigger_action_activate")
1069 ||($job_header eq "trigger_action_halt")
1070 ||($job_header eq "trigger_action_reboot")
1071 ||($job_header eq "trigger_action_wake")
1072 ||($job_header eq "trigger_wake")
1073 ) {
1075 my $sql_statement = "UPDATE $job_queue_tn ".
1076 "SET status='done' ".
1077 "WHERE id=$jobdb_id AND status='processed'";
1078 my $res = $job_db->update_dbentry($sql_statement);
1079 } else {
1080 &daemon_log("$session_id DEBUG: sending message succeed but cannot update job status.", 138);
1081 }
1082 }
1083 } else {
1084 &daemon_log("$session_id DEBUG: cannot update job status, msg has no jobdb_id-tag.", 138);
1085 }
1086 }
1088 sub reactivate_job_with_delay {
1089 my ($session_id, $target, $header, $delay) = @_ ;
1090 # Sometimes the client is still booting or does not wake up, in this case reactivate the job (if it exists) with a delay of n sec
1092 if (not defined $delay) { $delay = 30 } ;
1093 my $delay_timestamp = &calc_timestamp(&get_time(), "plus", $delay);
1095 my $sql = "UPDATE $job_queue_tn Set timestamp='$delay_timestamp', status='waiting' WHERE (macaddress LIKE 'target' AND headertag='$header')";
1096 my $res = $job_db->update_dbentry($sql);
1097 daemon_log("$session_id INFO: '$header'-job will be reactivated at '$delay_timestamp' ".
1098 "cause client '$target' is currently not available", 5);
1099 return;
1100 }
1103 sub sig_handler {
1104 my ($kernel, $signal) = @_[KERNEL, ARG0] ;
1105 daemon_log("0 INFO got signal '$signal'", 1);
1106 $kernel->sig_handled();
1107 return;
1108 }
1111 sub msg_to_decrypt {
1112 my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
1113 my $session_id = $session->ID;
1114 my ($msg, $msg_hash, $module);
1115 my $error = 0;
1117 # fetch new msg out of @msgs_to_decrypt
1118 my $tmp_next_msg = shift @msgs_to_decrypt;
1119 my ($next_msg, $msg_source) = split(/;/, $tmp_next_msg);
1121 # msg is from a new client or gosa
1122 ($msg, $msg_hash, $module) = &input_from_unknown_host($next_msg, $session_id);
1124 # msg is from a gosa-si-server
1125 if(((!$msg) || (!$msg_hash) || (!$module)) && ($serverPackages_enabled eq "true")){
1126 if (not defined $msg_source)
1127 {
1128 # Only needed, to be compatible with older gosa-si-server versions
1129 ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $heap->{'remote_ip'}, $session_id);
1130 }
1131 else
1132 {
1133 ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $msg_source, $session_id);
1134 }
1135 }
1136 # msg is from a gosa-si-client
1137 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1138 ($msg, $msg_hash, $module) = &input_from_known_client($next_msg, $msg_source, $session_id);
1139 }
1140 # an error occurred
1141 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1142 # If an incoming msg could not be decrypted (maybe a wrong key), decide if msg comes from a client
1143 # or a server. In case of a client, send a ping. If the client could not understand a msg from its
1144 # server the client cause a re-registering process. In case of a server, decrease update_time in kown_server_db
1145 # and trigger a re-registering process for servers
1146 if (defined $msg_source && $msg_source =~ /:$server_port$/ && $serverPackages_enabled eq "true")
1147 {
1148 daemon_log("$session_id WARNING: Cannot understand incoming msg from server '$msg_source'. Cause re-registration process for servers.", 3);
1149 my $update_statement = "UPDATE $known_server_tn SET update_time='19700101000000' WHERE hostname='$msg_source'";
1150 daemon_log("$session_id DEBUG: $update_statement", 7);
1151 my $upadte_res = $known_server_db->exec_statement($update_statement);
1152 $kernel->yield("register_at_foreign_servers");
1153 }
1154 elsif ((defined $msg_source) && (not $msg_source =~ /:$server_port$/))
1155 {
1156 daemon_log("$session_id WARNING: Cannot understand incoming msg from client '$msg_source'. Send ping-msg to cause a re-registering of the client if necessary", 3);
1157 #my $remote_ip = $heap->{'remote_ip'};
1158 #my $remote_port = $heap->{'remote_port'};
1159 my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source><target>$msg_source</target></xml>";
1160 my ($test_error, $test_error_string) = &send_msg_to_target($ping_msg, "$msg_source", "dummy-key", "gosa_ping", $session_id);
1161 daemon_log("$session_id WARNING: Sending msg to cause re-registering: $ping_msg", 3);
1162 }
1163 else
1164 {
1165 my $foreign_host = defined $msg_source ? $msg_source : $heap->{'remote_ip'};
1166 daemon_log("$session_id ERROR: Incoming message from host '$foreign_host' cannot be understood. Processing aborted!", 1);
1167 daemon_log("$session_id DEBUG: Aborted message: $tmp_next_msg", 11);
1168 }
1170 $error++
1171 }
1174 my $header;
1175 my $target;
1176 my $source;
1177 my $done = 0;
1178 my $sql;
1179 my $res;
1181 # check whether this message should be processed here
1182 if ($error == 0) {
1183 $header = @{$msg_hash->{'header'}}[0];
1184 $target = @{$msg_hash->{'target'}}[0];
1185 $source = @{$msg_hash->{'source'}}[0];
1186 my $not_found_in_known_clients_db = 0;
1187 my $not_found_in_known_server_db = 0;
1188 my $not_found_in_foreign_clients_db = 0;
1189 my $local_address;
1190 my $local_mac;
1191 my ($target_ip, $target_port) = split(':', $target);
1193 # Determine the local ip address if target is an ip address
1194 if ($target =~ /^\d+\.\d+\.\d+\.\d+:\d+$/) {
1195 $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1196 } else {
1197 $local_address = $server_address;
1198 }
1200 # Determine the local mac address if target is a mac address
1201 if ($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) {
1202 my $loc_ip = &get_local_ip_for_remote_ip($heap->{'remote_ip'});
1203 my $network_interface= &get_interface_for_ip($loc_ip);
1204 $local_mac = &get_mac_for_interface($network_interface);
1205 } else {
1206 $local_mac = $server_mac_address;
1207 }
1209 # target and source is equal to GOSA -> process here
1210 if (not $done) {
1211 if ($target eq "GOSA" && $source eq "GOSA") {
1212 $done = 1;
1213 &daemon_log("$session_id DEBUG: target and source is 'GOSA' -> process '$header' here", 11);
1214 }
1215 }
1217 # target is own address without forward_to_gosa-tag -> process here
1218 if (not $done) {
1219 #if ((($target eq $local_address) || ($target eq $local_mac) ) && (not exists $msg_hash->{'forward_to_gosa'})) {
1220 if (($target eq $local_address) && (not exists $msg_hash->{'forward_to_gosa'})) {
1221 $done = 1;
1222 if ($source eq "GOSA") {
1223 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1224 }
1225 &daemon_log("$session_id DEBUG: target is own address without forward_to_gosa-tag -> process '$header' here", 11);
1226 }
1227 }
1229 # target is own address with forward_to_gosa-tag not pointing to myself -> process here
1230 if (not $done) {
1231 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1232 my $gosa_at;
1233 my $gosa_session_id;
1234 if (($target eq $local_address) && (defined $forward_to_gosa)){
1235 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1236 if ($gosa_at ne $local_address) {
1237 $done = 1;
1238 &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag not pointing to myself -> process '$header' here", 11);
1239 }
1240 }
1241 }
1243 # Target is a client address and there is a processing function within a plugin -> process loaclly
1244 if (not $done)
1245 {
1246 # Check if target is a client address
1247 $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1248 $res = $known_clients_db->select_dbentry($sql);
1249 if ((keys(%$res) > 0) )
1250 {
1251 my $hostname = $res->{1}->{'hostname'};
1252 my $reduced_header = $header;
1253 $reduced_header =~ s/gosa_//;
1254 # Check if there is a processing function within a plugin
1255 if (exists $known_functions->{$reduced_header})
1256 {
1257 $msg =~ s/<target>\S*<\/target>/<target>$hostname<\/target>/;
1258 $done = 1;
1259 &daemon_log("$session_id DEBUG: Target is client address with processing function within a plugin -> process '$header' here", 11);
1260 }
1261 }
1262 }
1264 # If header has a 'job_' prefix, do always process message locally
1265 # which means put it into job queue
1266 if ((not $done) && ($header =~ /job_/))
1267 {
1268 $done = 1;
1269 &daemon_log("$session_id DEBUG: Header has a 'job_' prefix. Put it into job queue. -> process '$header' here", 11);
1270 }
1272 # if message should be processed here -> add message to incoming_db
1273 if ($done) {
1274 # if a 'job_' or a 'gosa_' message comes from a foreign server, fake module from
1275 # ServerPackages to GosaPackages so gosa-si-server knows how to process this kind of messages
1276 if ($header =~ /^gosa_/ || $header =~ /^job_/) {
1277 $module = "GosaPackages";
1278 }
1280 my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1281 primkey=>[],
1282 headertag=>$header,
1283 targettag=>$target,
1284 xmlmessage=>&encode_base64($msg),
1285 timestamp=>&get_time,
1286 module=>$module,
1287 sessionid=>$session_id,
1288 } );
1290 }
1292 # target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa
1293 if (not $done) {
1294 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1295 my $gosa_at;
1296 my $gosa_session_id;
1297 if (($target eq $local_address) && (defined $forward_to_gosa)){
1298 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1299 if ($gosa_at eq $local_address) {
1300 my $session_reference = $kernel->ID_id_to_session($gosa_session_id);
1301 if( defined $session_reference ) {
1302 $heap = $session_reference->get_heap();
1303 }
1304 if(exists $heap->{'client'}) {
1305 $msg = &encrypt_msg($msg, $GosaPackages_key);
1306 $heap->{'client'}->put($msg);
1307 &daemon_log("$session_id DEBUG: incoming '$header' message forwarded to GOsa", 11);
1308 }
1309 $done = 1;
1310 &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag pointing at myself -> forward '$header' to gosa", 11);
1311 }
1312 }
1314 }
1316 # target is a client address in known_clients -> forward to client
1317 if (not $done) {
1318 $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1319 $res = $known_clients_db->select_dbentry($sql);
1320 if (keys(%$res) > 0)
1321 {
1322 $done = 1;
1323 &daemon_log("$session_id DEBUG: target is a client address in known_clients -> forward '$header' to client", 11);
1324 my $hostkey = $res->{1}->{'hostkey'};
1325 my $hostname = $res->{1}->{'hostname'};
1326 $msg =~ s/<target>\S*<\/target>/<target>$hostname<\/target>/;
1327 $msg =~ s/<header>gosa_/<header>/;
1328 my $error= &send_msg_to_target($msg, $hostname, $hostkey, $header, $session_id);
1329 if ($error) {
1330 &daemon_log("$session_id ERROR: Some problems occurred while trying to send msg to client '$hostkey': $msg", 1);
1331 }
1332 }
1333 else
1334 {
1335 $not_found_in_known_clients_db = 1;
1336 }
1337 }
1339 # target is a client address in foreign_clients -> forward to registration server
1340 if (not $done) {
1341 $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1342 $res = $foreign_clients_db->select_dbentry($sql);
1343 if (keys(%$res) > 0) {
1344 my $hostname = $res->{1}->{'hostname'};
1345 my ($host_ip, $host_port) = split(/:/, $hostname);
1346 my $local_address = &get_local_ip_for_remote_ip($host_ip).":$server_port";
1347 my $regserver = $res->{1}->{'regserver'};
1348 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$regserver'";
1349 my $res = $known_server_db->select_dbentry($sql);
1350 if (keys(%$res) > 0) {
1351 my $regserver_key = $res->{1}->{'hostkey'};
1352 $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1353 $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1354 if ($source eq "GOSA") {
1355 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1356 }
1357 my $error= &send_msg_to_target($msg, $regserver, $regserver_key, $header, $session_id);
1358 if ($error) {
1359 &daemon_log("$session_id ERROR: some problems occurred while trying to send msg to registration server: $msg", 1);
1360 }
1361 }
1362 $done = 1;
1363 &daemon_log("$session_id DEBUG: target is a client address in foreign_clients -> forward '$header' to registration server", 11);
1364 } else {
1365 $not_found_in_foreign_clients_db = 1;
1366 }
1367 }
1369 # target is a server address -> forward to server
1370 if (not $done) {
1371 $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1372 $res = $known_server_db->select_dbentry($sql);
1373 if (keys(%$res) > 0) {
1374 my $hostkey = $res->{1}->{'hostkey'};
1376 if ($source eq "GOSA") {
1377 $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1378 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1380 }
1382 &send_msg_to_target($msg, $target, $hostkey, $header, $session_id);
1383 $done = 1;
1384 &daemon_log("$session_id DEBUG: target is a server address -> forward '$header' to server", 11);
1385 } else {
1386 $not_found_in_known_server_db = 1;
1387 }
1388 }
1391 # target is not in foreign_clients_db, known_server_db or known_clients_db, maybe it is a complete new one -> process here
1392 if ( $not_found_in_foreign_clients_db
1393 && $not_found_in_known_server_db
1394 && $not_found_in_known_clients_db) {
1395 &daemon_log("$session_id DEBUG: target is not in foreign_clients_db, known_server_db or known_clients_db, maybe it is a complete new one -> process '$header' here", 11);
1396 if ($header =~ /^gosa_/ || $header =~ /^job_/) {
1397 $module = "GosaPackages";
1398 }
1399 my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1400 primkey=>[],
1401 headertag=>$header,
1402 targettag=>$target,
1403 xmlmessage=>&encode_base64($msg),
1404 timestamp=>&get_time,
1405 module=>$module,
1406 sessionid=>$session_id,
1407 } );
1408 $done = 1;
1409 }
1412 if (not $done) {
1413 daemon_log("$session_id ERROR: do not know what to do with this message: $msg", 1);
1414 if ($source eq "GOSA") {
1415 my %data = ('error_msg' => &encode_base64($msg), 'error_string' => "Do not know what to do with this message!");
1416 my $error_msg = &build_msg("error", $local_address, "GOSA", \%data );
1418 my $session_reference = $kernel->ID_id_to_session($session_id);
1419 if( defined $session_reference ) {
1420 $heap = $session_reference->get_heap();
1421 }
1422 if(exists $heap->{'client'}) {
1423 $error_msg = &encrypt_msg($error_msg, $GosaPackages_key);
1424 $heap->{'client'}->put($error_msg);
1425 }
1426 }
1427 }
1429 }
1431 return;
1432 }
1435 sub next_task {
1436 my ($session, $heap, $task) = @_[SESSION, HEAP, ARG0, ARG1];
1437 my $running_task = POE::Wheel::Run->new(
1438 Program => sub { process_task($session, $heap, $task) },
1439 StdioFilter => POE::Filter::Reference->new(),
1440 StdoutEvent => "task_result",
1441 StderrEvent => "task_debug",
1442 CloseEvent => "task_done",
1443 );
1444 $heap->{task}->{ $running_task->ID } = $running_task;
1445 }
1447 sub handle_task_result {
1448 my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
1449 my $client_answer = $result->{'answer'};
1450 if( $client_answer =~ s/session_id=(\d+)$// ) {
1451 my $session_id = $1;
1452 if( defined $session_id ) {
1453 my $session_reference = $kernel->ID_id_to_session($session_id);
1454 if( defined $session_reference ) {
1455 $heap = $session_reference->get_heap();
1456 }
1457 }
1459 if(exists $heap->{'client'}) {
1460 $heap->{'client'}->put($client_answer);
1461 }
1462 }
1463 $kernel->sig(CHLD => "child_reap");
1464 }
1466 sub handle_task_debug {
1467 my $result = $_[ARG0];
1468 print STDERR "$result\n";
1469 }
1471 sub handle_task_done {
1472 my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1473 delete $heap->{task}->{$task_id};
1474 if (exists $heap->{ldap_handle}->{$task_id}) {
1475 &release_ldap_handle($heap->{ldap_handle}->{$task_id});
1476 }
1477 }
1479 sub process_task {
1480 no strict "refs";
1481 #CHECK: Not @_[...]?
1482 my ($session, $heap, $task) = @_;
1483 my $error = 0;
1484 my $answer_l;
1485 my ($answer_header, @answer_target_l, $answer_source);
1486 my $client_answer = "";
1488 # prepare all variables needed to process message
1489 #my $msg = $task->{'xmlmessage'};
1490 my $msg = &decode_base64($task->{'xmlmessage'});
1491 my $incoming_id = $task->{'id'};
1492 my $module = $task->{'module'};
1493 my $header = $task->{'headertag'};
1494 my $session_id = $task->{'sessionid'};
1495 my $msg_hash;
1496 eval {
1497 $msg_hash = $xml->XMLin($msg, ForceArray=>1);
1498 };
1499 daemon_log("ERROR: XML failure '$@'") if ($@);
1500 my $source = @{$msg_hash->{'source'}}[0];
1502 # set timestamp of incoming client uptodate, so client will not
1503 # be deleted from known_clients because of expiration
1504 my $cur_time = &get_time();
1505 my $sql = "UPDATE $known_clients_tn SET timestamp='$cur_time' WHERE hostname='$source'";
1506 my $res = $known_clients_db->exec_statement($sql);
1508 ######################
1509 # process incoming msg
1510 if( $error == 0) {
1511 daemon_log("$session_id INFO: Incoming msg (session_id=$session_id) with header '".@{$msg_hash->{'header'}}[0]."'", 5);
1512 daemon_log("$session_id DEBUG: Processing module ".$module, 26);
1513 $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1515 if ( 0 < @{$answer_l} ) {
1516 my $answer_str = join("\n", @{$answer_l});
1517 my @headers;
1518 while ($answer_str =~ /<header>(\w+)<\/header>/g) {
1519 push(@headers, $1);
1520 }
1521 daemon_log("$session_id INFO: got answer message(s) with header: '".join("', '", @headers)."'", 5);
1522 daemon_log("$session_id DEBUG: $module: got answer from module: \n".$answer_str,26);
1523 } else {
1524 daemon_log("$session_id DEBUG: $module: got no answer from module!" ,26);
1525 }
1527 }
1528 if( !$answer_l ) { $error++ };
1530 ########
1531 # answer
1532 if( $error == 0 ) {
1534 foreach my $answer ( @{$answer_l} ) {
1535 # check outgoing msg to xml validity
1536 my ($answer, $answer_hash) = &check_outgoing_xml_validity($answer, $session_id);
1537 if( not defined $answer_hash ) { next; }
1539 $answer_header = @{$answer_hash->{'header'}}[0];
1540 @answer_target_l = @{$answer_hash->{'target'}};
1541 $answer_source = @{$answer_hash->{'source'}}[0];
1543 # deliver msg to all targets
1544 foreach my $answer_target ( @answer_target_l ) {
1546 # targets of msg are all gosa-si-clients in known_clients_db
1547 if( $answer_target eq "*" ) {
1548 # answer is for all clients
1549 my $sql_statement= "SELECT * FROM known_clients";
1550 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1551 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1552 my $host_name = $hit->{hostname};
1553 my $host_key = $hit->{hostkey};
1554 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1555 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1556 }
1557 }
1559 # targets of msg are all gosa-si-server in known_server_db
1560 elsif( $answer_target eq "KNOWN_SERVER" ) {
1561 # answer is for all server in known_server
1562 my $sql_statement= "SELECT * FROM $known_server_tn";
1563 my $query_res = $known_server_db->select_dbentry( $sql_statement );
1564 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1565 my $host_name = $hit->{hostname};
1566 my $host_key = $hit->{hostkey};
1567 $answer =~ s/<target>\S+<\/target>/<target>$host_name<\/target>/g;
1568 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1569 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1570 }
1571 }
1573 # target of msg is GOsa
1574 elsif( $answer_target eq "GOSA" ) {
1575 my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1576 my $add_on = "";
1577 if( defined $session_id ) {
1578 $add_on = ".session_id=$session_id";
1579 }
1580 # answer is for GOSA and has to returned to connected client
1581 my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1582 $client_answer = $gosa_answer.$add_on;
1583 }
1585 # target of msg is job queue at this host
1586 elsif( $answer_target eq "JOBDB") {
1587 $answer =~ /<header>(\S+)<\/header>/;
1588 my $header;
1589 if( defined $1 ) { $header = $1; }
1590 my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1591 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1592 }
1594 # Target of msg is a mac address
1595 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 ) {
1596 daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients and foreign_clients", 5);
1598 # Looking for macaddress in known_clients
1599 my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1600 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1601 my $found_ip_flag = 0;
1602 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1603 my $host_name = $hit->{hostname};
1604 my $host_key = $hit->{hostkey};
1605 $answer =~ s/$answer_target/$host_name/g;
1606 daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1607 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1608 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1609 $found_ip_flag++ ;
1610 }
1612 # Looking for macaddress in foreign_clients
1613 if ($found_ip_flag == 0) {
1614 my $sql = "SELECT * FROM $foreign_clients_tn WHERE macaddress LIKE '$answer_target'";
1615 my $res = $foreign_clients_db->select_dbentry($sql);
1616 while( my ($hit_num, $hit) = each %{ $res } ) {
1617 my $host_name = $hit->{hostname};
1618 my $reg_server = $hit->{regserver};
1619 daemon_log("$session_id INFO: found host '$host_name' with mac '$answer_target', registered at '$reg_server'", 5);
1621 # Fetch key for reg_server
1622 my $reg_server_key;
1623 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$reg_server'";
1624 my $res = $known_server_db->select_dbentry($sql);
1625 if (exists $res->{1}) {
1626 $reg_server_key = $res->{1}->{'hostkey'};
1627 } else {
1628 daemon_log("$session_id ERROR: cannot find hostkey for '$host_name' in '$known_server_tn'", 1);
1629 daemon_log("$session_id ERROR: unable to forward answer to correct registration server, processing is aborted!", 1);
1630 $reg_server_key = undef;
1631 }
1633 # Send answer to server where client is registered
1634 if (defined $reg_server_key) {
1635 $answer =~ s/$answer_target/$host_name/g;
1636 my $error = &send_msg_to_target($answer, $reg_server, $reg_server_key, $answer_header, $session_id);
1637 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1638 $found_ip_flag++ ;
1639 }
1640 }
1641 }
1643 # No mac to ip matching found
1644 if( $found_ip_flag == 0) {
1645 daemon_log("$session_id WARNING: no host found in known_clients or foreign_clients with mac address '$answer_target'", 3);
1646 &reactivate_job_with_delay($session_id, $answer_target, $answer_header, 30);
1647 }
1649 # Answer is for one specific host
1650 } else {
1651 # get encrypt_key
1652 my $encrypt_key = &get_encrypt_key($answer_target);
1653 if( not defined $encrypt_key ) {
1654 # unknown target
1655 daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1656 next;
1657 }
1658 my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1659 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1660 }
1661 }
1662 }
1663 }
1665 my $filter = POE::Filter::Reference->new();
1666 my %result = (
1667 status => "seems ok to me",
1668 answer => $client_answer,
1669 );
1671 my $output = $filter->put( [ \%result ] );
1672 print @$output;
1675 }
1677 sub session_start {
1678 my ($kernel) = $_[KERNEL];
1679 $global_kernel = $kernel;
1680 $kernel->yield('register_at_foreign_servers');
1681 $kernel->yield('create_fai_server_db', $fai_server_tn );
1682 $kernel->yield('create_fai_release_db', $fai_release_tn );
1683 $kernel->yield('watch_for_next_tasks');
1684 $kernel->sig(USR1 => "sig_handler");
1685 $kernel->sig(USR2 => "recreate_packages_db");
1686 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1687 $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay);
1688 $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay);
1689 $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1690 $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1691 $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1692 $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1694 # Start opsi check
1695 if ($opsi_enabled eq "true") {
1696 $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay);
1697 }
1699 }
1702 sub watch_for_done_jobs {
1703 #CHECK: $heap for what?
1704 my ($kernel,$heap) = @_[KERNEL, HEAP];
1706 my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((status='done') AND (modified='0'))";
1707 my $res = $job_db->select_dbentry( $sql_statement );
1709 while( my ($id, $hit) = each %{$res} ) {
1710 my $jobdb_id = $hit->{id};
1711 my $sql_statement = "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1712 my $res = $job_db->del_dbentry($sql_statement);
1713 }
1715 $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1716 }
1719 sub watch_for_opsi_jobs {
1720 my ($kernel) = $_[KERNEL];
1722 # This is not very nice to look for opsi install jobs, but headertag has to be trigger_action_reinstall. The only way to identify a
1723 # opsi install job is to parse the xml message. There is still the correct header.
1724 my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((xmlmessage LIKE '%opsi_install_client</header>%') AND (status='processing') AND (siserver='localhost'))";
1725 my $res = $job_db->select_dbentry( $sql_statement );
1727 # Ask OPSI for an update of the running jobs
1728 while (my ($id, $hit) = each %$res ) {
1729 # Determine current parameters of the job
1730 my $hostId = $hit->{'plainname'};
1731 my $macaddress = $hit->{'macaddress'};
1732 my $progress = $hit->{'progress'};
1734 my $result= {};
1736 # For hosts, only return the products that are or get installed
1737 my $callobj;
1738 $callobj = {
1739 method => 'getProductStates_hash',
1740 params => [ $hostId ],
1741 id => 1,
1742 };
1744 my $hres = $opsi_client->call($opsi_url, $callobj);
1745 #my ($hres_err, $hres_err_string) = &check_opsi_res($hres);
1746 if (not &check_opsi_res($hres)) {
1747 my $htmp= $hres->result->{$hostId};
1749 # Check state != not_installed or action == setup -> load and add
1750 my $products= 0;
1751 my $installed= 0;
1752 my $installing = 0;
1753 my $error= 0;
1754 my @installed_list;
1755 my @error_list;
1756 my $act_status = "none";
1757 foreach my $product (@{$htmp}){
1759 if ($product->{'installationStatus'} ne "not_installed" or
1760 $product->{'actionRequest'} eq "setup"){
1762 # Increase number of products for this host
1763 $products++;
1765 if ($product->{'installationStatus'} eq "failed"){
1766 $result->{$product->{'productId'}}= "error";
1767 unshift(@error_list, $product->{'productId'});
1768 $error++;
1769 }
1770 if ($product->{'installationStatus'} eq "installed" && $product->{'actionRequest'} eq "none"){
1771 $result->{$product->{'productId'}}= "installed";
1772 unshift(@installed_list, $product->{'productId'});
1773 $installed++;
1774 }
1775 if ($product->{'installationStatus'} eq "installing"){
1776 $result->{$product->{'productId'}}= "installing";
1777 $installing++;
1778 $act_status = "installing - ".$product->{'productId'};
1779 }
1780 }
1781 }
1783 # Estimate "rough" progress, avoid division by zero
1784 if ($products == 0) {
1785 $result->{'progress'}= 0;
1786 } else {
1787 $result->{'progress'}= int($installed * 100 / $products);
1788 }
1790 # Set updates in job queue
1791 if ((not $error) && (not $installing) && ($installed)) {
1792 $act_status = "installed - ".join(", ", @installed_list);
1793 }
1794 if ($error) {
1795 $act_status = "error - ".join(", ", @error_list);
1796 }
1797 if ($progress ne $result->{'progress'} ) {
1798 # Updating progress and result
1799 my $update_statement = "UPDATE $job_queue_tn SET modified='1', progress='".$result->{'progress'}."', result='$act_status' WHERE macaddress LIKE '$macaddress' AND siserver='localhost'";
1800 my $update_res = $job_db->update_dbentry($update_statement);
1801 }
1802 if ($progress eq 100) {
1803 # Updateing status
1804 my $done_statement = "UPDATE $job_queue_tn SET modified='1', ";
1805 if ($error) {
1806 $done_statement .= "status='error'";
1807 } else {
1808 $done_statement .= "status='done'";
1809 }
1810 $done_statement .= " WHERE macaddress LIKE '$macaddress' AND siserver='localhost'";
1811 my $done_res = $job_db->update_dbentry($done_statement);
1812 }
1815 }
1816 }
1818 $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay);
1819 }
1822 # If a job got an update or was modified anyway, send to all other si-server an update message of this jobs.
1823 sub watch_for_modified_jobs {
1824 my ($kernel,$heap) = @_[KERNEL, HEAP];
1826 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE (modified='1')";
1827 my $res = $job_db->select_dbentry( $sql_statement );
1829 # if db contains no jobs which should be update, do nothing
1830 if (keys %$res != 0) {
1832 if ($job_synchronization eq "true") {
1833 # make out of the db result a gosa-si message
1834 my $update_msg = &db_res2si_msg ($res, "foreign_job_updates", "KNOWN_SERVER", "MY_LOCAL_ADDRESS");
1836 # update all other SI-server
1837 &inform_all_other_si_server($update_msg);
1838 }
1840 # set jobs all jobs to modified = 0, wait until the next modification for updates of other si-server
1841 $sql_statement = "UPDATE $job_queue_tn SET modified='0' ";
1842 $res = $job_db->update_dbentry($sql_statement);
1843 }
1845 $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay);
1846 }
1849 sub watch_for_new_jobs {
1850 if($watch_for_new_jobs_in_progress == 0) {
1851 $watch_for_new_jobs_in_progress = 1;
1852 my ($kernel,$heap) = @_[KERNEL, HEAP];
1854 # check gosa job queue for jobs with executable timestamp
1855 my $timestamp = &get_time();
1856 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE siserver='localhost' AND status='waiting' AND (CAST(timestamp AS UNSIGNED)) < $timestamp ORDER BY timestamp";
1857 my $res = $job_db->exec_statement( $sql_statement );
1859 # Merge all new jobs that would do the same actions
1860 my @drops;
1861 my $hits;
1862 foreach my $hit (reverse @{$res} ) {
1863 my $macaddress= lc @{$hit}[8];
1864 my $headertag= @{$hit}[5];
1865 if(
1866 defined($hits->{$macaddress}) &&
1867 defined($hits->{$macaddress}->{$headertag}) &&
1868 defined($hits->{$macaddress}->{$headertag}[0])
1869 ) {
1870 push @drops, "DELETE FROM $job_queue_tn WHERE id = $hits->{$macaddress}->{$headertag}[0]";
1871 }
1872 $hits->{$macaddress}->{$headertag}= $hit;
1873 }
1875 # Delete new jobs with a matching job in state 'processing'
1876 foreach my $macaddress (keys %{$hits}) {
1877 foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1878 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1879 if(defined($jobdb_id)) {
1880 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND headertag='$jobdb_headertag' AND status='processing'";
1881 my $res = $job_db->exec_statement( $sql_statement );
1882 foreach my $hit (@{$res}) {
1883 push @drops, "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1884 }
1885 } else {
1886 daemon_log("J ERROR: Job without id exists for macaddress $macaddress!", 1);
1887 }
1888 }
1889 }
1891 # Commit deletion
1892 $job_db->exec_statementlist(\@drops);
1894 # Look for new jobs that could be executed
1895 foreach my $macaddress (keys %{$hits}) {
1897 # Look if there is an executing job
1898 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='processing'";
1899 my $res = $job_db->exec_statement( $sql_statement );
1901 # Skip new jobs for host if there is a processing job
1902 if(defined($res) and defined @{$res}[0]) {
1903 # Prevent race condition if there is a trigger_activate job waiting and a goto-activation job processing
1904 my $row = @{$res}[0] if (ref $res eq 'ARRAY');
1905 if(@{$row}[5] eq 'trigger_action_reinstall') {
1906 my $sql_statement_2 = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='waiting' AND headertag = 'trigger_activate_new'";
1907 my $res_2 = $job_db->exec_statement( $sql_statement_2 );
1908 if(defined($res_2) and defined @{$res_2}[0]) {
1909 # Set status from goto-activation to 'waiting' and update timestamp
1910 $job_db->exec_statement("UPDATE $job_queue_tn SET status='waiting', timestamp='".&calc_timestamp(&get_time(), 'plus', 30)."' WHERE macaddress LIKE '$macaddress' AND headertag = 'trigger_action_reinstall'");
1911 }
1912 }
1913 next;
1914 }
1916 foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1917 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1918 if(defined($jobdb_id)) {
1919 my $job_msg = @{$hits->{$macaddress}->{$jobdb_headertag}}[7];
1921 daemon_log("J DEBUG: its time to execute $job_msg", 7);
1922 my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1923 my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1925 # expect macaddress is unique!!!!!!
1926 my $target = $res_hash->{1}->{hostname};
1928 # change header
1929 $job_msg =~ s/<header>job_/<header>gosa_/;
1931 # add sqlite_id
1932 $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1934 $job_msg =~ /<header>(\S+)<\/header>/;
1935 my $header = $1 ;
1936 my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");
1938 # update status in job queue to ...
1939 # ... 'processing', for jobs: 'reinstall', 'update'
1940 if (($header =~ /gosa_trigger_action_reinstall/)
1941 || ($header =~ /gosa_trigger_activate_new/)
1942 || ($header =~ /gosa_trigger_action_update/)) {
1943 my $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
1944 my $dbres = $job_db->update_dbentry($sql_statement);
1945 }
1947 # ... 'done', for all other jobs, they are no longer needed in the jobqueue
1948 else {
1949 my $sql_statement = "UPDATE $job_queue_tn SET status='done' WHERE id=$jobdb_id";
1950 my $dbres = $job_db->update_dbentry($sql_statement);
1951 }
1954 # We don't want parallel processing
1955 last;
1956 }
1957 }
1958 }
1960 $watch_for_new_jobs_in_progress = 0;
1961 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1962 }
1963 }
1966 sub watch_for_new_messages {
1967 my ($kernel,$heap) = @_[KERNEL, HEAP];
1968 my @coll_user_msg; # collection list of outgoing messages
1970 # check messaging_db for new incoming messages with executable timestamp
1971 my $timestamp = &get_time();
1972 my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS UNSIGNED))<$timestamp AND flag='n' AND direction='in' )";
1973 my $res = $messaging_db->exec_statement( $sql_statement );
1974 foreach my $hit (@{$res}) {
1976 # create outgoing messages
1977 my $message_to = @{$hit}[3];
1978 # translate message_to to plain login name
1979 my @message_to_l = split(/,/, $message_to);
1980 my %receiver_h;
1981 foreach my $receiver (@message_to_l) {
1982 if ($receiver =~ /^u_([\s\S]*)$/) {
1983 $receiver_h{$1} = 0;
1984 } elsif ($receiver =~ /^g_([\s\S]*)$/) {
1985 my $group_name = $1;
1986 # fetch all group members from ldap and add them to receiver hash
1987 my $ldap_handle = &get_ldap_handle();
1988 if (defined $ldap_handle) {
1989 my $mesg = $ldap_handle->search(
1990 base => $ldap_base,
1991 scope => 'sub',
1992 attrs => ['memberUid'],
1993 filter => "cn=$group_name",
1994 );
1995 if ($mesg->count) {
1996 my @entries = $mesg->entries;
1997 foreach my $entry (@entries) {
1998 my @receivers= $entry->get_value("memberUid");
1999 foreach my $receiver (@receivers) {
2000 $receiver_h{$receiver} = 0;
2001 }
2002 }
2003 }
2004 # translating errors ?
2005 if ($mesg->code) {
2006 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: $mesg->error", 1);
2007 }
2008 &release_ldap_handle($ldap_handle);
2009 # ldap handle error ?
2010 } else {
2011 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: no ldap handle available", 1);
2012 }
2013 } else {
2014 my $sbjct = &encode_base64(@{$hit}[1]);
2015 my $msg = &encode_base64(@{$hit}[7]);
2016 &daemon_log("M WARNING: unknown receiver '$receiver' for a user-message '$sbjct - $msg'", 3);
2017 }
2018 }
2019 my @receiver_l = keys(%receiver_h);
2021 my $message_id = @{$hit}[0];
2023 #add each outgoing msg to messaging_db
2024 my $receiver;
2025 foreach $receiver (@receiver_l) {
2026 my $sql_statement = "INSERT INTO $messaging_tn (id, subject, message_from, message_to, flag, direction, delivery_time, message, timestamp) ".
2027 "VALUES ('".
2028 $message_id."', '". # id
2029 @{$hit}[1]."', '". # subject
2030 @{$hit}[2]."', '". # message_from
2031 $receiver."', '". # message_to
2032 "none"."', '". # flag
2033 "out"."', '". # direction
2034 @{$hit}[6]."', '". # delivery_time
2035 @{$hit}[7]."', '". # message
2036 $timestamp."'". # timestamp
2037 ")";
2038 &daemon_log("M DEBUG: $sql_statement", 1);
2039 my $res = $messaging_db->exec_statement($sql_statement);
2040 &daemon_log("M INFO: message '".@{$hit}[0]."' is prepared for delivery to receiver '$receiver'", 5);
2041 }
2043 # set incoming message to flag d=deliverd
2044 $sql_statement = "UPDATE $messaging_tn SET flag='p' WHERE id='$message_id'";
2045 &daemon_log("M DEBUG: $sql_statement", 7);
2046 $res = $messaging_db->update_dbentry($sql_statement);
2047 &daemon_log("M INFO: message '$message_id' is set to flag 'p' (processed)", 5);
2048 }
2050 $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
2051 return;
2052 }
2054 sub watch_for_delivery_messages {
2055 my ($kernel, $heap) = @_[KERNEL, HEAP];
2057 # select outgoing messages
2058 my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( flag='p' AND direction='out' )";
2059 my $res = $messaging_db->exec_statement( $sql_statement );
2061 # build out msg for each usr
2062 foreach my $hit (@{$res}) {
2063 my $receiver = @{$hit}[3];
2064 my $msg_id = @{$hit}[0];
2065 my $subject = @{$hit}[1];
2066 my $message = @{$hit}[7];
2068 # resolve usr -> host where usr is logged in
2069 my $sql = "SELECT * FROM $login_users_tn WHERE (user='$receiver')";
2070 my $res = $login_users_db->exec_statement($sql);
2072 # receiver is logged in nowhere
2073 if (not ref(@$res[0]) eq "ARRAY") { next; }
2075 # receiver ist logged in at a client registered at local server
2076 my $send_succeed = 0;
2077 foreach my $hit (@$res) {
2078 my $receiver_host = @$hit[0];
2079 my $delivered2host = 0;
2080 &daemon_log("M DEBUG: user '$receiver' is logged in at host '$receiver_host'", 7);
2082 # Looking for host in know_clients_db
2083 my $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$receiver_host')";
2084 my $res = $known_clients_db->exec_statement($sql);
2086 # Host is known in known_clients_db
2087 if (ref(@$res[0]) eq "ARRAY") {
2088 my $receiver_key = @{@{$res}[0]}[2];
2089 my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
2090 my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data );
2091 my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0);
2092 if ($error == 0 ) {
2093 $send_succeed++ ;
2094 $delivered2host++ ;
2095 &daemon_log("M DEBUG: send message for user '$receiver' to host '$receiver_host'", 7);
2096 } else {
2097 &daemon_log("M DEBUG: cannot send message for user '$receiver' to host '$receiver_host'", 7);
2098 }
2099 }
2101 # Message already send, do not need to do anything more, otherwise ...
2102 if ($delivered2host) { next;}
2104 # ...looking for host in foreign_clients_db
2105 $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$receiver_host')";
2106 $res = $foreign_clients_db->exec_statement($sql);
2108 # Host is known in foreign_clients_db
2109 if (ref(@$res[0]) eq "ARRAY") {
2110 my $registration_server = @{@{$res}[0]}[2];
2112 # Fetch encryption key for registration server
2113 my $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$registration_server')";
2114 my $res = $known_server_db->exec_statement($sql);
2115 if (ref(@$res[0]) eq "ARRAY") {
2116 my $registration_server_key = @{@{$res}[0]}[3];
2117 my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
2118 my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data );
2119 my $error = &send_msg_to_target($out_msg, $registration_server, $registration_server_key, "usr_msg", 0);
2120 if ($error == 0 ) {
2121 $send_succeed++ ;
2122 $delivered2host++ ;
2123 &daemon_log("M DEBUG: send message for user '$receiver' to server '$registration_server'", 7);
2124 } else {
2125 &daemon_log("M ERROR: cannot send message for user '$receiver' to server '$registration_server'", 1);
2126 }
2128 } else {
2129 &daemon_log("M ERROR: host '$receiver_host' is reported to be ".
2130 "registrated at server '$registration_server', ".
2131 "but no data available in known_server_db ", 1);
2132 }
2133 }
2135 if (not $delivered2host) {
2136 &daemon_log("M ERROR: unable to send user message to host '$receiver_host'", 1);
2137 }
2138 }
2140 if ($send_succeed) {
2141 # set outgoing msg at db to deliverd
2142 my $sql = "UPDATE $messaging_tn SET flag='d' WHERE (id='$msg_id' AND direction='out' AND message_to='$receiver')";
2143 my $res = $messaging_db->exec_statement($sql);
2144 &daemon_log("M INFO: send message for user '$receiver' to logged in hosts", 5);
2145 } else {
2146 &daemon_log("M WARNING: failed to deliver message for user '$receiver'", 3);
2147 }
2148 }
2150 $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
2151 return;
2152 }
2155 sub watch_for_done_messages {
2156 my ($kernel,$heap) = @_[KERNEL, HEAP];
2158 my $sql = "SELECT * FROM $messaging_tn WHERE (flag='p' AND direction='in')";
2159 my $res = $messaging_db->exec_statement($sql);
2161 foreach my $hit (@{$res}) {
2162 my $msg_id = @{$hit}[0];
2164 my $sql = "SELECT * FROM $messaging_tn WHERE (id='$msg_id' AND direction='out' AND (NOT flag='s'))";
2165 my $res = $messaging_db->exec_statement($sql);
2167 # not all usr msgs have been seen till now
2168 if ( ref(@$res[0]) eq "ARRAY") { next; }
2170 $sql = "DELETE FROM $messaging_tn WHERE (id='$msg_id')";
2171 $res = $messaging_db->exec_statement($sql);
2173 }
2175 $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
2176 return;
2177 }
2180 sub watch_for_old_known_clients {
2181 my ($kernel,$heap) = @_[KERNEL, HEAP];
2183 my $sql_statement = "SELECT * FROM $known_clients_tn";
2184 my $res = $known_clients_db->select_dbentry( $sql_statement );
2186 my $cur_time = int(&get_time());
2188 while ( my ($hit_num, $hit) = each %$res) {
2189 my $expired_timestamp = int($hit->{'timestamp'});
2190 $expired_timestamp =~ /(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/;
2191 my $dt = DateTime->new( year => $1,
2192 month => $2,
2193 day => $3,
2194 hour => $4,
2195 minute => $5,
2196 second => $6,
2197 );
2199 $dt->add( seconds => 2 * int($hit->{'keylifetime'}) );
2200 $expired_timestamp = $dt->ymd('').$dt->hms('');
2201 if ($cur_time > $expired_timestamp) {
2202 my $hostname = $hit->{'hostname'};
2203 my $del_sql = "DELETE FROM $known_clients_tn WHERE hostname='$hostname'";
2204 my $del_res = $known_clients_db->exec_statement($del_sql);
2206 &main::daemon_log("0 INFO: timestamp '".$hit->{'timestamp'}."' of client '$hostname' is expired('$expired_timestamp'), client will be deleted from known_clients_db", 5);
2207 }
2209 }
2211 $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
2212 }
2215 sub watch_for_next_tasks {
2216 my ($kernel,$heap) = @_[KERNEL, HEAP];
2218 my $sql = "SELECT * FROM $incoming_tn";
2219 my $res = $incoming_db->select_dbentry($sql);
2221 while ( my ($hit_num, $hit) = each %$res) {
2222 my $headertag = $hit->{'headertag'};
2223 if ($headertag =~ /^answer_(\d+)/) {
2224 # do not start processing, this message is for a still running POE::Wheel
2225 next;
2226 }
2227 my $message_id = $hit->{'id'};
2228 my $session_id = $hit->{'sessionid'};
2229 &daemon_log("$session_id DEBUG: start processing for message with incoming id: '$message_id'", 11);
2231 $kernel->yield('next_task', $hit);
2233 my $sql = "DELETE FROM $incoming_tn WHERE id=$message_id";
2234 my $res = $incoming_db->exec_statement($sql);
2235 }
2237 $kernel->delay_set('watch_for_next_tasks', 1);
2238 }
2241 sub get_ldap_handle {
2242 my ($session_id) = @_;
2243 my $heap;
2245 if (not defined $session_id ) { $session_id = 0 };
2246 if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
2248 my ($package, $file, $row, $subroutine, $hasArgs, $wantArray, $evalText, $isRequire) = caller(1);
2249 my $caller_text = "subroutine $subroutine";
2250 if ($subroutine eq "(eval)") {
2251 $caller_text = "eval block within file '$file' for '$evalText'";
2252 }
2253 daemon_log("$session_id DEBUG: new ldap handle for '$caller_text' required!", 42);
2255 get_handle:
2256 my $ldap_handle = Net::LDAP->new( $ldap_uri );
2257 if (not ref $ldap_handle) {
2258 daemon_log("$session_id ERROR: Connection to LDAP URI '$ldap_uri' failed! Retrying!", 1);
2259 usleep(100000);
2260 goto get_handle;
2261 } else {
2262 daemon_log("$session_id DEBUG: Connection to LDAP URI '$ldap_uri' established.", 42);
2263 }
2265 $ldap_handle->bind($ldap_admin_dn, password => $ldap_admin_password) or &daemon_log("$session_id ERROR: Could not bind as '$ldap_admin_dn' to LDAP URI '$ldap_uri'!", 1);
2266 return $ldap_handle;
2267 }
2270 sub release_ldap_handle {
2271 my ($ldap_handle, $session_id) = @_ ;
2272 if (not defined $session_id ) { $session_id = 0 };
2274 if(ref $ldap_handle) {
2275 $ldap_handle->disconnect();
2276 }
2277 &main::daemon_log("$session_id DEBUG: Released a ldap handle!", 42);
2278 return;
2279 }
2282 sub change_fai_state {
2283 my ($st, $targets, $session_id) = @_;
2284 $session_id = 0 if not defined $session_id;
2285 # Set FAI state to localboot
2286 my %mapActions= (
2287 reboot => '',
2288 update => 'softupdate',
2289 localboot => 'localboot',
2290 reinstall => 'install',
2291 rescan => '',
2292 wake => '',
2293 memcheck => 'memcheck',
2294 sysinfo => 'sysinfo',
2295 install => 'install',
2296 );
2298 # Return if this is unknown
2299 if (!exists $mapActions{ $st }){
2300 daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1);
2301 return;
2302 }
2304 my $state= $mapActions{ $st };
2306 # Build search filter for hosts
2307 my $search= "(&(objectClass=GOhard)";
2308 foreach (@{$targets}){
2309 $search.= "(macAddress=$_)";
2310 }
2311 $search.= ")";
2313 # If there's any host inside of the search string, procress them
2314 if (!($search =~ /macAddress/)){
2315 daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);
2316 return;
2317 }
2319 my $ldap_handle = &get_ldap_handle($session_id);
2320 # Perform search for Unit Tag
2321 my $mesg = $ldap_handle->search(
2322 base => $ldap_base,
2323 scope => 'sub',
2324 attrs => ['dn', 'FAIstate', 'objectClass'],
2325 filter => "$search"
2326 );
2328 if ($mesg->count) {
2329 my @entries = $mesg->entries;
2330 if (0 == @entries) {
2331 daemon_log("$session_id ERROR: ldap search failed: ldap_base=$ldap_base, filter=$search", 1);
2332 }
2334 foreach my $entry (@entries) {
2335 # Only modify entry if it is not set to '$state'
2336 if ($entry->get_value("FAIstate") ne "$state"){
2337 daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
2338 my $result;
2339 my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
2340 if (exists $tmp{'FAIobject'}){
2341 if ($state eq ''){
2342 $result= $ldap_handle->modify($entry->dn, changes => [ delete => [ FAIstate => [] ] ]);
2343 } else {
2344 $result= $ldap_handle->modify($entry->dn, changes => [ replace => [ FAIstate => $state ] ]);
2345 }
2346 } elsif ($state ne ''){
2347 $result= $ldap_handle->modify($entry->dn, changes => [ add => [ objectClass => 'FAIobject' ], add => [ FAIstate => $state ] ]);
2348 }
2350 # Errors?
2351 if ($result->code){
2352 daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2353 }
2354 } else {
2355 daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 42);
2356 }
2357 }
2358 } else {
2359 daemon_log("$session_id ERROR: LDAP search failed: ldap_base=$ldap_base, filter=$search", 1);
2360 }
2361 &release_ldap_handle($ldap_handle, $session_id);
2363 return;
2364 }
2367 sub change_goto_state {
2368 my ($st, $targets, $session_id) = @_;
2369 $session_id = 0 if not defined $session_id;
2371 # Switch on or off?
2372 my $state= $st eq 'active' ? 'active': 'locked';
2374 my $ldap_handle = &get_ldap_handle($session_id);
2375 if( defined($ldap_handle) ) {
2377 # Build search filter for hosts
2378 my $search= "(&(objectClass=GOhard)";
2379 foreach (@{$targets}){
2380 $search.= "(macAddress=$_)";
2381 }
2382 $search.= ")";
2384 # If there's any host inside of the search string, procress them
2385 if (!($search =~ /macAddress/)){
2386 &release_ldap_handle($ldap_handle);
2387 return;
2388 }
2390 # Perform search for Unit Tag
2391 my $mesg = $ldap_handle->search(
2392 base => $ldap_base,
2393 scope => 'sub',
2394 attrs => ['dn', 'gotoMode'],
2395 filter => "$search"
2396 );
2398 if ($mesg->count) {
2399 my @entries = $mesg->entries;
2400 foreach my $entry (@entries) {
2402 # Only modify entry if it is not set to '$state'
2403 if ($entry->get_value("gotoMode") ne $state){
2405 daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
2406 my $result;
2407 $result= $ldap_handle->modify($entry->dn, changes => [replace => [ gotoMode => $state ] ]);
2409 # Errors?
2410 if ($result->code){
2411 &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2412 }
2414 }
2415 }
2416 } else {
2417 daemon_log("$session_id ERROR: LDAP search failed in function change_goto_state: ldap_base=$ldap_base, filter=$search", 1);
2418 }
2420 }
2421 &release_ldap_handle($ldap_handle, $session_id);
2422 return;
2423 }
2426 sub run_recreate_packages_db {
2427 my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2428 my $session_id = $session->ID;
2429 &main::daemon_log("$session_id INFO: Recreating FAI Packages DB ('$fai_release_tn', '$fai_server_tn', '$packages_list_tn')", 5);
2430 $kernel->yield('create_fai_release_db', $fai_release_tn);
2431 $kernel->yield('create_fai_server_db', $fai_server_tn);
2432 return;
2433 }
2436 sub run_create_fai_server_db {
2437 my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2438 my $session_id = $session->ID;
2439 my $task = POE::Wheel::Run->new(
2440 Program => sub { &create_fai_server_db($table_name,$kernel, undef, $session_id) },
2441 StdoutEvent => "session_run_result",
2442 StderrEvent => "session_run_debug",
2443 CloseEvent => "session_run_done",
2444 );
2446 $heap->{task}->{ $task->ID } = $task;
2447 return;
2448 }
2451 sub create_fai_server_db {
2452 my ($table_name, $kernel, $dont_create_packages_list, $session_id) = @_;
2453 my $result;
2455 if (not defined $session_id) { $session_id = 0; }
2456 my $ldap_handle = &get_ldap_handle($session_id);
2457 if(defined($ldap_handle)) {
2458 daemon_log("$session_id INFO: create_fai_server_db: start", 5);
2459 my $mesg= $ldap_handle->search(
2460 base => $ldap_base,
2461 scope => 'sub',
2462 attrs => ['FAIrepository', 'gosaUnitTag'],
2463 filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
2464 );
2465 if($mesg->{'resultCode'} == 0 &&
2466 $mesg->count != 0) {
2467 foreach my $entry (@{$mesg->{entries}}) {
2468 if($entry->exists('FAIrepository')) {
2469 # Add an entry for each Repository configured for server
2470 foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
2471 my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
2472 my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
2473 $result= $fai_server_db->add_dbentry( {
2474 table => $table_name,
2475 primkey => ['server', 'fai_release', 'tag'],
2476 server => $tmp_url,
2477 fai_release => $tmp_release,
2478 sections => $tmp_sections,
2479 tag => (length($tmp_tag)>0)?$tmp_tag:"",
2480 } );
2481 }
2482 }
2483 }
2484 }
2485 daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
2486 &release_ldap_handle($ldap_handle);
2488 # TODO: Find a way to post the 'create_packages_list_db' event
2489 if(not defined($dont_create_packages_list)) {
2490 &create_packages_list_db(undef, $session_id);
2491 }
2492 }
2494 return $result;
2495 }
2498 sub run_create_fai_release_db {
2499 my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2500 my $session_id = $session->ID;
2501 my $task = POE::Wheel::Run->new(
2502 Program => sub { &create_fai_release_db($table_name, $session_id) },
2503 StdoutEvent => "session_run_result",
2504 StderrEvent => "session_run_debug",
2505 CloseEvent => "session_run_done",
2506 );
2508 $heap->{task}->{ $task->ID } = $task;
2509 return;
2510 }
2513 sub create_fai_release_db {
2514 my ($table_name, $session_id) = @_;
2515 my $result;
2517 # used for logging
2518 if (not defined $session_id) { $session_id = 0; }
2520 my $ldap_handle = &get_ldap_handle($session_id);
2521 if(defined($ldap_handle)) {
2522 daemon_log("$session_id INFO: create_fai_release_db: start",5);
2523 my $mesg= $ldap_handle->search(
2524 base => $ldap_base,
2525 scope => 'sub',
2526 attrs => [],
2527 filter => "(&(objectClass=organizationalUnit)(ou=fai))",
2528 );
2529 if(($mesg->code == 0) && ($mesg->count != 0))
2530 {
2531 daemon_log("$session_id DEBUG: create_fai_release_db: count " . $mesg->count,138);
2533 # Walk through all possible FAI container ou's
2534 my @sql_list;
2535 my $timestamp= &get_time();
2536 foreach my $ou (@{$mesg->{entries}}) {
2537 my $tmp_classes= resolve_fai_classes($ou->dn, $ldap_handle, $session_id);
2538 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
2539 my @tmp_array=get_fai_release_entries($tmp_classes);
2540 if(@tmp_array) {
2541 foreach my $entry (@tmp_array) {
2542 if(defined($entry) && ref($entry) eq 'HASH') {
2543 my $sql=
2544 "INSERT INTO $table_name "
2545 ."(timestamp, fai_release, class, type, state) VALUES ("
2546 .$timestamp.","
2547 ."'".$entry->{'release'}."',"
2548 ."'".$entry->{'class'}."',"
2549 ."'".$entry->{'type'}."',"
2550 ."'".$entry->{'state'}."')";
2551 push @sql_list, $sql;
2552 }
2553 }
2554 }
2555 }
2556 }
2558 daemon_log("$session_id DEBUG: create_fai_release_db: Inserting ".scalar @sql_list." entries to DB",138);
2559 &release_ldap_handle($ldap_handle);
2560 if(@sql_list) {
2561 unshift @sql_list, "VACUUM";
2562 unshift @sql_list, "DELETE FROM $table_name";
2563 $fai_release_db->exec_statementlist(\@sql_list);
2564 }
2565 daemon_log("$session_id DEBUG: create_fai_release_db: Done with inserting",138);
2566 } else {
2567 daemon_log("$session_id INFO: create_fai_release_db: error: " . $mesg->code, 5);
2568 }
2569 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
2570 }
2571 return $result;
2572 }
2574 sub get_fai_types {
2575 my $tmp_classes = shift || return undef;
2576 my @result;
2578 foreach my $type(keys %{$tmp_classes}) {
2579 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
2580 my $entry = {
2581 type => $type,
2582 state => $tmp_classes->{$type}[0],
2583 };
2584 push @result, $entry;
2585 }
2586 }
2588 return @result;
2589 }
2591 sub get_fai_state {
2592 my $result = "";
2593 my $tmp_classes = shift || return $result;
2595 foreach my $type(keys %{$tmp_classes}) {
2596 if(defined($tmp_classes->{$type}[0])) {
2597 $result = $tmp_classes->{$type}[0];
2599 # State is equal for all types in class
2600 last;
2601 }
2602 }
2604 return $result;
2605 }
2607 sub resolve_fai_classes {
2608 my ($fai_base, $ldap_handle, $session_id) = @_;
2609 if (not defined $session_id) { $session_id = 0; }
2610 my $result;
2611 my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
2612 my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
2613 my $fai_classes;
2615 daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base", 138);
2616 my $mesg= $ldap_handle->search(
2617 base => $fai_base,
2618 scope => 'sub',
2619 attrs => ['cn','objectClass','FAIstate'],
2620 filter => $fai_filter,
2621 );
2622 daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries", 138);
2624 if($mesg->{'resultCode'} == 0 &&
2625 $mesg->count != 0) {
2626 foreach my $entry (@{$mesg->{entries}}) {
2627 if($entry->exists('cn')) {
2628 my $tmp_dn= $entry->dn();
2629 $tmp_dn= substr( $tmp_dn, 0, length($tmp_dn)
2630 - length($fai_base) - 1 );
2632 # Skip classname and ou dn parts for class
2633 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?)$/;
2635 # Skip classes without releases
2636 if((!defined($tmp_release)) || length($tmp_release)==0) {
2637 next;
2638 }
2640 my $tmp_cn= $entry->get_value('cn');
2641 my $tmp_state= $entry->get_value('FAIstate');
2643 my $tmp_type;
2644 # Get FAI type
2645 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
2646 if(grep $_ eq $oclass, @possible_fai_classes) {
2647 $tmp_type= $oclass;
2648 last;
2649 }
2650 }
2652 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2653 # A Subrelease
2654 my @sub_releases = split(/,/, $tmp_release);
2656 # Walk through subreleases and build hash tree
2657 my $hash;
2658 while(my $tmp_sub_release = pop @sub_releases) {
2659 $hash .= "\{'$tmp_sub_release'\}->";
2660 }
2661 eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
2662 } else {
2663 # A branch, no subrelease
2664 push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
2665 }
2666 } elsif (!$entry->exists('cn')) {
2667 my $tmp_dn= $entry->dn();
2668 $tmp_dn= substr( $tmp_dn, 0, length($tmp_dn)
2669 - length($fai_base) - 1 );
2670 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?)$/;
2672 # Skip classes without releases
2673 if((!defined($tmp_release)) || length($tmp_release)==0) {
2674 next;
2675 }
2677 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2678 # A Subrelease
2679 my @sub_releases= split(/,/, $tmp_release);
2681 # Walk through subreleases and build hash tree
2682 my $hash;
2683 while(my $tmp_sub_release = pop @sub_releases) {
2684 $hash .= "\{'$tmp_sub_release'\}->";
2685 }
2686 # Remove the last two characters
2687 chop($hash);
2688 chop($hash);
2690 eval('$fai_classes->'.$hash.'= {}');
2691 } else {
2692 # A branch, no subrelease
2693 if(!exists($fai_classes->{$tmp_release})) {
2694 $fai_classes->{$tmp_release} = {};
2695 }
2696 }
2697 }
2698 }
2700 # The hash is complete, now we can honor the copy-on-write based missing entries
2701 foreach my $release (keys %$fai_classes) {
2702 $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
2703 }
2704 }
2705 return $result;
2706 }
2708 sub apply_fai_inheritance {
2709 my $fai_classes = shift || return {};
2710 my $tmp_classes;
2712 # Get the classes from the branch
2713 foreach my $class (keys %{$fai_classes}) {
2714 # Skip subreleases
2715 if($class =~ /^ou=.*$/) {
2716 next;
2717 } else {
2718 $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
2719 }
2720 }
2722 # Apply to each subrelease
2723 foreach my $subrelease (keys %{$fai_classes}) {
2724 if($subrelease =~ /ou=/) {
2725 foreach my $tmp_class (keys %{$tmp_classes}) {
2726 if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
2727 $fai_classes->{$subrelease}->{$tmp_class} =
2728 deep_copy($tmp_classes->{$tmp_class});
2729 } else {
2730 foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
2731 if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
2732 $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
2733 deep_copy($tmp_classes->{$tmp_class}->{$type});
2734 }
2735 }
2736 }
2737 }
2738 }
2739 }
2741 # Find subreleases in deeper levels
2742 foreach my $subrelease (keys %{$fai_classes}) {
2743 if($subrelease =~ /ou=/) {
2744 foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
2745 if($subsubrelease =~ /ou=/) {
2746 apply_fai_inheritance($fai_classes->{$subrelease});
2747 }
2748 }
2749 }
2750 }
2752 return $fai_classes;
2753 }
2755 sub get_fai_release_entries {
2756 my $tmp_classes = shift || return;
2757 my $parent = shift || "";
2758 my @result = shift || ();
2760 foreach my $entry (keys %{$tmp_classes}) {
2761 if(defined($entry)) {
2762 if($entry =~ /^ou=.*$/) {
2763 my $release_name = $entry;
2764 $release_name =~ s/ou=//g;
2765 if(length($parent)>0) {
2766 $release_name = $parent."/".$release_name;
2767 }
2768 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2769 foreach my $bufentry(@bufentries) {
2770 push @result, $bufentry;
2771 }
2772 } else {
2773 my @types = get_fai_types($tmp_classes->{$entry});
2774 foreach my $type (@types) {
2775 push @result,
2776 {
2777 'class' => $entry,
2778 'type' => $type->{'type'},
2779 'release' => $parent,
2780 'state' => $type->{'state'},
2781 };
2782 }
2783 }
2784 }
2785 }
2787 return @result;
2788 }
2790 sub deep_copy {
2791 my $this = shift;
2792 if (not ref $this) {
2793 $this;
2794 } elsif (ref $this eq "ARRAY") {
2795 [map deep_copy($_), @$this];
2796 } elsif (ref $this eq "HASH") {
2797 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2798 } else { die "what type is $_?" }
2799 }
2802 sub session_run_result {
2803 my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];
2804 $kernel->sig(CHLD => "child_reap");
2805 }
2807 sub session_run_debug {
2808 my $result = $_[ARG0];
2809 print STDERR "$result\n";
2810 }
2812 sub session_run_done {
2813 my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2814 delete $heap->{task}->{$task_id};
2815 if (exists $heap->{ldap_handle}->{$task_id}) {
2816 &release_ldap_handle($heap->{ldap_handle}->{$task_id});
2817 }
2818 delete $heap->{ldap_handle}->{$task_id};
2819 }
2822 sub create_sources_list {
2823 my $session_id = shift || 0;
2824 my $result="/tmp/gosa_si_tmp_sources_list";
2826 # Remove old file
2827 if(stat($result)) {
2828 unlink($result);
2829 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7);
2830 }
2832 my $fh;
2833 open($fh, ">$result");
2834 if (not defined $fh) {
2835 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7);
2836 return undef;
2837 }
2838 if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2839 my $ldap_handle = &get_ldap_handle($session_id);
2840 my $mesg=$ldap_handle->search(
2841 base => $main::ldap_server_dn,
2842 scope => 'base',
2843 attrs => 'FAIrepository',
2844 filter => 'objectClass=FAIrepositoryServer'
2845 );
2846 if($mesg->count) {
2847 foreach my $entry(@{$mesg->{'entries'}}) {
2848 foreach my $value(@{$entry->get_value('FAIrepository', asref => 1)}) {
2849 my ($server, $tag, $release, $sections)= split /\|/, $value;
2850 my $line = "deb $server $release";
2851 $sections =~ s/,/ /g;
2852 $line.= " $sections";
2853 print $fh $line."\n";
2854 }
2855 }
2856 }
2857 &release_ldap_handle($ldap_handle);
2858 } else {
2859 if (defined $main::ldap_server_dn){
2860 &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1);
2861 } else {
2862 &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2863 }
2864 }
2865 close($fh);
2867 return $result;
2868 }
2871 sub run_create_packages_list_db {
2872 my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2873 my $session_id = $session->ID;
2874 my $task = POE::Wheel::Run->new(
2875 Priority => +20,
2876 Program => sub {&create_packages_list_db(undef, $session_id)},
2877 StdoutEvent => "session_run_result",
2878 StderrEvent => "session_run_debug",
2879 CloseEvent => "session_run_done",
2880 );
2881 $heap->{task}->{ $task->ID } = $task;
2882 }
2885 sub create_packages_list_db {
2886 my ($sources_file, $session_id) = @_;
2888 # it should not be possible to trigger a recreation of packages_list_db
2889 # while packages_list_db is under construction, so set flag packages_list_under_construction
2890 # which is tested befor recreation can be started
2891 if (-r $packages_list_under_construction) {
2892 daemon_log("$session_id WARNING: packages_list_db is right now under construction, please wait until this process is finished", 3);
2893 return;
2894 } else {
2895 daemon_log("$session_id INFO: create_packages_list_db: start", 5);
2896 # set packages_list_under_construction to true
2897 system("touch $packages_list_under_construction");
2898 @packages_list_statements=();
2899 }
2901 if (not defined $session_id) { $session_id = 0; }
2903 if (not defined $sources_file) {
2904 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5);
2905 $sources_file = &create_sources_list($session_id);
2906 }
2908 if (not defined $sources_file) {
2909 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1);
2910 unlink($packages_list_under_construction);
2911 return;
2912 }
2914 my $line;
2916 open(CONFIG, "<$sources_file") or do {
2917 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2918 unlink($packages_list_under_construction);
2919 return;
2920 };
2922 # Read lines
2923 while ($line = <CONFIG>){
2924 # Unify
2925 chop($line);
2926 $line =~ s/^\s+//;
2927 $line =~ s/^\s+/ /;
2929 # Strip comments
2930 $line =~ s/#.*$//g;
2932 # Skip empty lines
2933 if ($line =~ /^\s*$/){
2934 next;
2935 }
2937 # Interpret deb line
2938 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2939 my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2940 my $section;
2941 foreach $section (split(' ', $sections)){
2942 &parse_package_info( $baseurl, $dist, $section, $session_id );
2943 }
2944 }
2945 }
2947 close (CONFIG);
2949 if(keys(%repo_dirs)) {
2950 find(\&cleanup_and_extract, keys( %repo_dirs ));
2951 &main::strip_packages_list_statements();
2952 $packages_list_db->exec_statementlist(\@packages_list_statements);
2953 }
2954 unlink($packages_list_under_construction);
2955 daemon_log("$session_id INFO: create_packages_list_db: finished", 5);
2956 return;
2957 }
2959 # This function should do some intensive task to minimize the db-traffic
2960 sub strip_packages_list_statements {
2961 my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2962 my @new_statement_list=();
2963 my $hash;
2964 my $insert_hash;
2965 my $update_hash;
2966 my $delete_hash;
2967 my $known_packages_hash;
2968 my $local_timestamp=get_time();
2970 foreach my $existing_entry (@existing_entries) {
2971 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
2972 }
2974 foreach my $statement (@packages_list_statements) {
2975 if($statement =~ /^INSERT/i) {
2976 # Assign the values from the insert statement
2977 my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~
2978 /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
2979 if(exists($hash->{$distribution}->{$package}->{$version})) {
2980 # If section or description has changed, update the DB
2981 if(
2982 (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or
2983 (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
2984 ) {
2985 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
2986 } else {
2987 # package is already present in database. cache this knowledge for later use
2988 @{$known_packages_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2989 }
2990 } else {
2991 # Insert a non-existing entry to db
2992 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2993 }
2994 } elsif ($statement =~ /^UPDATE/i) {
2995 my ($template,$package,$version) = ($1,$2,$3) if $statement =~
2996 /^update\s+?$main::packages_list_tn\s+?set\s+?template\s*?=\s*?'(.*?)'\s+?where\s+?package\s*?=\s*?'(.*?)'\s+?and\s+?version\s*?=\s*?'(.*?)'\s*?;$/si;
2997 foreach my $distribution (keys %{$hash}) {
2998 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
2999 # update the insertion hash to execute only one query per package (insert instead insert+update)
3000 @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
3001 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
3002 if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
3003 my $section;
3004 my $description;
3005 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
3006 length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
3007 $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
3008 }
3009 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
3010 $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
3011 }
3012 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
3013 }
3014 }
3015 }
3016 }
3017 }
3019 # Check for orphaned entries
3020 foreach my $existing_entry (@existing_entries) {
3021 my $distribution= @{$existing_entry}[0];
3022 my $package= @{$existing_entry}[1];
3023 my $version= @{$existing_entry}[2];
3024 my $section= @{$existing_entry}[3];
3026 if(
3027 exists($insert_hash->{$distribution}->{$package}->{$version}) ||
3028 exists($update_hash->{$distribution}->{$package}->{$version}) ||
3029 exists($known_packages_hash->{$distribution}->{$package}->{$version})
3030 ) {
3031 next;
3032 } else {
3033 # Insert entry to delete hash
3034 @{$delete_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section);
3035 }
3036 }
3038 # unroll the insert hash
3039 foreach my $distribution (keys %{$insert_hash}) {
3040 foreach my $package (keys %{$insert_hash->{$distribution}}) {
3041 foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
3042 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
3043 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
3044 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
3045 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
3046 ."'$local_timestamp')";
3047 }
3048 }
3049 }
3051 # unroll the update hash
3052 foreach my $distribution (keys %{$update_hash}) {
3053 foreach my $package (keys %{$update_hash->{$distribution}}) {
3054 foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
3055 my $set = "";
3056 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
3057 $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
3058 }
3059 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
3060 $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
3061 }
3062 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
3063 $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
3064 }
3065 if(defined($set) and length($set) > 0) {
3066 $set .= "timestamp = '$local_timestamp'";
3067 } else {
3068 next;
3069 }
3070 push @new_statement_list,
3071 "UPDATE $main::packages_list_tn SET $set WHERE"
3072 ." distribution = '$distribution'"
3073 ." AND package = '$package'"
3074 ." AND version = '$version'";
3075 }
3076 }
3077 }
3079 # unroll the delete hash
3080 foreach my $distribution (keys %{$delete_hash}) {
3081 foreach my $package (keys %{$delete_hash->{$distribution}}) {
3082 foreach my $version (keys %{$delete_hash->{$distribution}->{$package}}) {
3083 my $section = @{$delete_hash->{$distribution}->{$package}->{$version}}[3];
3084 push @new_statement_list, "DELETE FROM $main::packages_list_tn WHERE distribution='$distribution' AND package='$package' AND version='$version' AND section='$section'";
3085 }
3086 }
3087 }
3089 unshift(@new_statement_list, "VACUUM");
3091 @packages_list_statements = @new_statement_list;
3092 }
3095 sub parse_package_info {
3096 my ($baseurl, $dist, $section, $session_id)= @_;
3097 my ($package);
3098 if (not defined $session_id) { $session_id = 0; }
3099 my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
3100 $repo_dirs{ "${repo_path}/pool" } = 1;
3102 foreach $package ("Packages.gz"){
3103 daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
3104 get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
3105 parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
3106 }
3108 }
3111 sub get_package {
3112 my ($url, $dest, $session_id)= @_;
3113 if (not defined $session_id) { $session_id = 0; }
3115 my $tpath = dirname($dest);
3116 -d "$tpath" || mkpath "$tpath";
3118 # This is ugly, but I've no time to take a look at "how it works in perl"
3119 if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
3120 system("gunzip -cd '$dest' > '$dest.in'");
3121 daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 7);
3122 unlink($dest);
3123 daemon_log("$session_id DEBUG: delete file '$dest'", 7);
3124 } else {
3125 daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' into '$dest' failed!", 1);
3126 }
3127 return 0;
3128 }
3131 sub parse_package {
3132 my ($path, $dist, $srv_path, $session_id)= @_;
3133 if (not defined $session_id) { $session_id = 0;}
3134 my ($package, $version, $section, $description);
3135 my $PACKAGES;
3136 my $timestamp = &get_time();
3138 if(not stat("$path.in")) {
3139 daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
3140 return;
3141 }
3143 open($PACKAGES, "<$path.in");
3144 if(not defined($PACKAGES)) {
3145 daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1);
3146 return;
3147 }
3149 # Read lines
3150 while (<$PACKAGES>){
3151 my $line = $_;
3152 # Unify
3153 chop($line);
3155 # Use empty lines as a trigger
3156 if ($line =~ /^\s*$/){
3157 my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
3158 push(@packages_list_statements, $sql);
3159 $package = "none";
3160 $version = "none";
3161 $section = "none";
3162 $description = "none";
3163 next;
3164 }
3166 # Trigger for package name
3167 if ($line =~ /^Package:\s/){
3168 ($package)= ($line =~ /^Package: (.*)$/);
3169 next;
3170 }
3172 # Trigger for version
3173 if ($line =~ /^Version:\s/){
3174 ($version)= ($line =~ /^Version: (.*)$/);
3175 next;
3176 }
3178 # Trigger for description
3179 if ($line =~ /^Description:\s/){
3180 ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
3181 next;
3182 }
3184 # Trigger for section
3185 if ($line =~ /^Section:\s/){
3186 ($section)= ($line =~ /^Section: (.*)$/);
3187 next;
3188 }
3190 # Trigger for filename
3191 if ($line =~ /^Filename:\s/){
3192 my ($filename) = ($line =~ /^Filename: (.*)$/);
3193 store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
3194 next;
3195 }
3196 }
3198 close( $PACKAGES );
3199 unlink( "$path.in" );
3200 }
3203 sub store_fileinfo {
3204 my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
3206 my %fileinfo = (
3207 'package' => $package,
3208 'dist' => $dist,
3209 'version' => $vers,
3210 );
3212 $repo_files{ "${srvdir}/$file" } = \%fileinfo;
3213 }
3216 sub cleanup_and_extract {
3217 my $fileinfo = $repo_files{ $File::Find::name };
3219 if( defined $fileinfo ) {
3220 my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
3221 my $sql;
3222 my $package = $fileinfo->{ 'package' };
3223 my $newver = $fileinfo->{ 'version' };
3225 mkpath($dir);
3226 system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
3228 if( -f "$dir/DEBIAN/templates" ) {
3230 daemon_log("0 DEBUG: Found debconf templates in '$package' - $newver", 7);
3232 my $tmpl= ""; {
3233 local $/=undef;
3234 open FILE, "$dir/DEBIAN/templates";
3235 $tmpl = &encode_base64(<FILE>);
3236 close FILE;
3237 }
3238 rmtree("$dir/DEBIAN/templates");
3240 $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
3241 push @packages_list_statements, $sql;
3242 }
3243 }
3245 return;
3246 }
3249 sub prepare_server_registration
3250 {
3251 # Add foreign server from cfg file
3252 my @foreign_server_list;
3253 if ($foreign_server_string ne "") {
3254 my @cfg_foreign_server_list = split(",", $foreign_server_string);
3255 foreach my $foreign_server (@cfg_foreign_server_list) {
3256 push(@foreign_server_list, $foreign_server);
3257 }
3259 daemon_log("0 INFO: found foreign server in config file: ".join(", ", @foreign_server_list), 5);
3260 }
3262 # Perform a DNS lookup for server registration if flag is true
3263 if ($dns_lookup eq "true") {
3264 # Add foreign server from dns
3265 my @tmp_servers;
3266 if (not $server_domain) {
3267 # Try our DNS Searchlist
3268 for my $domain(get_dns_domains()) {
3269 chomp($domain);
3270 my ($tmp_domains, $error_string) = &get_server_addresses($domain);
3271 if(@$tmp_domains) {
3272 for my $tmp_server(@$tmp_domains) {
3273 push @tmp_servers, $tmp_server;
3274 }
3275 }
3276 }
3277 if(@tmp_servers && length(@tmp_servers)==0) {
3278 daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3279 }
3280 } else {
3281 @tmp_servers = &get_server_addresses($server_domain);
3282 if( 0 == @tmp_servers ) {
3283 daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3284 }
3285 }
3287 daemon_log("0 INFO: found foreign server via DNS ".join(", ", @tmp_servers), 5);
3289 foreach my $server (@tmp_servers) {
3290 unshift(@foreign_server_list, $server);
3291 }
3292 } else {
3293 daemon_log("0 INFO: DNS lookup for server registration is disabled", 5);
3294 }
3296 # eliminate duplicate entries
3297 @foreign_server_list = &del_doubles(@foreign_server_list);
3298 my $all_foreign_server = join(", ", @foreign_server_list);
3299 daemon_log("0 INFO: found foreign server in config file and DNS: '$all_foreign_server'", 5);
3301 # add all found foreign servers to known_server
3302 my $cur_timestamp = &get_time();
3303 foreach my $foreign_server (@foreign_server_list) {
3305 # do not add myself to known_server_db
3306 if (&is_local($foreign_server)) { next; }
3307 ######################################
3309 my $res = $known_server_db->add_dbentry( {table=>$known_server_tn,
3310 primkey=>['hostname'],
3311 hostname=>$foreign_server,
3312 macaddress=>"",
3313 status=>'not_yet_registered',
3314 hostkey=>"none",
3315 loaded_modules => "none",
3316 timestamp=>$cur_timestamp,
3317 update_time=>'19700101000000',
3318 } );
3319 }
3320 }
3322 sub register_at_foreign_servers {
3323 my ($kernel) = $_[KERNEL];
3325 # Update status and update-time of all si-server with expired update_time and
3326 # block them for race conditional registration processes of other si-servers.
3327 my $act_time = &get_time();
3328 my $block_statement = "UPDATE $known_server_tn SET status='new_server',update_time='19700101000000' WHERE (CAST(update_time AS UNSIGNED))<$act_time ";
3329 my $block_res = $known_server_db->exec_statement($block_statement);
3331 # Fetch all si-server from db where update_time is younger than act_time
3332 my $fetch_statement = "SELECT * FROM $known_server_tn WHERE update_time='19700101000000'";
3333 my $fetch_res = $known_server_db->exec_statement($fetch_statement);
3335 # Detect already connected clients. Will be added to registration msg later.
3336 my $client_sql = "SELECT * FROM $known_clients_tn";
3337 my $client_res = $known_clients_db->exec_statement($client_sql);
3339 # Send registration messag to all fetched si-server
3340 foreach my $hit (@$fetch_res) {
3341 my $hostname = @$hit[0];
3342 my $hostkey = &create_passwd;
3344 # Add already connected clients to registration message
3345 my $myhash = &create_xml_hash('new_server', $server_address, $hostname);
3346 &add_content2xml_hash($myhash, 'key', $hostkey);
3347 map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res);
3349 # Add locally loaded gosa-si modules to registration message
3350 my $loaded_modules = {};
3351 while (my ($package, $pck_info) = each %$known_modules) {
3352 next if ((!defined(@$pck_info[2])) || (!(ref (@$pck_info[2]) eq 'HASH')));
3353 foreach my $act_module (keys(%{@$pck_info[2]})) {
3354 $loaded_modules->{$act_module} = "";
3355 }
3356 }
3357 map(&add_content2xml_hash($myhash, "loaded_modules", $_), keys(%$loaded_modules));
3359 # Add macaddress to registration message
3360 my ($host_ip, $host_port) = split(/:/, $hostname);
3361 my $local_ip = &get_local_ip_for_remote_ip($host_ip);
3362 my $network_interface= &get_interface_for_ip($local_ip);
3363 my $host_mac = &get_mac_for_interface($network_interface);
3364 &add_content2xml_hash($myhash, 'macaddress', $host_mac);
3366 # Build registration message and send it
3367 my $foreign_server_msg = &create_xml_string($myhash);
3368 my $error = &send_msg_to_target($foreign_server_msg, $hostname, $ServerPackages_key, "new_server", 0);
3369 }
3372 # After n sec perform a check of all server registration processes
3373 $kernel->delay_set("control_server_registration", 2);
3375 return;
3376 }
3379 sub control_server_registration {
3380 my ($kernel) = $_[KERNEL];
3382 # Check if all registration processes succeed or not
3383 my $select_statement = "SELECT * FROM $known_server_tn WHERE status='new_server'";
3384 my $select_res = $known_server_db->exec_statement($select_statement);
3386 # If at least one registration process failed, maybe in case of a race condition
3387 # with a foreign registration process
3388 if (@$select_res > 0)
3389 {
3390 # Release block statement 'new_server' to make the server accessible
3391 # for foreign registration processes
3392 my $update_statement = "UPDATE $known_server_tn SET status='waiting' WHERE status='new_server'";
3393 my $update_res = $known_server_db->exec_statement($update_statement);
3395 # Set a random delay to avoid the registration race condition
3396 my $new_foreign_servers_register_delay = int(rand(4))+1;
3397 $kernel->delay_set("register_at_foreign_servers", $new_foreign_servers_register_delay);
3398 }
3399 # If all registration processes succeed
3400 else
3401 {
3402 $kernel->delay_set("register_at_foreign_servers", $foreign_servers_register_delay);
3403 }
3405 return;
3406 }
3409 #==== MAIN = main ==============================================================
3410 # parse commandline options
3411 Getopt::Long::Configure( "bundling" );
3412 GetOptions("h|help" => \&usage,
3413 "c|config=s" => \$cfg_file,
3414 "f|foreground" => \$foreground,
3415 "v|verbose+" => \$verbose,
3416 "no-arp+" => \$no_arp,
3417 "d=s" => \$debug_parts,
3418 ) or &usage("", 1);
3420 # read and set config parameters
3421 &check_cmdline_param ;
3422 &read_configfile($cfg_file, %cfg_defaults);
3423 &check_pid;
3425 $SIG{CHLD} = 'IGNORE';
3427 # forward error messages to logfile
3428 if( ! $foreground ) {
3429 open( STDIN, '+>/dev/null' );
3430 open( STDOUT, '+>&STDIN' );
3431 open( STDERR, '+>&STDIN' );
3432 }
3434 # Just fork, if we are not in foreground mode
3435 if( ! $foreground ) {
3436 chdir '/' or die "Can't chdir to /: $!";
3437 $pid = fork;
3438 setsid or die "Can't start a new session: $!";
3439 umask 0;
3440 } else {
3441 $pid = $$;
3442 }
3444 # Do something useful - put our PID into the pid_file
3445 if( 0 != $pid ) {
3446 open( LOCK_FILE, ">$pid_file" );
3447 print LOCK_FILE "$pid\n";
3448 close( LOCK_FILE );
3449 if( !$foreground ) {
3450 exit( 0 )
3451 };
3452 }
3454 # parse head url and revision from svn
3455 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
3456 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
3457 $server_headURL = defined $1 ? $1 : 'unknown' ;
3458 $server_revision = defined $2 ? $2 : 'unknown' ;
3459 if ($server_headURL =~ /\/tag\// ||
3460 $server_headURL =~ /\/branches\// ) {
3461 $server_status = "stable";
3462 } else {
3463 $server_status = "developmental" ;
3464 }
3465 # Prepare log file and set permissions
3466 $root_uid = getpwnam('root');
3467 $adm_gid = getgrnam('adm');
3468 open(FH, ">>$log_file");
3469 close FH;
3470 chmod(0440, $log_file);
3471 chown($root_uid, $adm_gid, $log_file);
3472 chown($root_uid, $adm_gid, "/var/lib/gosa-si");
3474 daemon_log(" ", 1);
3475 daemon_log("$0 started!", 1);
3476 daemon_log("status: $server_status", 1);
3477 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1);
3479 # Buildup data bases
3480 {
3481 no strict "refs";
3483 if ($db_module eq "DBmysql") {
3484 # connect to incoming_db
3485 $incoming_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3487 # connect to gosa-si job queue
3488 $job_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3490 # connect to known_clients_db
3491 $known_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3493 # connect to foreign_clients_db
3494 $foreign_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3496 # connect to known_server_db
3497 $known_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3499 # connect to login_usr_db
3500 $login_users_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3502 # connect to fai_server_db
3503 $fai_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3505 # connect to fai_release_db
3506 $fai_release_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3508 # connect to packages_list_db
3509 $packages_list_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3511 # connect to messaging_db
3512 $messaging_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3514 } elsif ($db_module eq "DBsqlite") {
3515 # connect to incoming_db
3516 unlink($incoming_file_name);
3517 $incoming_db = GOSA::DBsqlite->new($incoming_file_name);
3518 chmod(0640, $incoming_file_name);
3519 chown($root_uid, $adm_gid, $incoming_file_name);
3521 # connect to gosa-si job queue
3522 $job_db = GOSA::DBsqlite->new($job_queue_file_name);
3523 chmod(0640, $job_queue_file_name);
3524 chown($root_uid, $adm_gid, $job_queue_file_name);
3526 # connect to known_clients_db
3527 #unlink($known_clients_file_name);
3528 $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
3529 chmod(0640, $known_clients_file_name);
3530 chown($root_uid, $adm_gid, $known_clients_file_name);
3532 # connect to foreign_clients_db
3533 #unlink($foreign_clients_file_name);
3534 $foreign_clients_db = GOSA::DBsqlite->new($foreign_clients_file_name);
3535 chmod(0640, $foreign_clients_file_name);
3536 chown($root_uid, $adm_gid, $foreign_clients_file_name);
3538 # connect to known_server_db
3539 unlink($known_server_file_name); # do not delete, gosa-si-server should be forced to check config file and dns at each start
3540 $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
3541 chmod(0640, $known_server_file_name);
3542 chown($root_uid, $adm_gid, $known_server_file_name);
3544 # connect to login_usr_db
3545 #unlink($login_users_file_name);
3546 $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
3547 chmod(0640, $login_users_file_name);
3548 chown($root_uid, $adm_gid, $login_users_file_name);
3550 # connect to fai_server_db
3551 unlink($fai_server_file_name);
3552 $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
3553 chmod(0640, $fai_server_file_name);
3554 chown($root_uid, $adm_gid, $fai_server_file_name);
3556 # connect to fai_release_db
3557 unlink($fai_release_file_name);
3558 $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
3559 chmod(0640, $fai_release_file_name);
3560 chown($root_uid, $adm_gid, $fai_release_file_name);
3562 # connect to packages_list_db
3563 unlink($packages_list_under_construction);
3564 $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
3565 chmod(0640, $packages_list_file_name);
3566 chown($root_uid, $adm_gid, $packages_list_file_name);
3568 # connect to messaging_db
3569 #unlink($messaging_file_name);
3570 $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
3571 chmod(0640, $messaging_file_name);
3572 chown($root_uid, $adm_gid, $messaging_file_name);
3573 }
3574 }
3576 # Creating tables
3577 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
3578 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
3579 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
3580 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
3581 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
3582 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
3583 $foreign_clients_db->create_table($foreign_clients_tn, \@foreign_clients_col_names);
3584 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
3585 $incoming_db->create_table($incoming_tn, \@incoming_col_names);
3586 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
3588 # create xml object used for en/decrypting
3589 $xml = new XML::Simple();
3591 # Import all modules
3592 &import_modules;
3594 # Check wether all modules are gosa-si valid passwd check
3595 &password_check;
3597 # Check DNS and config file for server registration
3598 if ($serverPackages_enabled eq "true") { &prepare_server_registration; }
3600 # Create functions hash
3601 while (my ($module, @mod_info) = each %$known_modules)
3602 {
3603 while (my ($plugin, $functions) = each %{$mod_info[0][2]})
3604 {
3605 while (my ($function, $nothing) = each %$functions )
3606 {
3607 $known_functions->{$function} = $nothing;
3608 }
3609 }
3610 }
3612 # Prepare for using Opsi
3613 if ($opsi_enabled eq "true") {
3614 use JSON::RPC::Client;
3615 use XML::Quote qw(:all);
3616 $opsi_url= "https://".$opsi_admin.":".$opsi_password."@".$opsi_server.":4447/rpc";
3617 $opsi_client = new JSON::RPC::Client;
3618 }
3621 POE::Component::Server::TCP->new(
3622 Alias => "TCP_SERVER",
3623 Port => $server_port,
3624 ClientInput => sub {
3625 my ($kernel, $input, $heap, $session) = @_[KERNEL, ARG0, HEAP, SESSION];
3626 my $session_id = $session->ID;
3627 if ($input =~ /;([\d\.]+):([\d]+)$/)
3628 {
3629 # Messages from other servers should be blocked if config option is set
3630 if (($2 eq $server_port) && ($serverPackages_enabled eq "false"))
3631 {
3632 return;
3633 }
3634 &daemon_log("$session_id DEBUG: incoming message from '$1:$2'", 11);
3635 }
3636 else
3637 {
3638 my $remote_ip = $heap->{'remote_ip'};
3639 &daemon_log("$session_id DEBUG: incoming message from '$remote_ip'", 11);
3640 }
3641 push(@msgs_to_decrypt, $input);
3642 $kernel->yield("msg_to_decrypt");
3643 },
3644 InlineStates => {
3645 msg_to_decrypt => \&msg_to_decrypt,
3646 next_task => \&next_task,
3647 task_result => \&handle_task_result,
3648 task_done => \&handle_task_done,
3649 task_debug => \&handle_task_debug,
3650 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!" },
3651 }
3652 );
3654 daemon_log("0 INFO: start socket for incoming xml messages at port '$server_port' ", 1);
3656 # create session for repeatedly checking the job queue for jobs
3657 POE::Session->create(
3658 inline_states => {
3659 _start => \&session_start,
3660 register_at_foreign_servers => \®ister_at_foreign_servers,
3661 control_server_registration => \&control_server_registration,
3662 sig_handler => \&sig_handler,
3663 next_task => \&next_task,
3664 task_result => \&handle_task_result,
3665 task_done => \&handle_task_done,
3666 task_debug => \&handle_task_debug,
3667 watch_for_next_tasks => \&watch_for_next_tasks,
3668 watch_for_new_messages => \&watch_for_new_messages,
3669 watch_for_delivery_messages => \&watch_for_delivery_messages,
3670 watch_for_done_messages => \&watch_for_done_messages,
3671 watch_for_new_jobs => \&watch_for_new_jobs,
3672 watch_for_modified_jobs => \&watch_for_modified_jobs,
3673 watch_for_done_jobs => \&watch_for_done_jobs,
3674 watch_for_opsi_jobs => \&watch_for_opsi_jobs,
3675 watch_for_old_known_clients => \&watch_for_old_known_clients,
3676 create_packages_list_db => \&run_create_packages_list_db,
3677 create_fai_server_db => \&run_create_fai_server_db,
3678 create_fai_release_db => \&run_create_fai_release_db,
3679 recreate_packages_db => \&run_recreate_packages_db,
3680 session_run_result => \&session_run_result,
3681 session_run_debug => \&session_run_debug,
3682 session_run_done => \&session_run_done,
3683 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!" },
3684 }
3685 );
3688 POE::Kernel->run();
3689 exit;