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 "periodic VARCHAR(6) DEFAULT 'none'",
155 );
157 # holds all other gosa-si-server
158 our $known_server_db;
159 our $known_server_tn = "known_server";
160 my $known_server_file_name;
161 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)");
163 # holds all registrated clients
164 our $known_clients_db;
165 our $known_clients_tn = "known_clients";
166 my $known_clients_file_name;
167 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)");
169 # holds all registered clients at a foreign server
170 our $foreign_clients_db;
171 our $foreign_clients_tn = "foreign_clients";
172 my $foreign_clients_file_name;
173 my @foreign_clients_col_names = ("hostname VARCHAR(255)", "macaddress VARCHAR(17)", "regserver VARCHAR(255)", "timestamp VARCHAR(14)");
175 # holds all logged in user at each client
176 our $login_users_db;
177 our $login_users_tn = "login_users";
178 my $login_users_file_name;
179 my @login_users_col_names = ("client VARCHAR(255)", "user VARCHAR(255)", "timestamp VARCHAR(14)", "regserver VARCHAR(255) DEFAULT 'localhost'");
181 # holds all fai server, the debian release and tag
182 our $fai_server_db;
183 our $fai_server_tn = "fai_server";
184 my $fai_server_file_name;
185 our @fai_server_col_names = ("timestamp VARCHAR(14)", "server VARCHAR(255)", "fai_release VARCHAR(255)", "sections VARCHAR(255)", "tag VARCHAR(255)");
187 our $fai_release_db;
188 our $fai_release_tn = "fai_release";
189 my $fai_release_file_name;
190 our @fai_release_col_names = ("timestamp VARCHAR(14)", "fai_release VARCHAR(255)", "class VARCHAR(255)", "type VARCHAR(255)", "state VARCHAR(255)");
192 # holds all packages available from different repositories
193 our $packages_list_db;
194 our $packages_list_tn = "packages_list";
195 my $packages_list_file_name;
196 our @packages_list_col_names = ("distribution VARCHAR(255)", "package VARCHAR(255)", "version VARCHAR(255)", "section VARCHAR(255)", "description TEXT", "template LONGBLOB", "timestamp VARCHAR(14)");
197 my $outdir = "/tmp/packages_list_db";
198 my $arch = "i386";
200 # holds all messages which should be delivered to a user
201 our $messaging_db;
202 our $messaging_tn = "messaging";
203 our @messaging_col_names = ("id INTEGER", "subject TEXT", "message_from VARCHAR(255)", "message_to VARCHAR(255)",
204 "flag VARCHAR(255)", "direction VARCHAR(255)", "delivery_time VARCHAR(255)", "message TEXT", "timestamp VARCHAR(14)" );
205 my $messaging_file_name;
207 # path to directory to store client install log files
208 our $client_fai_log_dir = "/var/log/fai";
210 # queue which stores taskes until one of the $max_children children are ready to process the task
211 #my @tasks = qw();
212 my @msgs_to_decrypt = qw();
213 my $max_children = 2;
216 # loop delay for job queue to look for opsi jobs
217 my $job_queue_opsi_delay = 10;
218 our $opsi_client;
219 our $opsi_url;
221 # Lifetime of logged in user information. If no update information comes after n seconds,
222 # the user is expeceted to be no longer logged in or the host is no longer running. Because
223 # of this, the user is deleted from login_users_db
224 our $logged_in_user_date_of_expiry = 600;
226 # List of month names, used in function daemon_log
227 my @monthnames = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
229 # List of accepted periodical xml tags related to cpan modul DateTime
230 our $check_periodic = {"months"=>'', "weeks"=>'', "days"=>'', "hours"=>'', "minutes"=>''};
233 %cfg_defaults = (
234 "general" => {
235 "log-file" => [\$log_file, "/var/run/".$prg.".log"],
236 "pid-file" => [\$pid_file, "/var/run/".$prg.".pid"],
237 },
238 "server" => {
239 "ip" => [\$server_ip, "0.0.0.0"],
240 "port" => [\$server_port, "20081"],
241 "known-clients" => [\$known_clients_file_name, '/var/lib/gosa-si/clients.db' ],
242 "known-servers" => [\$known_server_file_name, '/var/lib/gosa-si/servers.db'],
243 "incoming" => [\$incoming_file_name, '/var/lib/gosa-si/incoming.db'],
244 "login-users" => [\$login_users_file_name, '/var/lib/gosa-si/users.db'],
245 "fai-server" => [\$fai_server_file_name, '/var/lib/gosa-si/fai_server.db'],
246 "fai-release" => [\$fai_release_file_name, '/var/lib/gosa-si/fai_release.db'],
247 "packages-list" => [\$packages_list_file_name, '/var/lib/gosa-si/packages.db'],
248 "messaging" => [\$messaging_file_name, '/var/lib/gosa-si/messaging.db'],
249 "foreign-clients" => [\$foreign_clients_file_name, '/var/lib/gosa-si/foreign_clients.db'],
250 "source-list" => [\$sources_list, '/etc/apt/sources.list'],
251 "repo-path" => [\$repo_path, '/srv/www/repository'],
252 "ldap-uri" => [\$ldap_uri, ""],
253 "ldap-base" => [\$ldap_base, ""],
254 "ldap-admin-dn" => [\$ldap_admin_dn, ""],
255 "ldap-admin-password" => [\$ldap_admin_password, ""],
256 "ldap-version" => [\$ldap_version, 3],
257 "gosa-unit-tag" => [\$gosa_unit_tag, ""],
258 "max-clients" => [\$max_clients, 10],
259 "wol-password" => [\$wake_on_lan_passwd, ""],
260 "mysql-username" => [\$mysql_username, "gosa_si"],
261 "mysql-password" => [\$mysql_password, ""],
262 "mysql-database" => [\$mysql_database, "gosa_si"],
263 "mysql-host" => [\$mysql_host, "127.0.0.1"],
264 },
265 "GOsaPackages" => {
266 "job-queue" => [\$job_queue_file_name, '/var/lib/gosa-si/jobs.db'],
267 "job-queue-loop-delay" => [\$job_queue_loop_delay, 3],
268 "messaging-db-loop-delay" => [\$messaging_db_loop_delay, 3],
269 "key" => [\$GosaPackages_key, "none"],
270 "new-systems-ou" => [\$new_systems_ou, 'ou=workstations,ou=systems'],
271 },
272 "ClientPackages" => {
273 "key" => [\$ClientPackages_key, "none"],
274 "user-date-of-expiry" => [\$logged_in_user_date_of_expiry, 600],
275 },
276 "ServerPackages"=> {
277 "enabled" => [\$serverPackages_enabled, "true"],
278 "address" => [\$foreign_server_string, ""],
279 "dns-lookup" => [\$dns_lookup, "true"],
280 "domain" => [\$server_domain, ""],
281 "key" => [\$ServerPackages_key, "none"],
282 "key-lifetime" => [\$foreign_servers_register_delay, 120],
283 "job-synchronization-enabled" => [\$job_synchronization, "true"],
284 "synchronization-loop" => [\$modified_jobs_loop_delay, 5],
285 },
286 "ArpHandler" => {
287 "enabled" => [\$arp_enabled, "true"],
288 "interface" => [\$arp_interface, "all"],
289 },
290 "Opsi" => {
291 "enabled" => [\$opsi_enabled, "false"],
292 "server" => [\$opsi_server, "localhost"],
293 "admin" => [\$opsi_admin, "opsi-admin"],
294 "password" => [\$opsi_password, "secret"],
295 },
297 );
300 #=== FUNCTION ================================================================
301 # NAME: usage
302 # PARAMETERS: nothing
303 # RETURNS: nothing
304 # DESCRIPTION: print out usage text to STDERR
305 #===============================================================================
306 sub usage {
307 print STDERR << "EOF" ;
308 usage: $prg [-hvf] [-c config] [-d number]
310 -h : this (help) message
311 -c <file> : config file
312 -f : foreground, process will not be forked to background
313 -v : be verbose (multiple to increase verbosity)
314 'v': error logs
315 'vvv': warning plus error logs
316 'vvvvv': info plus warning logs
317 'vvvvvvv': debug plus info logs
318 -no-arp : starts $prg without connection to arp module
319 -d <int> : if verbose level is higher than 7x 'v' specified parts can be debugged
320 1 : receiving messages
321 2 : sending messages
322 4 : encrypting/decrypting messages
323 8 : verification if a message complies gosa-si requirements
324 16 : message processing
325 32 : ldap connectivity
326 64 : database status and connectivity
327 128 : main process
328 256 : creation of packages_list_db
329 512 : ARP debug information
330 EOF
331 exit(0);
332 }
335 #=== FUNCTION ================================================================
336 # NAME: logging
337 # PARAMETERS: level - string - default 'info'
338 # msg - string -
339 # facility - string - default 'LOG_DAEMON'
340 # RETURNS: nothing
341 # DESCRIPTION: function for logging
342 #===============================================================================
343 sub daemon_log {
344 my( $msg, $level ) = @_;
345 if (not defined $msg) { return }
346 if (not defined $level) { $level = 1 }
347 my $to_be_logged = 0;
349 # Write log line if line level is lower than verbosity given in commandline
350 if ($level <= $verbose)
351 {
352 $to_be_logged = 1 ;
353 }
355 # Write if debug flag is set and bitstring matches
356 if ($debug_parts > 0)
357 {
358 my $tmp_level = ($level - 10 >= 0) ? $level - 10 : 0 ;
359 my $tmp_level_bitstring = unpack("B32", pack("N", $tmp_level));
360 if (int($debug_parts_bitstring & $tmp_level_bitstring))
361 {
362 $to_be_logged = 1;
363 }
364 }
366 if ($to_be_logged)
367 {
368 if(defined $log_file){
369 my $open_log_fh = sysopen(LOG_HANDLE, $log_file, O_WRONLY | O_CREAT | O_APPEND , 0440);
370 if(not $open_log_fh) {
371 print STDERR "cannot open $log_file: $!";
372 return;
373 }
374 # Check owner and group of log_file and update settings if necessary
375 my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = stat($log_file);
376 if((not $uid eq $root_uid) || (not $gid eq $adm_gid)) {
377 chown($root_uid, $adm_gid, $log_file);
378 }
380 # Prepare time string for log message
381 my ($seconds,$minutes,$hours,$monthday,$month,$year,$weekday,$yearday,$sommertime) = localtime(time);
382 $hours = $hours < 10 ? $hours = "0".$hours : $hours;
383 $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
384 $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
385 $month = $monthnames[$month];
386 $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
387 $year+=1900;
390 # Build log message and write it to log file and commandline
391 chomp($msg);
392 my $log_msg = "$month $monthday $hours:$minutes:$seconds $prg $msg\n";
393 flock(LOG_HANDLE, LOCK_EX);
394 seek(LOG_HANDLE, 0, 2);
395 print LOG_HANDLE $log_msg;
396 flock(LOG_HANDLE, LOCK_UN);
397 if( $foreground )
398 {
399 print STDERR $log_msg;
400 }
401 close( LOG_HANDLE );
402 }
403 }
404 }
407 #=== FUNCTION ================================================================
408 # NAME: check_cmdline_param
409 # PARAMETERS: nothing
410 # RETURNS: nothing
411 # DESCRIPTION: validates commandline parameter
412 #===============================================================================
413 sub check_cmdline_param () {
414 my $err_counter = 0;
416 # Check configuration file
417 if(not defined($cfg_file)) {
418 $cfg_file = "/etc/gosa-si/server.conf";
419 if(! -r $cfg_file) {
420 print STDERR "Please specify a config file.\n";
421 $err_counter++;
422 }
423 }
425 # Prepare identification which gosa-si parts should be debugged and which not
426 if (defined $debug_parts)
427 {
428 if ($debug_parts =~ /^\d+$/)
429 {
430 $debug_parts_bitstring = unpack("B32", pack("N", $debug_parts));
431 }
432 else
433 {
434 print STDERR "Value '$debug_parts' invalid for option d (number expected)\n";
435 $err_counter++;
436 }
437 }
439 # Exit if an error occour
440 if( $err_counter > 0 ) { &usage( "", 1 ); }
441 }
444 #=== FUNCTION ================================================================
445 # NAME: check_pid
446 # PARAMETERS: nothing
447 # RETURNS: nothing
448 # DESCRIPTION: handels pid processing
449 #===============================================================================
450 sub check_pid {
451 $pid = -1;
452 # Check, if we are already running
453 if( open(LOCK_FILE, "<$pid_file") ) {
454 $pid = <LOCK_FILE>;
455 if( defined $pid ) {
456 chomp( $pid );
457 if( -f "/proc/$pid/stat" ) {
458 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
459 if( $stat ) {
460 print STDERR "\nERROR: Already running!\n";
461 close( LOCK_FILE );
462 exit -1;
463 }
464 }
465 }
466 close( LOCK_FILE );
467 unlink( $pid_file );
468 }
470 # create a syslog msg if it is not to possible to open PID file
471 if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
472 my($msg) = "Couldn't obtain lockfile '$pid_file' ";
473 if (open(LOCK_FILE, '<', $pid_file)
474 && ($pid = <LOCK_FILE>))
475 {
476 chomp($pid);
477 $msg .= "(PID $pid)\n";
478 } else {
479 $msg .= "(unable to read PID)\n";
480 }
481 if( ! ($foreground) ) {
482 openlog( $0, "cons,pid", "daemon" );
483 syslog( "warning", $msg );
484 closelog();
485 }
486 else {
487 print( STDERR " $msg " );
488 }
489 exit( -1 );
490 }
491 }
493 #=== FUNCTION ================================================================
494 # NAME: import_modules
495 # PARAMETERS: module_path - string - abs. path to the directory the modules
496 # are stored
497 # RETURNS: nothing
498 # DESCRIPTION: each file in module_path which ends with '.pm' and activation
499 # state is on is imported by "require 'file';"
500 #===============================================================================
501 sub import_modules {
502 if (not -e $modules_path) {
503 daemon_log("0 ERROR: cannot find directory or directory is not readable: $modules_path", 1);
504 }
506 opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
508 while (defined (my $file = readdir (DIR))) {
509 if (not $file =~ /(\S*?).pm$/) {
510 next;
511 }
512 my $mod_name = $1;
514 # ArpHandler switch
515 if( $file =~ /ArpHandler.pm/ ) {
516 if( $arp_enabled eq "false" ) { next; }
517 }
519 # ServerPackages switch
520 if ($file eq "ServerPackages.pm" && $serverPackages_enabled eq "false")
521 {
522 $dns_lookup = "false";
523 next;
524 }
526 eval { require $file; };
527 if ($@) {
528 daemon_log("0 ERROR: gosa-si-server could not load module $file", 1);
529 daemon_log("$@", 1);
530 exit;
531 } else {
532 my $info = eval($mod_name.'::get_module_info()');
533 # Only load module if get_module_info() returns a non-null object
534 if( $info ) {
535 my ($input_address, $input_key, $event_hash) = @{$info};
536 $known_modules->{$mod_name} = $info;
537 daemon_log("0 INFO: module $mod_name loaded", 5);
538 }
539 }
540 }
541 close (DIR);
542 }
544 #=== FUNCTION ================================================================
545 # NAME: password_check
546 # PARAMETERS: nothing
547 # RETURNS: nothing
548 # DESCRIPTION: escalates an critical error if two modules exist which are avaialable by
549 # the same password
550 #===============================================================================
551 sub password_check {
552 my $passwd_hash = {};
553 while (my ($mod_name, $mod_info) = each %$known_modules) {
554 my $mod_passwd = @$mod_info[1];
555 if (not defined $mod_passwd) { next; }
556 if (not exists $passwd_hash->{$mod_passwd}) {
557 $passwd_hash->{$mod_passwd} = $mod_name;
559 # escalates critical error
560 } else {
561 &daemon_log("0 ERROR: two loaded modules do have the same password. Please modify the 'key'-parameter in config file");
562 &daemon_log("0 ERROR: module='$mod_name' and module='".$passwd_hash->{$mod_passwd}."'");
563 exit( -1 );
564 }
565 }
567 }
570 #=== FUNCTION ================================================================
571 # NAME: sig_int_handler
572 # PARAMETERS: signal - string - signal arose from system
573 # RETURNS: nothing
574 # DESCRIPTION: handels tasks to be done befor signal becomes active
575 #===============================================================================
576 sub sig_int_handler {
577 my ($signal) = @_;
579 # if (defined($ldap_handle)) {
580 # $ldap_handle->disconnect;
581 # }
582 # TODO alle verbliebenden ldap verbindungen aus allen heaps beenden
585 daemon_log("shutting down gosa-si-server", 1);
586 system("kill `ps -C gosa-si-server -o pid=`");
587 }
588 $SIG{INT} = \&sig_int_handler;
591 sub check_key_and_xml_validity {
592 my ($crypted_msg, $module_key, $session_id) = @_;
593 my $msg;
594 my $msg_hash;
595 my $error_string;
596 eval{
597 $msg = &decrypt_msg($crypted_msg, $module_key);
599 if ($msg =~ /<xml>/i){
600 $msg =~ s/\s+/ /g; # just for better daemon_log
601 daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 18);
602 $msg_hash = $xml->XMLin($msg, ForceArray=>1);
604 ##############
605 # check header
606 if( not exists $msg_hash->{'header'} ) { die "no header specified"; }
607 my $header_l = $msg_hash->{'header'};
608 if( (1 > @{$header_l}) || ( ( 'HASH' eq ref @{$header_l}[0]) && (1 > keys %{@{$header_l}[0]}) ) ) { die 'empty header tag'; }
609 if( 1 < @{$header_l} ) { die 'more than one header specified'; }
610 my $header = @{$header_l}[0];
611 if( 0 == length $header) { die 'empty string in header tag'; }
613 ##############
614 # check source
615 if( not exists $msg_hash->{'source'} ) { die "no source specified"; }
616 my $source_l = $msg_hash->{'source'};
617 if( (1 > @{$source_l}) || ( ( 'HASH' eq ref @{$source_l}[0]) && (1 > keys %{@{$source_l}[0]}) ) ) { die 'empty source tag'; }
618 if( 1 < @{$source_l} ) { die 'more than one source specified'; }
619 my $source = @{$source_l}[0];
620 if( 0 == length $source) { die 'source error'; }
622 ##############
623 # check target
624 if( not exists $msg_hash->{'target'} ) { die "no target specified"; }
625 my $target_l = $msg_hash->{'target'};
626 if( (1 > @{$target_l}) || ( ('HASH' eq ref @{$target_l}[0]) && (1 > keys %{@{$target_l}[0]}) ) ) { die 'empty target tag'; }
627 }
628 };
629 if($@) {
630 daemon_log("$session_id ERROR: do not understand the message: $@", 1);
631 $msg = undef;
632 $msg_hash = undef;
633 }
635 return ($msg, $msg_hash);
636 }
639 sub check_outgoing_xml_validity {
640 my ($msg, $session_id) = @_;
642 my $msg_hash;
643 eval{
644 $msg_hash = $xml->XMLin($msg, ForceArray=>1);
646 ##############
647 # check header
648 my $header_l = $msg_hash->{'header'};
649 if( 1 != @{$header_l} ) {
650 die 'no or more than one headers specified';
651 }
652 my $header = @{$header_l}[0];
653 if( 0 == length $header) {
654 die 'header has length 0';
655 }
657 ##############
658 # check source
659 my $source_l = $msg_hash->{'source'};
660 if( 1 != @{$source_l} ) {
661 die 'no or more than 1 sources specified';
662 }
663 my $source = @{$source_l}[0];
664 if( 0 == length $source) {
665 die 'source has length 0';
666 }
668 # Check if source contains hostname instead of ip address
669 if($source =~ /^[a-z][\w\-\.]+:\d+$/i) {
670 my ($hostname,$port) = split(/:/, $source);
671 my $ip_address = inet_ntoa(scalar gethostbyname($hostname));
672 if(defined($ip_address) && $ip_address =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/ && $port =~ /^\d+$/) {
673 # Write ip address to $source variable
674 $source = "$ip_address:$port";
675 $msg_hash->{source}[0] = $source ;
676 $msg =~ s/<source>.*<\/source>/<source>$source<\/source>/;
677 }
678 }
679 unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
680 $source =~ /^GOSA$/i) {
681 die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
682 }
684 ##############
685 # check target
686 my $target_l = $msg_hash->{'target'};
687 if( 0 == @{$target_l} ) {
688 die "no targets specified";
689 }
690 foreach my $target (@$target_l) {
691 if( 0 == length $target) {
692 die "target has length 0";
693 }
694 unless( $target =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
695 $target =~ /^GOSA$/i ||
696 $target =~ /^\*$/ ||
697 $target =~ /KNOWN_SERVER/i ||
698 $target =~ /JOBDB/i ||
699 $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 ){
700 die "target '$target' is not a complete ip-address with port or a valid target name or a mac-address";
701 }
702 }
703 };
704 if($@) {
705 daemon_log("$session_id ERROR: outgoing msg is not gosa-si envelope conform: $@", 1);
706 daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 1);
707 $msg_hash = undef;
708 }
710 return ($msg, $msg_hash);
711 }
714 sub input_from_known_server {
715 my ($input, $remote_ip, $session_id) = @_ ;
716 my ($msg, $msg_hash, $module);
718 my $sql_statement= "SELECT * FROM known_server";
719 my $query_res = $known_server_db->select_dbentry( $sql_statement );
721 while( my ($hit_num, $hit) = each %{ $query_res } ) {
722 my $host_name = $hit->{hostname};
723 if( not $host_name =~ "^$remote_ip") {
724 next;
725 }
726 my $host_key = $hit->{hostkey};
727 daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 14);
728 daemon_log("$session_id DEBUG: input_from_known_server: host_key: $host_key", 14);
730 # check if module can open msg envelope with module key
731 my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
732 if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
733 daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 14);
734 daemon_log("$@", 14);
735 next;
736 }
737 else {
738 $msg = $tmp_msg;
739 $msg_hash = $tmp_msg_hash;
740 $module = "ServerPackages";
741 daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 14);
742 last;
743 }
744 }
746 if( (!$msg) || (!$msg_hash) || (!$module) ) {
747 daemon_log("$session_id DEBUG: Incoming message is not from a known server", 14);
748 }
750 return ($msg, $msg_hash, $module);
751 }
754 sub input_from_known_client {
755 my ($input, $remote_ip, $session_id) = @_ ;
756 my ($msg, $msg_hash, $module);
758 my $sql_statement= "SELECT * FROM known_clients";
759 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
760 while( my ($hit_num, $hit) = each %{ $query_res } ) {
761 my $host_name = $hit->{hostname};
762 if( not $host_name =~ /^$remote_ip/) {
763 next;
764 }
765 my $host_key = $hit->{hostkey};
766 &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 14);
767 &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 14);
769 # check if module can open msg envelope with module key
770 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
772 if( (!$msg) || (!$msg_hash) ) {
773 &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 14);
774 next;
775 }
776 else {
777 $module = "ClientPackages";
778 daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 14);
779 last;
780 }
781 }
783 if( (!$msg) || (!$msg_hash) || (!$module) ) {
784 &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 14);
785 }
787 return ($msg, $msg_hash, $module);
788 }
791 sub input_from_unknown_host {
792 no strict "refs";
793 my ($input, $session_id) = @_ ;
794 my ($msg, $msg_hash, $module);
795 my $error_string;
797 my %act_modules = %$known_modules;
799 while( my ($mod, $info) = each(%act_modules)) {
801 # check a key exists for this module
802 my $module_key = ${$mod."_key"};
803 if( not defined $module_key ) {
804 if( $mod eq 'ArpHandler' ) {
805 next;
806 }
807 daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
808 next;
809 }
810 daemon_log("$session_id DEBUG: $mod: $module_key", 14);
812 # check if module can open msg envelope with module key
813 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
814 if( (not defined $msg) || (not defined $msg_hash) ) {
815 next;
816 } else {
817 $module = $mod;
818 daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 18);
819 last;
820 }
821 }
823 if( (!$msg) || (!$msg_hash) || (!$module)) {
824 daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 14);
825 }
827 return ($msg, $msg_hash, $module);
828 }
831 sub create_ciphering {
832 my ($passwd) = @_;
833 if((!defined($passwd)) || length($passwd)==0) {
834 $passwd = "";
835 }
836 $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
837 my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
838 my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
839 $my_cipher->set_iv($iv);
840 return $my_cipher;
841 }
844 sub encrypt_msg {
845 my ($msg, $key) = @_;
846 my $my_cipher = &create_ciphering($key);
847 my $len;
848 {
849 use bytes;
850 $len= 16-length($msg)%16;
851 }
852 $msg = "\0"x($len).$msg;
853 $msg = $my_cipher->encrypt($msg);
854 chomp($msg = &encode_base64($msg));
855 # there are no newlines allowed inside msg
856 $msg=~ s/\n//g;
857 return $msg;
858 }
861 sub decrypt_msg {
863 my ($msg, $key) = @_ ;
864 $msg = &decode_base64($msg);
865 my $my_cipher = &create_ciphering($key);
866 $msg = $my_cipher->decrypt($msg);
867 $msg =~ s/\0*//g;
868 return $msg;
869 }
872 sub get_encrypt_key {
873 my ($target) = @_ ;
874 my $encrypt_key;
875 my $error = 0;
877 # target can be in known_server
878 if( not defined $encrypt_key ) {
879 my $sql_statement= "SELECT * FROM known_server WHERE hostname='$target'";
880 my $query_res = $known_server_db->select_dbentry( $sql_statement );
881 while( my ($hit_num, $hit) = each %{ $query_res } ) {
882 my $host_name = $hit->{hostname};
883 if( $host_name ne $target ) {
884 next;
885 }
886 $encrypt_key = $hit->{hostkey};
887 last;
888 }
889 }
891 # target can be in known_client
892 if( not defined $encrypt_key ) {
893 my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$target'";
894 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
895 while( my ($hit_num, $hit) = each %{ $query_res } ) {
896 my $host_name = $hit->{hostname};
897 if( $host_name ne $target ) {
898 next;
899 }
900 $encrypt_key = $hit->{hostkey};
901 last;
902 }
903 }
905 return $encrypt_key;
906 }
909 #=== FUNCTION ================================================================
910 # NAME: open_socket
911 # PARAMETERS: PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
912 # [PeerPort] string necessary if port not appended by PeerAddr
913 # RETURNS: socket IO::Socket::INET
914 # DESCRIPTION: open a socket to PeerAddr
915 #===============================================================================
916 sub open_socket {
917 my ($PeerAddr, $PeerPort) = @_ ;
918 if(defined($PeerPort)){
919 $PeerAddr = $PeerAddr.":".$PeerPort;
920 }
921 my $socket;
922 $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
923 Porto => "tcp",
924 Type => SOCK_STREAM,
925 Timeout => 5,
926 );
927 if(not defined $socket) {
928 return;
929 }
930 # &daemon_log("DEBUG: open_socket: $PeerAddr", 7);
931 return $socket;
932 }
935 sub send_msg_to_target {
936 my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
937 my $error = 0;
938 my $header;
939 my $timestamp = &get_time();
940 my $new_status;
941 my $act_status;
942 my ($sql_statement, $res);
944 if( $msg_header ) {
945 $header = "'$msg_header'-";
946 } else {
947 $header = "";
948 }
950 # Memorize own source address
951 my $own_source_address = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
952 $own_source_address .= ":".$server_port;
954 # Patch 0.0.0.0 source to real address
955 $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$own_source_address<\/source>/s;
956 # Patch GOSA source to real address and add forward_to_gosa tag
957 $msg =~ s/<source>GOSA<\/source>/<source>$own_source_address<\/source> <forward_to_gosa>$own_source_address,$session_id<\/forward_to_gosa>/ ;
959 # encrypt xml msg
960 my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
962 # opensocket
963 my $socket = &open_socket($address);
964 if( !$socket ) {
965 daemon_log("$session_id ERROR: Cannot open socket to host '$address'. Message processing aborted!", 1);
966 $error++;
967 }
969 if( $error == 0 ) {
970 # send xml msg
971 print $socket $crypted_msg.";$own_source_address\n";
972 daemon_log("$session_id INFO: send ".$header."msg to $address", 5);
973 daemon_log("$session_id DEBUG: message:\n$msg", 12);
975 }
977 # close socket in any case
978 if( $socket ) {
979 close $socket;
980 }
982 if( $error > 0 ) { $new_status = "down"; }
983 else { $new_status = $msg_header; }
986 # known_clients
987 $sql_statement = "SELECT * FROM $known_clients_tn WHERE hostname='$address'";
988 $res = $known_clients_db->select_dbentry($sql_statement);
989 if( keys(%$res) == 1) {
990 $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
991 if ($act_status eq "down" && $new_status eq "down") {
992 $sql_statement = "DELETE FROM known_clients WHERE hostname='$address'";
993 $res = $known_clients_db->del_dbentry($sql_statement);
994 daemon_log("$session_id WARNING: failed 2x to send msg to host '$address', delete host from known_clients", 3);
995 } else {
996 $sql_statement = "UPDATE known_clients SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
997 $res = $known_clients_db->update_dbentry($sql_statement);
998 if($new_status eq "down"){
999 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
1000 } else {
1001 daemon_log("$session_id DEBUG: set '$address' from status '$act_status' to '$new_status'", 138);
1002 }
1003 }
1004 }
1006 # known_server
1007 $sql_statement = "SELECT * FROM $known_server_tn WHERE hostname='$address'";
1008 $res = $known_server_db->select_dbentry($sql_statement);
1009 if( keys(%$res) == 1) {
1010 $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
1011 if ($act_status eq "down" && $new_status eq "down") {
1012 $sql_statement = "DELETE FROM known_server WHERE hostname='$address'";
1013 $res = $known_server_db->del_dbentry($sql_statement);
1014 daemon_log("$session_id WARNING: failed 2x to send a message to host '$address', delete host from known_server", 3);
1015 }
1016 else {
1017 $sql_statement = "UPDATE known_server SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
1018 $res = $known_server_db->update_dbentry($sql_statement);
1019 if($new_status eq "down"){
1020 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
1021 } else {
1022 daemon_log("$session_id DEBUG: set '$address' from status '$act_status' to '$new_status'", 138);
1023 }
1024 }
1025 }
1026 return $error;
1027 }
1030 sub update_jobdb_status_for_send_msgs {
1031 my ($session_id, $answer, $error) = @_;
1032 if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
1033 &daemon_log("$session_id DEBUG: try to update job status", 138);
1034 my $jobdb_id = $1;
1036 $answer =~ /<header>(.*)<\/header>/;
1037 my $job_header = $1;
1039 $answer =~ /<target>(.*)<\/target>/;
1040 my $job_target = $1;
1042 # Sending msg failed
1043 if( $error ) {
1045 # Set jobs to done, jobs do not need to deliver their message in any case
1046 if (($job_header eq "trigger_action_localboot")
1047 ||($job_header eq "trigger_action_lock")
1048 ||($job_header eq "trigger_action_halt")
1049 ) {
1050 my $sql_statement = "UPDATE $job_queue_tn SET status='done' WHERE id=$jobdb_id";
1051 my $res = $job_db->update_dbentry($sql_statement);
1053 # Reactivate jobs, jobs need to deliver their message
1054 } elsif (($job_header eq "trigger_action_activate")
1055 ||($job_header eq "trigger_action_update")
1056 ||($job_header eq "trigger_action_reinstall")
1057 ||($job_header eq "trigger_activate_new")
1058 ) {
1059 &reactivate_job_with_delay($session_id, $job_target, $job_header, 30 );
1061 # For all other messages
1062 } else {
1063 my $sql_statement = "UPDATE $job_queue_tn ".
1064 "SET status='error', result='can not deliver msg, please consult log file' ".
1065 "WHERE id=$jobdb_id";
1066 my $res = $job_db->update_dbentry($sql_statement);
1067 }
1069 # Sending msg was successful
1070 } else {
1071 # Set jobs localboot, lock, activate, halt, reboot and wake to done
1072 # jobs reinstall, update, inst_update do themself setting to done
1073 if (($job_header eq "trigger_action_localboot")
1074 ||($job_header eq "trigger_action_lock")
1075 ||($job_header eq "trigger_action_activate")
1076 ||($job_header eq "trigger_action_halt")
1077 ||($job_header eq "trigger_action_reboot")
1078 ||($job_header eq "trigger_action_wake")
1079 ||($job_header eq "trigger_wake")
1080 ) {
1082 my $sql_statement = "UPDATE $job_queue_tn ".
1083 "SET status='done' ".
1084 "WHERE id=$jobdb_id AND status='processed'";
1085 my $res = $job_db->update_dbentry($sql_statement);
1086 } else {
1087 &daemon_log("$session_id DEBUG: sending message succeed but cannot update job status.", 138);
1088 }
1089 }
1090 } else {
1091 &daemon_log("$session_id DEBUG: cannot update job status, msg has no jobdb_id-tag.", 138);
1092 }
1093 }
1095 sub reactivate_job_with_delay {
1096 my ($session_id, $target, $header, $delay) = @_ ;
1097 # 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
1099 if (not defined $delay) { $delay = 30 } ;
1100 my $delay_timestamp = &calc_timestamp(&get_time(), "plus", $delay);
1102 my $sql = "UPDATE $job_queue_tn Set timestamp='$delay_timestamp', status='waiting' WHERE (macaddress LIKE 'target' AND headertag='$header')";
1103 my $res = $job_db->update_dbentry($sql);
1104 daemon_log("$session_id INFO: '$header'-job will be reactivated at '$delay_timestamp' ".
1105 "cause client '$target' is currently not available", 5);
1106 return;
1107 }
1110 sub sig_handler {
1111 my ($kernel, $signal) = @_[KERNEL, ARG0] ;
1112 daemon_log("0 INFO got signal '$signal'", 1);
1113 $kernel->sig_handled();
1114 return;
1115 }
1118 sub msg_to_decrypt {
1119 my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
1120 my $session_id = $session->ID;
1121 my ($msg, $msg_hash, $module);
1122 my $error = 0;
1124 # fetch new msg out of @msgs_to_decrypt
1125 my $tmp_next_msg = shift @msgs_to_decrypt;
1126 my ($next_msg, $msg_source) = split(/;/, $tmp_next_msg);
1128 # msg is from a new client or gosa
1129 ($msg, $msg_hash, $module) = &input_from_unknown_host($next_msg, $session_id);
1131 # msg is from a gosa-si-server
1132 if(((!$msg) || (!$msg_hash) || (!$module)) && ($serverPackages_enabled eq "true")){
1133 if (not defined $msg_source)
1134 {
1135 # Only needed, to be compatible with older gosa-si-server versions
1136 ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $heap->{'remote_ip'}, $session_id);
1137 }
1138 else
1139 {
1140 ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $msg_source, $session_id);
1141 }
1142 }
1143 # msg is from a gosa-si-client
1144 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1145 if (not defined $msg_source)
1146 {
1147 # Only needed, to be compatible with older gosa-si-server versions
1148 ($msg, $msg_hash, $module) = &input_from_known_client($next_msg, $heap->{'remote_ip'}, $session_id);
1149 }
1150 else
1151 {
1152 ($msg, $msg_hash, $module) = &input_from_known_client($next_msg, $msg_source, $session_id);
1153 }
1154 }
1155 # an error occurred
1156 if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1157 # If an incoming msg could not be decrypted (maybe a wrong key), decide if msg comes from a client
1158 # or a server. In case of a client, send a ping. If the client could not understand a msg from its
1159 # server the client cause a re-registering process. In case of a server, decrease update_time in kown_server_db
1160 # and trigger a re-registering process for servers
1161 if (defined $msg_source && $msg_source =~ /:$server_port$/ && $serverPackages_enabled eq "true")
1162 {
1163 daemon_log("$session_id WARNING: Cannot understand incoming msg from server '$msg_source'. Cause re-registration process for servers.", 3);
1164 my $update_statement = "UPDATE $known_server_tn SET update_time='19700101000000' WHERE hostname='$msg_source'";
1165 daemon_log("$session_id DEBUG: $update_statement", 7);
1166 my $upadte_res = $known_server_db->exec_statement($update_statement);
1167 $kernel->yield("register_at_foreign_servers");
1168 }
1169 elsif ((defined $msg_source) && (not $msg_source =~ /:$server_port$/))
1170 {
1171 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);
1172 #my $remote_ip = $heap->{'remote_ip'};
1173 #my $remote_port = $heap->{'remote_port'};
1174 my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source><target>$msg_source</target></xml>";
1175 my ($test_error, $test_error_string) = &send_msg_to_target($ping_msg, "$msg_source", "dummy-key", "gosa_ping", $session_id);
1176 daemon_log("$session_id WARNING: Sending msg to cause re-registering: $ping_msg", 3);
1177 }
1178 else
1179 {
1180 my $foreign_host = defined $msg_source ? $msg_source : $heap->{'remote_ip'};
1181 daemon_log("$session_id ERROR: Incoming message from host '$foreign_host' cannot be understood. Processing aborted!", 1);
1182 daemon_log("$session_id DEBUG: Aborted message: $tmp_next_msg", 11);
1183 }
1185 $error++
1186 }
1189 my $header;
1190 my $target;
1191 my $source;
1192 my $done = 0;
1193 my $sql;
1194 my $res;
1196 # check whether this message should be processed here
1197 if ($error == 0) {
1198 $header = @{$msg_hash->{'header'}}[0];
1199 $target = @{$msg_hash->{'target'}}[0];
1200 $source = @{$msg_hash->{'source'}}[0];
1201 my $not_found_in_known_clients_db = 0;
1202 my $not_found_in_known_server_db = 0;
1203 my $not_found_in_foreign_clients_db = 0;
1204 my $local_address;
1205 my $local_mac;
1206 my ($target_ip, $target_port) = split(':', $target);
1208 # Determine the local ip address if target is an ip address
1209 if ($target =~ /^\d+\.\d+\.\d+\.\d+:\d+$/) {
1210 $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1211 } else {
1212 $local_address = $server_address;
1213 }
1215 # Determine the local mac address if target is a mac address
1216 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) {
1217 my $loc_ip = &get_local_ip_for_remote_ip($heap->{'remote_ip'});
1218 my $network_interface= &get_interface_for_ip($loc_ip);
1219 $local_mac = &get_mac_for_interface($network_interface);
1220 } else {
1221 $local_mac = $server_mac_address;
1222 }
1224 # target and source is equal to GOSA -> process here
1225 if (not $done) {
1226 if ($target eq "GOSA" && $source eq "GOSA") {
1227 $done = 1;
1228 &daemon_log("$session_id DEBUG: target and source is 'GOSA' -> process '$header' here", 11);
1229 }
1230 }
1232 # target is own address without forward_to_gosa-tag -> process here
1233 if (not $done) {
1234 #if ((($target eq $local_address) || ($target eq $local_mac) ) && (not exists $msg_hash->{'forward_to_gosa'})) {
1235 if (($target eq $local_address) && (not exists $msg_hash->{'forward_to_gosa'})) {
1236 $done = 1;
1237 if ($source eq "GOSA") {
1238 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1239 }
1240 &daemon_log("$session_id DEBUG: target is own address without forward_to_gosa-tag -> process '$header' here", 11);
1241 }
1242 }
1244 # target is own address with forward_to_gosa-tag not pointing to myself -> process here
1245 if (not $done) {
1246 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1247 my $gosa_at;
1248 my $gosa_session_id;
1249 if (($target eq $local_address) && (defined $forward_to_gosa)){
1250 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1251 if ($gosa_at ne $local_address) {
1252 $done = 1;
1253 &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag not pointing to myself -> process '$header' here", 11);
1254 }
1255 }
1256 }
1258 # Target is a client address and there is a processing function within a plugin -> process loaclly
1259 if (not $done)
1260 {
1261 # Check if target is a client address
1262 $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1263 $res = $known_clients_db->select_dbentry($sql);
1264 if ((keys(%$res) > 0) )
1265 {
1266 my $hostname = $res->{1}->{'hostname'};
1267 my $reduced_header = $header;
1268 $reduced_header =~ s/gosa_//;
1269 # Check if there is a processing function within a plugin
1270 if (exists $known_functions->{$reduced_header})
1271 {
1272 $msg =~ s/<target>\S*<\/target>/<target>$hostname<\/target>/;
1273 $done = 1;
1274 &daemon_log("$session_id DEBUG: Target is client address with processing function within a plugin -> process '$header' here", 11);
1275 }
1276 }
1277 }
1279 # If header has a 'job_' prefix, do always process message locally
1280 # which means put it into job queue
1281 if ((not $done) && ($header =~ /job_/))
1282 {
1283 $done = 1;
1284 &daemon_log("$session_id DEBUG: Header has a 'job_' prefix. Put it into job queue. -> process '$header' here", 11);
1285 }
1287 # if message should be processed here -> add message to incoming_db
1288 if ($done) {
1289 # if a 'job_' or a 'gosa_' message comes from a foreign server, fake module from
1290 # ServerPackages to GosaPackages so gosa-si-server knows how to process this kind of messages
1291 if ($header =~ /^gosa_/ || $header =~ /^job_/) {
1292 $module = "GosaPackages";
1293 }
1295 my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1296 primkey=>[],
1297 headertag=>$header,
1298 targettag=>$target,
1299 xmlmessage=>&encode_base64($msg),
1300 timestamp=>&get_time,
1301 module=>$module,
1302 sessionid=>$session_id,
1303 } );
1305 }
1307 # target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa
1308 if (not $done) {
1309 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1310 my $gosa_at;
1311 my $gosa_session_id;
1312 if (($target eq $local_address) && (defined $forward_to_gosa)){
1313 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1314 if ($gosa_at eq $local_address) {
1315 my $session_reference = $kernel->ID_id_to_session($gosa_session_id);
1316 if( defined $session_reference ) {
1317 $heap = $session_reference->get_heap();
1318 }
1319 if(exists $heap->{'client'}) {
1320 $msg = &encrypt_msg($msg, $GosaPackages_key);
1321 $heap->{'client'}->put($msg);
1322 &daemon_log("$session_id DEBUG: incoming '$header' message forwarded to GOsa", 11);
1323 }
1324 $done = 1;
1325 &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag pointing at myself -> forward '$header' to gosa", 11);
1326 }
1327 }
1329 }
1331 # target is a client address in known_clients -> forward to client
1332 if (not $done) {
1333 $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1334 $res = $known_clients_db->select_dbentry($sql);
1335 if (keys(%$res) > 0)
1336 {
1337 $done = 1;
1338 &daemon_log("$session_id DEBUG: target is a client address in known_clients -> forward '$header' to client", 11);
1339 my $hostkey = $res->{1}->{'hostkey'};
1340 my $hostname = $res->{1}->{'hostname'};
1341 $msg =~ s/<target>\S*<\/target>/<target>$hostname<\/target>/;
1342 $msg =~ s/<header>gosa_/<header>/;
1343 my $error= &send_msg_to_target($msg, $hostname, $hostkey, $header, $session_id);
1344 if ($error) {
1345 &daemon_log("$session_id ERROR: Some problems occurred while trying to send msg to client '$hostname': $msg", 1);
1346 }
1347 }
1348 else
1349 {
1350 $not_found_in_known_clients_db = 1;
1351 }
1352 }
1354 # target is a client address in foreign_clients -> forward to registration server
1355 if (not $done) {
1356 $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1357 $res = $foreign_clients_db->select_dbentry($sql);
1358 if (keys(%$res) > 0) {
1359 my $hostname = $res->{1}->{'hostname'};
1360 my ($host_ip, $host_port) = split(/:/, $hostname);
1361 my $local_address = &get_local_ip_for_remote_ip($host_ip).":$server_port";
1362 my $regserver = $res->{1}->{'regserver'};
1363 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$regserver'";
1364 my $res = $known_server_db->select_dbentry($sql);
1365 if (keys(%$res) > 0) {
1366 my $regserver_key = $res->{1}->{'hostkey'};
1367 $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1368 $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1369 if ($source eq "GOSA") {
1370 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1371 }
1372 my $error= &send_msg_to_target($msg, $regserver, $regserver_key, $header, $session_id);
1373 if ($error) {
1374 &daemon_log("$session_id ERROR: some problems occurred while trying to send msg to registration server: $msg", 1);
1375 }
1376 }
1377 $done = 1;
1378 &daemon_log("$session_id DEBUG: target is a client address in foreign_clients -> forward '$header' to registration server", 11);
1379 } else {
1380 $not_found_in_foreign_clients_db = 1;
1381 }
1382 }
1384 # target is a server address -> forward to server
1385 if (not $done) {
1386 $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1387 $res = $known_server_db->select_dbentry($sql);
1388 if (keys(%$res) > 0) {
1389 my $hostkey = $res->{1}->{'hostkey'};
1391 if ($source eq "GOSA") {
1392 $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1393 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1395 }
1397 &send_msg_to_target($msg, $target, $hostkey, $header, $session_id);
1398 $done = 1;
1399 &daemon_log("$session_id DEBUG: target is a server address -> forward '$header' to server", 11);
1400 } else {
1401 $not_found_in_known_server_db = 1;
1402 }
1403 }
1406 # target is not in foreign_clients_db, known_server_db or known_clients_db, maybe it is a complete new one -> process here
1407 if ( $not_found_in_foreign_clients_db
1408 && $not_found_in_known_server_db
1409 && $not_found_in_known_clients_db) {
1410 &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);
1411 if ($header =~ /^gosa_/ || $header =~ /^job_/) {
1412 $module = "GosaPackages";
1413 }
1414 my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1415 primkey=>[],
1416 headertag=>$header,
1417 targettag=>$target,
1418 xmlmessage=>&encode_base64($msg),
1419 timestamp=>&get_time,
1420 module=>$module,
1421 sessionid=>$session_id,
1422 } );
1423 $done = 1;
1424 }
1427 if (not $done) {
1428 daemon_log("$session_id ERROR: do not know what to do with this message: $msg", 1);
1429 if ($source eq "GOSA") {
1430 my %data = ('error_msg' => &encode_base64($msg), 'error_string' => "Do not know what to do with this message!");
1431 my $error_msg = &build_msg("error", $local_address, "GOSA", \%data );
1433 my $session_reference = $kernel->ID_id_to_session($session_id);
1434 if( defined $session_reference ) {
1435 $heap = $session_reference->get_heap();
1436 }
1437 if(exists $heap->{'client'}) {
1438 $error_msg = &encrypt_msg($error_msg, $GosaPackages_key);
1439 $heap->{'client'}->put($error_msg);
1440 }
1441 }
1442 }
1444 }
1446 return;
1447 }
1450 sub next_task {
1451 my ($session, $heap, $task) = @_[SESSION, HEAP, ARG0, ARG1];
1452 my $running_task = POE::Wheel::Run->new(
1453 Program => sub { process_task($session, $heap, $task) },
1454 StdioFilter => POE::Filter::Reference->new(),
1455 StdoutEvent => "task_result",
1456 StderrEvent => "task_debug",
1457 CloseEvent => "task_done",
1458 );
1459 $heap->{task}->{ $running_task->ID } = $running_task;
1460 }
1462 sub handle_task_result {
1463 my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
1464 my $client_answer = $result->{'answer'};
1465 if( $client_answer =~ s/session_id=(\d+)$// ) {
1466 my $session_id = $1;
1467 if( defined $session_id ) {
1468 my $session_reference = $kernel->ID_id_to_session($session_id);
1469 if( defined $session_reference ) {
1470 $heap = $session_reference->get_heap();
1471 }
1472 }
1474 if(exists $heap->{'client'}) {
1475 $heap->{'client'}->put($client_answer);
1476 }
1477 }
1478 $kernel->sig(CHLD => "child_reap");
1479 }
1481 sub handle_task_debug {
1482 my $result = $_[ARG0];
1483 print STDERR "$result\n";
1484 }
1486 sub handle_task_done {
1487 my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1488 delete $heap->{task}->{$task_id};
1489 if (exists $heap->{ldap_handle}->{$task_id}) {
1490 &release_ldap_handle($heap->{ldap_handle}->{$task_id});
1491 }
1492 }
1494 sub process_task {
1495 no strict "refs";
1496 #CHECK: Not @_[...]?
1497 my ($session, $heap, $task) = @_;
1498 my $error = 0;
1499 my $answer_l;
1500 my ($answer_header, @answer_target_l, $answer_source);
1501 my $client_answer = "";
1503 # prepare all variables needed to process message
1504 #my $msg = $task->{'xmlmessage'};
1505 my $msg = &decode_base64($task->{'xmlmessage'});
1506 my $incoming_id = $task->{'id'};
1507 my $module = $task->{'module'};
1508 my $header = $task->{'headertag'};
1509 my $session_id = $task->{'sessionid'};
1510 my $msg_hash;
1511 eval {
1512 $msg_hash = $xml->XMLin($msg, ForceArray=>1);
1513 };
1514 daemon_log("ERROR: XML failure '$@'") if ($@);
1515 my $source = @{$msg_hash->{'source'}}[0];
1517 # set timestamp of incoming client uptodate, so client will not
1518 # be deleted from known_clients because of expiration
1519 my $cur_time = &get_time();
1520 my $sql = "UPDATE $known_clients_tn SET timestamp='$cur_time' WHERE hostname='$source'";
1521 my $res = $known_clients_db->exec_statement($sql);
1523 ######################
1524 # process incoming msg
1525 if( $error == 0) {
1526 daemon_log("$session_id INFO: Incoming msg (session_id=$session_id) with header '".@{$msg_hash->{'header'}}[0]."'", 5);
1527 daemon_log("$session_id DEBUG: Processing module ".$module, 26);
1528 $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1530 if ( 0 < @{$answer_l} ) {
1531 my $answer_str = join("\n", @{$answer_l});
1532 my @headers;
1533 while ($answer_str =~ /<header>(\w+)<\/header>/g) {
1534 push(@headers, $1);
1535 }
1536 daemon_log("$session_id INFO: got answer message(s) with header: '".join("', '", @headers)."'", 5);
1537 daemon_log("$session_id DEBUG: $module: got answer from module: \n".$answer_str,26);
1538 } else {
1539 daemon_log("$session_id DEBUG: $module: got no answer from module!" ,26);
1540 }
1542 }
1543 if( !$answer_l ) { $error++ };
1545 ########
1546 # answer
1547 if( $error == 0 ) {
1549 foreach my $answer ( @{$answer_l} ) {
1550 # check outgoing msg to xml validity
1551 my ($answer, $answer_hash) = &check_outgoing_xml_validity($answer, $session_id);
1552 if( not defined $answer_hash ) { next; }
1554 $answer_header = @{$answer_hash->{'header'}}[0];
1555 @answer_target_l = @{$answer_hash->{'target'}};
1556 $answer_source = @{$answer_hash->{'source'}}[0];
1558 # deliver msg to all targets
1559 foreach my $answer_target ( @answer_target_l ) {
1561 # targets of msg are all gosa-si-clients in known_clients_db
1562 if( $answer_target eq "*" ) {
1563 # answer is for all clients
1564 my $sql_statement= "SELECT * FROM known_clients";
1565 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1566 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1567 my $host_name = $hit->{hostname};
1568 my $host_key = $hit->{hostkey};
1569 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1570 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1571 }
1572 }
1574 # targets of msg are all gosa-si-server in known_server_db
1575 elsif( $answer_target eq "KNOWN_SERVER" ) {
1576 # answer is for all server in known_server
1577 my $sql_statement= "SELECT * FROM $known_server_tn";
1578 my $query_res = $known_server_db->select_dbentry( $sql_statement );
1579 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1580 my $host_name = $hit->{hostname};
1581 my $host_key = $hit->{hostkey};
1582 $answer =~ s/<target>\S+<\/target>/<target>$host_name<\/target>/g;
1583 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1584 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1585 }
1586 }
1588 # target of msg is GOsa
1589 elsif( $answer_target eq "GOSA" ) {
1590 my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1591 my $add_on = "";
1592 if( defined $session_id ) {
1593 $add_on = ".session_id=$session_id";
1594 }
1595 # answer is for GOSA and has to returned to connected client
1596 my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1597 $client_answer = $gosa_answer.$add_on;
1598 }
1600 # target of msg is job queue at this host
1601 elsif( $answer_target eq "JOBDB") {
1602 $answer =~ /<header>(\S+)<\/header>/;
1603 my $header;
1604 if( defined $1 ) { $header = $1; }
1605 my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1606 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1607 }
1609 # Target of msg is a mac address
1610 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 ) {
1611 daemon_log("$session_id DEBUG: target is mac address '$answer_target', looking for host in known_clients and foreign_clients", 138);
1613 # Looking for macaddress in known_clients
1614 my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1615 my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1616 my $found_ip_flag = 0;
1617 while( my ($hit_num, $hit) = each %{ $query_res } ) {
1618 my $host_name = $hit->{hostname};
1619 my $host_key = $hit->{hostkey};
1620 $answer =~ s/$answer_target/$host_name/g;
1621 daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1622 my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1623 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1624 $found_ip_flag++ ;
1625 }
1627 # Looking for macaddress in foreign_clients
1628 if ($found_ip_flag == 0) {
1629 my $sql = "SELECT * FROM $foreign_clients_tn WHERE macaddress LIKE '$answer_target'";
1630 my $res = $foreign_clients_db->select_dbentry($sql);
1631 while( my ($hit_num, $hit) = each %{ $res } ) {
1632 my $host_name = $hit->{hostname};
1633 my $reg_server = $hit->{regserver};
1634 daemon_log("$session_id INFO: found host '$host_name' with mac '$answer_target', registered at '$reg_server'", 5);
1636 # Fetch key for reg_server
1637 my $reg_server_key;
1638 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$reg_server'";
1639 my $res = $known_server_db->select_dbentry($sql);
1640 if (exists $res->{1}) {
1641 $reg_server_key = $res->{1}->{'hostkey'};
1642 } else {
1643 daemon_log("$session_id ERROR: cannot find hostkey for '$host_name' in '$known_server_tn'", 1);
1644 daemon_log("$session_id ERROR: unable to forward answer to correct registration server, processing is aborted!", 1);
1645 $reg_server_key = undef;
1646 }
1648 # Send answer to server where client is registered
1649 if (defined $reg_server_key) {
1650 $answer =~ s/$answer_target/$host_name/g;
1651 my $error = &send_msg_to_target($answer, $reg_server, $reg_server_key, $answer_header, $session_id);
1652 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1653 $found_ip_flag++ ;
1654 }
1655 }
1656 }
1658 # No mac to ip matching found
1659 if( $found_ip_flag == 0) {
1660 daemon_log("$session_id WARNING: no host found in known_clients or foreign_clients with mac address '$answer_target'", 3);
1661 &reactivate_job_with_delay($session_id, $answer_target, $answer_header, 30);
1662 }
1664 # Answer is for one specific host
1665 } else {
1666 # get encrypt_key
1667 my $encrypt_key = &get_encrypt_key($answer_target);
1668 if( not defined $encrypt_key ) {
1669 # unknown target
1670 daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1671 next;
1672 }
1673 my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1674 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1675 }
1676 }
1677 }
1678 }
1680 my $filter = POE::Filter::Reference->new();
1681 my %result = (
1682 status => "seems ok to me",
1683 answer => $client_answer,
1684 );
1686 my $output = $filter->put( [ \%result ] );
1687 print @$output;
1690 }
1692 sub session_start {
1693 my ($kernel) = $_[KERNEL];
1694 $global_kernel = $kernel;
1695 $kernel->yield('register_at_foreign_servers');
1696 $kernel->yield('create_fai_server_db', $fai_server_tn );
1697 $kernel->yield('create_fai_release_db', $fai_release_tn );
1698 $kernel->yield('watch_for_next_tasks');
1699 $kernel->sig(USR1 => "sig_handler");
1700 $kernel->sig(USR2 => "recreate_packages_db");
1701 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1702 $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay);
1703 $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay);
1704 $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1705 $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1706 $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1707 $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1709 # Start opsi check
1710 if ($opsi_enabled eq "true") {
1711 $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay);
1712 }
1714 }
1717 sub watch_for_done_jobs {
1718 my $kernel = $_[KERNEL];
1720 my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((status='done') AND (modified='0'))";
1721 my $res = $job_db->select_dbentry( $sql_statement );
1723 while( my ($number, $hit) = each %{$res} )
1724 {
1725 # Non periodical jobs can be deleted.
1726 if ($hit->{periodic} eq "none")
1727 {
1728 my $jobdb_id = $hit->{id};
1729 my $sql_statement = "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1730 my $res = $job_db->del_dbentry($sql_statement);
1731 }
1733 # Periodical jobs should not be deleted but reactivated with new timestamp instead.
1734 else
1735 {
1736 my ($p_time, $periodic) = split("_", $hit->{periodic});
1737 my $reactivated_ts = $hit->{timestamp};
1738 my $act_ts = int(&get_time());
1739 while ($act_ts > int($reactivated_ts)) # Redo calculation to avoid multiple jobs in the past
1740 {
1741 $reactivated_ts = &calc_timestamp($reactivated_ts, "plus", $p_time, $periodic);
1742 }
1743 my $sql = "UPDATE $job_queue_tn SET status='waiting', timestamp='$reactivated_ts' WHERE id='".$hit->{id}."'";
1744 my $res = $job_db->exec_statement($sql);
1745 &daemon_log("J INFO: Update periodical job '".$hit->{headertag}."' for client '".$hit->{targettag}."'. New execution time '$reactivated_ts'.", 5);
1746 }
1747 }
1749 $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1750 }
1753 sub watch_for_opsi_jobs {
1754 my ($kernel) = $_[KERNEL];
1756 # 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
1757 # opsi install job is to parse the xml message. There is still the correct header.
1758 my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((xmlmessage LIKE '%opsi_install_client</header>%') AND (status='processing') AND (siserver='localhost'))";
1759 my $res = $job_db->select_dbentry( $sql_statement );
1761 # Ask OPSI for an update of the running jobs
1762 while (my ($id, $hit) = each %$res ) {
1763 # Determine current parameters of the job
1764 my $hostId = $hit->{'plainname'};
1765 my $macaddress = $hit->{'macaddress'};
1766 my $progress = $hit->{'progress'};
1768 my $result= {};
1770 # For hosts, only return the products that are or get installed
1771 my $callobj;
1772 $callobj = {
1773 method => 'getProductStates_hash',
1774 params => [ $hostId ],
1775 id => 1,
1776 };
1778 my $hres = $opsi_client->call($opsi_url, $callobj);
1779 #my ($hres_err, $hres_err_string) = &check_opsi_res($hres);
1780 if (not &check_opsi_res($hres)) {
1781 my $htmp= $hres->result->{$hostId};
1783 # Check state != not_installed or action == setup -> load and add
1784 my $products= 0;
1785 my $installed= 0;
1786 my $installing = 0;
1787 my $error= 0;
1788 my @installed_list;
1789 my @error_list;
1790 my $act_status = "none";
1791 foreach my $product (@{$htmp}){
1793 if ($product->{'installationStatus'} ne "not_installed" or
1794 $product->{'actionRequest'} eq "setup"){
1796 # Increase number of products for this host
1797 $products++;
1799 if ($product->{'installationStatus'} eq "failed"){
1800 $result->{$product->{'productId'}}= "error";
1801 unshift(@error_list, $product->{'productId'});
1802 $error++;
1803 }
1804 if ($product->{'installationStatus'} eq "installed" && $product->{'actionRequest'} eq "none"){
1805 $result->{$product->{'productId'}}= "installed";
1806 unshift(@installed_list, $product->{'productId'});
1807 $installed++;
1808 }
1809 if ($product->{'installationStatus'} eq "installing"){
1810 $result->{$product->{'productId'}}= "installing";
1811 $installing++;
1812 $act_status = "installing - ".$product->{'productId'};
1813 }
1814 }
1815 }
1817 # Estimate "rough" progress, avoid division by zero
1818 if ($products == 0) {
1819 $result->{'progress'}= 0;
1820 } else {
1821 $result->{'progress'}= int($installed * 100 / $products);
1822 }
1824 # Set updates in job queue
1825 if ((not $error) && (not $installing) && ($installed)) {
1826 $act_status = "installed - ".join(", ", @installed_list);
1827 }
1828 if ($error) {
1829 $act_status = "error - ".join(", ", @error_list);
1830 }
1831 if ($progress ne $result->{'progress'} ) {
1832 # Updating progress and result
1833 my $update_statement = "UPDATE $job_queue_tn SET modified='1', progress='".$result->{'progress'}."', result='$act_status' WHERE macaddress LIKE '$macaddress' AND siserver='localhost'";
1834 my $update_res = $job_db->update_dbentry($update_statement);
1835 }
1836 if ($progress eq 100) {
1837 # Updateing status
1838 my $done_statement = "UPDATE $job_queue_tn SET modified='1', ";
1839 if ($error) {
1840 $done_statement .= "status='error'";
1841 } else {
1842 $done_statement .= "status='done'";
1843 }
1844 $done_statement .= " WHERE macaddress LIKE '$macaddress' AND siserver='localhost'";
1845 my $done_res = $job_db->update_dbentry($done_statement);
1846 }
1849 }
1850 }
1852 $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay);
1853 }
1856 # If a job got an update or was modified anyway, send to all other si-server an update message of this jobs.
1857 sub watch_for_modified_jobs {
1858 my ($kernel,$heap) = @_[KERNEL, HEAP];
1860 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE (modified='1')";
1861 my $res = $job_db->select_dbentry( $sql_statement );
1863 # if db contains no jobs which should be update, do nothing
1864 if (keys %$res != 0) {
1866 if ($job_synchronization eq "true") {
1867 # make out of the db result a gosa-si message
1868 my $update_msg = &db_res2si_msg ($res, "foreign_job_updates", "KNOWN_SERVER", "MY_LOCAL_ADDRESS");
1870 # update all other SI-server
1871 &inform_all_other_si_server($update_msg);
1872 }
1874 # set jobs all jobs to modified = 0, wait until the next modification for updates of other si-server
1875 $sql_statement = "UPDATE $job_queue_tn SET modified='0' ";
1876 $res = $job_db->update_dbentry($sql_statement);
1877 }
1879 $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay);
1880 }
1883 sub watch_for_new_jobs {
1884 if($watch_for_new_jobs_in_progress == 0) {
1885 $watch_for_new_jobs_in_progress = 1;
1886 my ($kernel,$heap) = @_[KERNEL, HEAP];
1888 # check gosa job queue for jobs with executable timestamp
1889 my $timestamp = &get_time();
1890 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE siserver='localhost' AND status='waiting' AND (CAST(timestamp AS UNSIGNED)) < $timestamp ORDER BY timestamp";
1891 my $res = $job_db->exec_statement( $sql_statement );
1893 # Merge all new jobs that would do the same actions
1894 my @drops;
1895 my $hits;
1896 foreach my $hit (reverse @{$res} ) {
1897 my $macaddress= lc @{$hit}[8];
1898 my $headertag= @{$hit}[5];
1899 if(
1900 defined($hits->{$macaddress}) &&
1901 defined($hits->{$macaddress}->{$headertag}) &&
1902 defined($hits->{$macaddress}->{$headertag}[0])
1903 ) {
1904 push @drops, "DELETE FROM $job_queue_tn WHERE id = $hits->{$macaddress}->{$headertag}[0]";
1905 }
1906 $hits->{$macaddress}->{$headertag}= $hit;
1907 }
1909 # Delete new jobs with a matching job in state 'processing'
1910 foreach my $macaddress (keys %{$hits}) {
1911 foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1912 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1913 if(defined($jobdb_id)) {
1914 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND headertag='$jobdb_headertag' AND status='processing'";
1915 my $res = $job_db->exec_statement( $sql_statement );
1916 foreach my $hit (@{$res}) {
1917 push @drops, "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1918 }
1919 } else {
1920 daemon_log("J ERROR: Job without id exists for macaddress $macaddress!", 1);
1921 }
1922 }
1923 }
1925 # Commit deletion
1926 $job_db->exec_statementlist(\@drops);
1928 # Look for new jobs that could be executed
1929 foreach my $macaddress (keys %{$hits}) {
1931 # Look if there is an executing job
1932 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='processing'";
1933 my $res = $job_db->exec_statement( $sql_statement );
1935 # Skip new jobs for host if there is a processing job
1936 if(defined($res) and defined @{$res}[0]) {
1937 # Prevent race condition if there is a trigger_activate job waiting and a goto-activation job processing
1938 my $row = @{$res}[0] if (ref $res eq 'ARRAY');
1939 if(@{$row}[5] eq 'trigger_action_reinstall') {
1940 my $sql_statement_2 = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='waiting' AND headertag = 'trigger_activate_new'";
1941 my $res_2 = $job_db->exec_statement( $sql_statement_2 );
1942 if(defined($res_2) and defined @{$res_2}[0]) {
1943 # Set status from goto-activation to 'waiting' and update timestamp
1944 $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'");
1945 }
1946 }
1947 next;
1948 }
1950 foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1951 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1952 if(defined($jobdb_id)) {
1953 my $job_msg = @{$hits->{$macaddress}->{$jobdb_headertag}}[7];
1955 daemon_log("J DEBUG: its time to execute $job_msg", 7);
1956 my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1957 my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1959 # expect macaddress is unique!!!!!!
1960 my $target = $res_hash->{1}->{hostname};
1962 # change header
1963 $job_msg =~ s/<header>job_/<header>gosa_/;
1965 # add sqlite_id
1966 $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1968 $job_msg =~ /<header>(\S+)<\/header>/;
1969 my $header = $1 ;
1970 my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");
1972 # update status in job queue to ...
1973 # ... 'processing', for jobs: 'reinstall', 'update', activate_new
1974 if (($header =~ /gosa_trigger_action_reinstall/)
1975 || ($header =~ /gosa_trigger_activate_new/)
1976 || ($header =~ /gosa_trigger_action_update/)) {
1977 my $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
1978 my $dbres = $job_db->update_dbentry($sql_statement);
1979 }
1981 # ... 'done', for all other jobs, they are no longer needed in the jobqueue
1982 else {
1983 my $sql_statement = "UPDATE $job_queue_tn SET status='done' WHERE id=$jobdb_id";
1984 my $dbres = $job_db->exec_statement($sql_statement);
1985 }
1988 # We don't want parallel processing
1989 last;
1990 }
1991 }
1992 }
1994 $watch_for_new_jobs_in_progress = 0;
1995 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1996 }
1997 }
2000 sub watch_for_new_messages {
2001 my ($kernel,$heap) = @_[KERNEL, HEAP];
2002 my @coll_user_msg; # collection list of outgoing messages
2004 # check messaging_db for new incoming messages with executable timestamp
2005 my $timestamp = &get_time();
2006 my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS UNSIGNED))<$timestamp AND flag='n' AND direction='in' )";
2007 my $res = $messaging_db->exec_statement( $sql_statement );
2008 foreach my $hit (@{$res}) {
2010 # create outgoing messages
2011 my $message_to = @{$hit}[3];
2012 # translate message_to to plain login name
2013 my @message_to_l = split(/,/, $message_to);
2014 my %receiver_h;
2015 foreach my $receiver (@message_to_l) {
2016 if ($receiver =~ /^u_([\s\S]*)$/) {
2017 $receiver_h{$1} = 0;
2018 } elsif ($receiver =~ /^g_([\s\S]*)$/) {
2019 my $group_name = $1;
2020 # fetch all group members from ldap and add them to receiver hash
2021 my $ldap_handle = &get_ldap_handle();
2022 if (defined $ldap_handle) {
2023 my $mesg = $ldap_handle->search(
2024 base => $ldap_base,
2025 scope => 'sub',
2026 attrs => ['memberUid'],
2027 filter => "cn=$group_name",
2028 );
2029 if ($mesg->count) {
2030 my @entries = $mesg->entries;
2031 foreach my $entry (@entries) {
2032 my @receivers= $entry->get_value("memberUid");
2033 foreach my $receiver (@receivers) {
2034 $receiver_h{$receiver} = 0;
2035 }
2036 }
2037 }
2038 # translating errors ?
2039 if ($mesg->code) {
2040 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: $mesg->error", 1);
2041 }
2042 &release_ldap_handle($ldap_handle);
2043 # ldap handle error ?
2044 } else {
2045 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: no ldap handle available", 1);
2046 }
2047 } else {
2048 my $sbjct = &encode_base64(@{$hit}[1]);
2049 my $msg = &encode_base64(@{$hit}[7]);
2050 &daemon_log("M WARNING: unknown receiver '$receiver' for a user-message '$sbjct - $msg'", 3);
2051 }
2052 }
2053 my @receiver_l = keys(%receiver_h);
2055 my $message_id = @{$hit}[0];
2057 #add each outgoing msg to messaging_db
2058 my $receiver;
2059 foreach $receiver (@receiver_l) {
2060 my $sql_statement = "INSERT INTO $messaging_tn (id, subject, message_from, message_to, flag, direction, delivery_time, message, timestamp) ".
2061 "VALUES ('".
2062 $message_id."', '". # id
2063 @{$hit}[1]."', '". # subject
2064 @{$hit}[2]."', '". # message_from
2065 $receiver."', '". # message_to
2066 "none"."', '". # flag
2067 "out"."', '". # direction
2068 @{$hit}[6]."', '". # delivery_time
2069 @{$hit}[7]."', '". # message
2070 $timestamp."'". # timestamp
2071 ")";
2072 &daemon_log("M DEBUG: $sql_statement", 1);
2073 my $res = $messaging_db->exec_statement($sql_statement);
2074 &daemon_log("M INFO: message '".@{$hit}[0]."' is prepared for delivery to receiver '$receiver'", 5);
2075 }
2077 # set incoming message to flag d=deliverd
2078 $sql_statement = "UPDATE $messaging_tn SET flag='p' WHERE id='$message_id'";
2079 &daemon_log("M DEBUG: $sql_statement", 7);
2080 $res = $messaging_db->update_dbentry($sql_statement);
2081 &daemon_log("M INFO: message '$message_id' is set to flag 'p' (processed)", 5);
2082 }
2084 $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
2085 return;
2086 }
2088 sub watch_for_delivery_messages {
2089 my ($kernel, $heap) = @_[KERNEL, HEAP];
2091 # select outgoing messages
2092 my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( flag='p' AND direction='out' )";
2093 my $res = $messaging_db->exec_statement( $sql_statement );
2095 # build out msg for each usr
2096 foreach my $hit (@{$res}) {
2097 my $receiver = @{$hit}[3];
2098 my $msg_id = @{$hit}[0];
2099 my $subject = @{$hit}[1];
2100 my $message = @{$hit}[7];
2102 # resolve usr -> host where usr is logged in
2103 my $sql = "SELECT * FROM $login_users_tn WHERE (user='$receiver')";
2104 my $res = $login_users_db->exec_statement($sql);
2106 # receiver is logged in nowhere
2107 if (not ref(@$res[0]) eq "ARRAY") { next; }
2109 # receiver ist logged in at a client registered at local server
2110 my $send_succeed = 0;
2111 foreach my $hit (@$res) {
2112 my $receiver_host = @$hit[0];
2113 my $delivered2host = 0;
2114 &daemon_log("M DEBUG: user '$receiver' is logged in at host '$receiver_host'", 7);
2116 # Looking for host in know_clients_db
2117 my $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$receiver_host')";
2118 my $res = $known_clients_db->exec_statement($sql);
2120 # Host is known in known_clients_db
2121 if (ref(@$res[0]) eq "ARRAY") {
2122 my $receiver_key = @{@{$res}[0]}[2];
2123 my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
2124 my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data );
2125 my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0);
2126 if ($error == 0 ) {
2127 $send_succeed++ ;
2128 $delivered2host++ ;
2129 &daemon_log("M DEBUG: send message for user '$receiver' to host '$receiver_host'", 7);
2130 } else {
2131 &daemon_log("M DEBUG: cannot send message for user '$receiver' to host '$receiver_host'", 7);
2132 }
2133 }
2135 # Message already send, do not need to do anything more, otherwise ...
2136 if ($delivered2host) { next;}
2138 # ...looking for host in foreign_clients_db
2139 $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$receiver_host')";
2140 $res = $foreign_clients_db->exec_statement($sql);
2142 # Host is known in foreign_clients_db
2143 if (ref(@$res[0]) eq "ARRAY") {
2144 my $registration_server = @{@{$res}[0]}[2];
2146 # Fetch encryption key for registration server
2147 my $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$registration_server')";
2148 my $res = $known_server_db->exec_statement($sql);
2149 if (ref(@$res[0]) eq "ARRAY") {
2150 my $registration_server_key = @{@{$res}[0]}[3];
2151 my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
2152 my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data );
2153 my $error = &send_msg_to_target($out_msg, $registration_server, $registration_server_key, "usr_msg", 0);
2154 if ($error == 0 ) {
2155 $send_succeed++ ;
2156 $delivered2host++ ;
2157 &daemon_log("M DEBUG: send message for user '$receiver' to server '$registration_server'", 7);
2158 } else {
2159 &daemon_log("M ERROR: cannot send message for user '$receiver' to server '$registration_server'", 1);
2160 }
2162 } else {
2163 &daemon_log("M ERROR: host '$receiver_host' is reported to be ".
2164 "registrated at server '$registration_server', ".
2165 "but no data available in known_server_db ", 1);
2166 }
2167 }
2169 if (not $delivered2host) {
2170 &daemon_log("M ERROR: unable to send user message to host '$receiver_host'", 1);
2171 }
2172 }
2174 if ($send_succeed) {
2175 # set outgoing msg at db to deliverd
2176 my $sql = "UPDATE $messaging_tn SET flag='d' WHERE (id='$msg_id' AND direction='out' AND message_to='$receiver')";
2177 my $res = $messaging_db->exec_statement($sql);
2178 &daemon_log("M INFO: send message for user '$receiver' to logged in hosts", 5);
2179 } else {
2180 &daemon_log("M WARNING: failed to deliver message for user '$receiver'", 3);
2181 }
2182 }
2184 $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
2185 return;
2186 }
2189 sub watch_for_done_messages {
2190 my ($kernel,$heap) = @_[KERNEL, HEAP];
2192 my $sql = "SELECT * FROM $messaging_tn WHERE (flag='p' AND direction='in')";
2193 my $res = $messaging_db->exec_statement($sql);
2195 foreach my $hit (@{$res}) {
2196 my $msg_id = @{$hit}[0];
2198 my $sql = "SELECT * FROM $messaging_tn WHERE (id='$msg_id' AND direction='out' AND (NOT flag='s'))";
2199 my $res = $messaging_db->exec_statement($sql);
2201 # not all usr msgs have been seen till now
2202 if ( ref(@$res[0]) eq "ARRAY") { next; }
2204 $sql = "DELETE FROM $messaging_tn WHERE (id='$msg_id')";
2205 $res = $messaging_db->exec_statement($sql);
2207 }
2209 $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
2210 return;
2211 }
2214 sub watch_for_old_known_clients {
2215 my ($kernel,$heap) = @_[KERNEL, HEAP];
2217 my $sql_statement = "SELECT * FROM $known_clients_tn";
2218 my $res = $known_clients_db->select_dbentry( $sql_statement );
2220 my $cur_time = int(&get_time());
2222 while ( my ($hit_num, $hit) = each %$res) {
2223 my $expired_timestamp = int($hit->{'timestamp'});
2224 $expired_timestamp =~ /(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/;
2225 my $dt = DateTime->new( year => $1,
2226 month => $2,
2227 day => $3,
2228 hour => $4,
2229 minute => $5,
2230 second => $6,
2231 );
2233 $dt->add( seconds => 2 * int($hit->{'keylifetime'}) );
2234 $expired_timestamp = $dt->ymd('').$dt->hms('');
2235 if ($cur_time > $expired_timestamp) {
2236 my $hostname = $hit->{'hostname'};
2237 my $del_sql = "DELETE FROM $known_clients_tn WHERE hostname='$hostname'";
2238 my $del_res = $known_clients_db->exec_statement($del_sql);
2240 &main::daemon_log("0 INFO: timestamp '".$hit->{'timestamp'}."' of client '$hostname' is expired('$expired_timestamp'), client will be deleted from known_clients_db", 5);
2241 }
2243 }
2245 $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
2246 }
2249 sub watch_for_next_tasks {
2250 my ($kernel,$heap) = @_[KERNEL, HEAP];
2252 my $sql = "SELECT * FROM $incoming_tn";
2253 my $res = $incoming_db->select_dbentry($sql);
2255 while ( my ($hit_num, $hit) = each %$res) {
2256 my $headertag = $hit->{'headertag'};
2257 if ($headertag =~ /^answer_(\d+)/) {
2258 # do not start processing, this message is for a still running POE::Wheel
2259 next;
2260 }
2261 my $message_id = $hit->{'id'};
2262 my $session_id = $hit->{'sessionid'};
2263 &daemon_log("$session_id DEBUG: start processing for message with incoming id: '$message_id'", 11);
2265 $kernel->yield('next_task', $hit);
2267 my $sql = "DELETE FROM $incoming_tn WHERE id=$message_id";
2268 my $res = $incoming_db->exec_statement($sql);
2269 }
2271 $kernel->delay_set('watch_for_next_tasks', 1);
2272 }
2275 sub get_ldap_handle {
2276 my ($session_id) = @_;
2277 my $heap;
2279 if (not defined $session_id ) { $session_id = 0 };
2280 if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
2282 my ($package, $file, $row, $subroutine, $hasArgs, $wantArray, $evalText, $isRequire) = caller(1);
2283 my $caller_text = "subroutine $subroutine";
2284 if ($subroutine eq "(eval)") {
2285 $caller_text = "eval block within file '$file' for '$evalText'";
2286 }
2287 daemon_log("$session_id DEBUG: new ldap handle for '$caller_text' required!", 42);
2289 get_handle:
2290 my $ldap_handle = Net::LDAP->new( $ldap_uri );
2291 if (not ref $ldap_handle) {
2292 daemon_log("$session_id ERROR: Connection to LDAP URI '$ldap_uri' failed! Retrying!", 1);
2293 usleep(100000);
2294 goto get_handle;
2295 } else {
2296 daemon_log("$session_id DEBUG: Connection to LDAP URI '$ldap_uri' established.", 42);
2297 }
2299 $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);
2300 return $ldap_handle;
2301 }
2304 sub release_ldap_handle {
2305 my ($ldap_handle, $session_id) = @_ ;
2306 if (not defined $session_id ) { $session_id = 0 };
2308 if(ref $ldap_handle) {
2309 $ldap_handle->disconnect();
2310 }
2311 &main::daemon_log("$session_id DEBUG: Released a ldap handle!", 42);
2312 return;
2313 }
2316 sub change_fai_state {
2317 my ($st, $targets, $session_id) = @_;
2318 $session_id = 0 if not defined $session_id;
2319 # Set FAI state to localboot
2320 my %mapActions= (
2321 reboot => '',
2322 update => 'softupdate',
2323 localboot => 'localboot',
2324 reinstall => 'install',
2325 rescan => '',
2326 wake => '',
2327 memcheck => 'memcheck',
2328 sysinfo => 'sysinfo',
2329 install => 'install',
2330 );
2332 # Return if this is unknown
2333 if (!exists $mapActions{ $st }){
2334 daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1);
2335 return;
2336 }
2338 my $state= $mapActions{ $st };
2340 # Build search filter for hosts
2341 my $search= "(&(objectClass=GOhard)";
2342 foreach (@{$targets}){
2343 $search.= "(macAddress=$_)";
2344 }
2345 $search.= ")";
2347 # If there's any host inside of the search string, procress them
2348 if (!($search =~ /macAddress/)){
2349 daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);
2350 return;
2351 }
2353 my $ldap_handle = &get_ldap_handle($session_id);
2354 # Perform search for Unit Tag
2355 my $mesg = $ldap_handle->search(
2356 base => $ldap_base,
2357 scope => 'sub',
2358 attrs => ['dn', 'FAIstate', 'objectClass'],
2359 filter => "$search"
2360 );
2362 if ($mesg->count) {
2363 my @entries = $mesg->entries;
2364 if (0 == @entries) {
2365 daemon_log("$session_id ERROR: ldap search failed: ldap_base=$ldap_base, filter=$search", 1);
2366 }
2368 foreach my $entry (@entries) {
2369 # Only modify entry if it is not set to '$state'
2370 if ($entry->get_value("FAIstate") ne "$state"){
2371 daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
2372 my $result;
2373 my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
2374 if (exists $tmp{'FAIobject'}){
2375 if ($state eq ''){
2376 $result= $ldap_handle->modify($entry->dn, changes => [ delete => [ FAIstate => [] ] ]);
2377 } else {
2378 $result= $ldap_handle->modify($entry->dn, changes => [ replace => [ FAIstate => $state ] ]);
2379 }
2380 } elsif ($state ne ''){
2381 $result= $ldap_handle->modify($entry->dn, changes => [ add => [ objectClass => 'FAIobject' ], add => [ FAIstate => $state ] ]);
2382 }
2384 # Errors?
2385 if ($result->code){
2386 daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2387 }
2388 } else {
2389 daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 42);
2390 }
2391 }
2392 } else {
2393 daemon_log("$session_id ERROR: LDAP search failed: ldap_base=$ldap_base, filter=$search", 1);
2394 }
2395 &release_ldap_handle($ldap_handle, $session_id);
2397 return;
2398 }
2401 sub change_goto_state {
2402 my ($st, $targets, $session_id) = @_;
2403 $session_id = 0 if not defined $session_id;
2405 # Switch on or off?
2406 my $state= $st eq 'active' ? 'active': 'locked';
2408 my $ldap_handle = &get_ldap_handle($session_id);
2409 if( defined($ldap_handle) ) {
2411 # Build search filter for hosts
2412 my $search= "(&(objectClass=GOhard)";
2413 foreach (@{$targets}){
2414 $search.= "(macAddress=$_)";
2415 }
2416 $search.= ")";
2418 # If there's any host inside of the search string, procress them
2419 if (!($search =~ /macAddress/)){
2420 &release_ldap_handle($ldap_handle);
2421 return;
2422 }
2424 # Perform search for Unit Tag
2425 my $mesg = $ldap_handle->search(
2426 base => $ldap_base,
2427 scope => 'sub',
2428 attrs => ['dn', 'gotoMode'],
2429 filter => "$search"
2430 );
2432 if ($mesg->count) {
2433 my @entries = $mesg->entries;
2434 foreach my $entry (@entries) {
2436 # Only modify entry if it is not set to '$state'
2437 if ($entry->get_value("gotoMode") ne $state){
2439 daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
2440 my $result;
2441 $result= $ldap_handle->modify($entry->dn, changes => [replace => [ gotoMode => $state ] ]);
2443 # Errors?
2444 if ($result->code){
2445 &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2446 }
2448 }
2449 }
2450 } else {
2451 daemon_log("$session_id ERROR: LDAP search failed in function change_goto_state: ldap_base=$ldap_base, filter=$search", 1);
2452 }
2454 }
2455 &release_ldap_handle($ldap_handle, $session_id);
2456 return;
2457 }
2460 sub run_recreate_packages_db {
2461 my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2462 my $session_id = $session->ID;
2463 &main::daemon_log("$session_id INFO: Recreating FAI Packages DB ('$fai_release_tn', '$fai_server_tn', '$packages_list_tn')", 5);
2464 $kernel->yield('create_fai_release_db', $fai_release_tn);
2465 $kernel->yield('create_fai_server_db', $fai_server_tn);
2466 return;
2467 }
2470 sub run_create_fai_server_db {
2471 my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2472 my $session_id = $session->ID;
2473 my $task = POE::Wheel::Run->new(
2474 Program => sub { &create_fai_server_db($table_name,$kernel, undef, $session_id) },
2475 StdoutEvent => "session_run_result",
2476 StderrEvent => "session_run_debug",
2477 CloseEvent => "session_run_done",
2478 );
2480 $heap->{task}->{ $task->ID } = $task;
2481 return;
2482 }
2485 sub create_fai_server_db {
2486 my ($table_name, $kernel, $dont_create_packages_list, $session_id) = @_;
2487 my $result;
2489 if (not defined $session_id) { $session_id = 0; }
2490 my $ldap_handle = &get_ldap_handle($session_id);
2491 if(defined($ldap_handle)) {
2492 daemon_log("$session_id INFO: create_fai_server_db: start", 5);
2493 my $mesg= $ldap_handle->search(
2494 base => $ldap_base,
2495 scope => 'sub',
2496 attrs => ['FAIrepository', 'gosaUnitTag'],
2497 filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
2498 );
2499 if($mesg->{'resultCode'} == 0 &&
2500 $mesg->count != 0) {
2501 foreach my $entry (@{$mesg->{entries}}) {
2502 if($entry->exists('FAIrepository')) {
2503 # Add an entry for each Repository configured for server
2504 foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
2505 my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
2506 my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
2507 $result= $fai_server_db->add_dbentry( {
2508 table => $table_name,
2509 primkey => ['server', 'fai_release', 'tag'],
2510 server => $tmp_url,
2511 fai_release => $tmp_release,
2512 sections => $tmp_sections,
2513 tag => (length($tmp_tag)>0)?$tmp_tag:"",
2514 } );
2515 }
2516 }
2517 }
2518 }
2519 daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
2520 &release_ldap_handle($ldap_handle);
2522 # TODO: Find a way to post the 'create_packages_list_db' event
2523 if(not defined($dont_create_packages_list)) {
2524 &create_packages_list_db(undef, $session_id);
2525 }
2526 }
2528 return $result;
2529 }
2532 sub run_create_fai_release_db {
2533 my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2534 my $session_id = $session->ID;
2535 my $task = POE::Wheel::Run->new(
2536 Program => sub { &create_fai_release_db($table_name, $session_id) },
2537 StdoutEvent => "session_run_result",
2538 StderrEvent => "session_run_debug",
2539 CloseEvent => "session_run_done",
2540 );
2542 $heap->{task}->{ $task->ID } = $task;
2543 return;
2544 }
2547 sub create_fai_release_db {
2548 my ($table_name, $session_id) = @_;
2549 my $result;
2551 # used for logging
2552 if (not defined $session_id) { $session_id = 0; }
2554 my $ldap_handle = &get_ldap_handle($session_id);
2555 if(defined($ldap_handle)) {
2556 daemon_log("$session_id INFO: create_fai_release_db: start",5);
2557 my $mesg= $ldap_handle->search(
2558 base => $ldap_base,
2559 scope => 'sub',
2560 attrs => [],
2561 filter => "(&(objectClass=organizationalUnit)(ou=fai))",
2562 );
2563 if(($mesg->code == 0) && ($mesg->count != 0))
2564 {
2565 daemon_log("$session_id DEBUG: create_fai_release_db: count " . $mesg->count,138);
2567 # Walk through all possible FAI container ou's
2568 my @sql_list;
2569 my $timestamp= &get_time();
2570 foreach my $ou (@{$mesg->{entries}}) {
2571 my $tmp_classes= resolve_fai_classes($ou->dn, $ldap_handle, $session_id);
2572 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
2573 my @tmp_array=get_fai_release_entries($tmp_classes);
2574 if(@tmp_array) {
2575 foreach my $entry (@tmp_array) {
2576 if(defined($entry) && ref($entry) eq 'HASH') {
2577 my $sql=
2578 "INSERT INTO $table_name "
2579 ."(timestamp, fai_release, class, type, state) VALUES ("
2580 .$timestamp.","
2581 ."'".$entry->{'release'}."',"
2582 ."'".$entry->{'class'}."',"
2583 ."'".$entry->{'type'}."',"
2584 ."'".$entry->{'state'}."')";
2585 push @sql_list, $sql;
2586 }
2587 }
2588 }
2589 }
2590 }
2592 daemon_log("$session_id DEBUG: create_fai_release_db: Inserting ".scalar @sql_list." entries to DB",138);
2593 &release_ldap_handle($ldap_handle);
2594 if(@sql_list) {
2595 unshift @sql_list, "VACUUM";
2596 unshift @sql_list, "DELETE FROM $table_name";
2597 $fai_release_db->exec_statementlist(\@sql_list);
2598 }
2599 daemon_log("$session_id DEBUG: create_fai_release_db: Done with inserting",138);
2600 } else {
2601 daemon_log("$session_id INFO: create_fai_release_db: error: " . $mesg->code, 5);
2602 }
2603 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
2604 }
2605 return $result;
2606 }
2608 sub get_fai_types {
2609 my $tmp_classes = shift || return undef;
2610 my @result;
2612 foreach my $type(keys %{$tmp_classes}) {
2613 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
2614 my $entry = {
2615 type => $type,
2616 state => $tmp_classes->{$type}[0],
2617 };
2618 push @result, $entry;
2619 }
2620 }
2622 return @result;
2623 }
2625 sub get_fai_state {
2626 my $result = "";
2627 my $tmp_classes = shift || return $result;
2629 foreach my $type(keys %{$tmp_classes}) {
2630 if(defined($tmp_classes->{$type}[0])) {
2631 $result = $tmp_classes->{$type}[0];
2633 # State is equal for all types in class
2634 last;
2635 }
2636 }
2638 return $result;
2639 }
2641 sub resolve_fai_classes {
2642 my ($fai_base, $ldap_handle, $session_id) = @_;
2643 if (not defined $session_id) { $session_id = 0; }
2644 my $result;
2645 my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
2646 my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
2647 my $fai_classes;
2649 daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base", 138);
2650 my $mesg= $ldap_handle->search(
2651 base => $fai_base,
2652 scope => 'sub',
2653 attrs => ['cn','objectClass','FAIstate'],
2654 filter => $fai_filter,
2655 );
2656 daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries", 138);
2658 if($mesg->{'resultCode'} == 0 &&
2659 $mesg->count != 0) {
2660 foreach my $entry (@{$mesg->{entries}}) {
2661 if($entry->exists('cn')) {
2662 my $tmp_dn= $entry->dn();
2663 $tmp_dn= substr( $tmp_dn, 0, length($tmp_dn)
2664 - length($fai_base) - 1 );
2666 # Skip classname and ou dn parts for class
2667 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?)$/;
2669 # Skip classes without releases
2670 if((!defined($tmp_release)) || length($tmp_release)==0) {
2671 next;
2672 }
2674 my $tmp_cn= $entry->get_value('cn');
2675 my $tmp_state= $entry->get_value('FAIstate');
2677 my $tmp_type;
2678 # Get FAI type
2679 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
2680 if(grep $_ eq $oclass, @possible_fai_classes) {
2681 $tmp_type= $oclass;
2682 last;
2683 }
2684 }
2686 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2687 # A Subrelease
2688 my @sub_releases = split(/,/, $tmp_release);
2690 # Walk through subreleases and build hash tree
2691 my $hash;
2692 while(my $tmp_sub_release = pop @sub_releases) {
2693 $hash .= "\{'$tmp_sub_release'\}->";
2694 }
2695 eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
2696 } else {
2697 # A branch, no subrelease
2698 push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
2699 }
2700 } elsif (!$entry->exists('cn')) {
2701 my $tmp_dn= $entry->dn();
2702 $tmp_dn= substr( $tmp_dn, 0, length($tmp_dn)
2703 - length($fai_base) - 1 );
2704 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?)$/;
2706 # Skip classes without releases
2707 if((!defined($tmp_release)) || length($tmp_release)==0) {
2708 next;
2709 }
2711 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2712 # A Subrelease
2713 my @sub_releases= split(/,/, $tmp_release);
2715 # Walk through subreleases and build hash tree
2716 my $hash;
2717 while(my $tmp_sub_release = pop @sub_releases) {
2718 $hash .= "\{'$tmp_sub_release'\}->";
2719 }
2720 # Remove the last two characters
2721 chop($hash);
2722 chop($hash);
2724 eval('$fai_classes->'.$hash.'= {}');
2725 } else {
2726 # A branch, no subrelease
2727 if(!exists($fai_classes->{$tmp_release})) {
2728 $fai_classes->{$tmp_release} = {};
2729 }
2730 }
2731 }
2732 }
2734 # The hash is complete, now we can honor the copy-on-write based missing entries
2735 foreach my $release (keys %$fai_classes) {
2736 $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
2737 }
2738 }
2739 return $result;
2740 }
2742 sub apply_fai_inheritance {
2743 my $fai_classes = shift || return {};
2744 my $tmp_classes;
2746 # Get the classes from the branch
2747 foreach my $class (keys %{$fai_classes}) {
2748 # Skip subreleases
2749 if($class =~ /^ou=.*$/) {
2750 next;
2751 } else {
2752 $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
2753 }
2754 }
2756 # Apply to each subrelease
2757 foreach my $subrelease (keys %{$fai_classes}) {
2758 if($subrelease =~ /ou=/) {
2759 foreach my $tmp_class (keys %{$tmp_classes}) {
2760 if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
2761 $fai_classes->{$subrelease}->{$tmp_class} =
2762 deep_copy($tmp_classes->{$tmp_class});
2763 } else {
2764 foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
2765 if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
2766 $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
2767 deep_copy($tmp_classes->{$tmp_class}->{$type});
2768 }
2769 }
2770 }
2771 }
2772 }
2773 }
2775 # Find subreleases in deeper levels
2776 foreach my $subrelease (keys %{$fai_classes}) {
2777 if($subrelease =~ /ou=/) {
2778 foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
2779 if($subsubrelease =~ /ou=/) {
2780 apply_fai_inheritance($fai_classes->{$subrelease});
2781 }
2782 }
2783 }
2784 }
2786 return $fai_classes;
2787 }
2789 sub get_fai_release_entries {
2790 my $tmp_classes = shift || return;
2791 my $parent = shift || "";
2792 my @result = shift || ();
2794 foreach my $entry (keys %{$tmp_classes}) {
2795 if(defined($entry)) {
2796 if($entry =~ /^ou=.*$/) {
2797 my $release_name = $entry;
2798 $release_name =~ s/ou=//g;
2799 if(length($parent)>0) {
2800 $release_name = $parent."/".$release_name;
2801 }
2802 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2803 foreach my $bufentry(@bufentries) {
2804 push @result, $bufentry;
2805 }
2806 } else {
2807 my @types = get_fai_types($tmp_classes->{$entry});
2808 foreach my $type (@types) {
2809 push @result,
2810 {
2811 'class' => $entry,
2812 'type' => $type->{'type'},
2813 'release' => $parent,
2814 'state' => $type->{'state'},
2815 };
2816 }
2817 }
2818 }
2819 }
2821 return @result;
2822 }
2824 sub deep_copy {
2825 my $this = shift;
2826 if (not ref $this) {
2827 $this;
2828 } elsif (ref $this eq "ARRAY") {
2829 [map deep_copy($_), @$this];
2830 } elsif (ref $this eq "HASH") {
2831 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2832 } else { die "what type is $_?" }
2833 }
2836 sub session_run_result {
2837 my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];
2838 $kernel->sig(CHLD => "child_reap");
2839 }
2841 sub session_run_debug {
2842 my $result = $_[ARG0];
2843 print STDERR "$result\n";
2844 }
2846 sub session_run_done {
2847 my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2848 delete $heap->{task}->{$task_id};
2849 if (exists $heap->{ldap_handle}->{$task_id}) {
2850 &release_ldap_handle($heap->{ldap_handle}->{$task_id});
2851 }
2852 delete $heap->{ldap_handle}->{$task_id};
2853 }
2856 sub create_sources_list {
2857 my $session_id = shift || 0;
2858 my $result="/tmp/gosa_si_tmp_sources_list";
2860 # Remove old file
2861 if(stat($result)) {
2862 unlink($result);
2863 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7);
2864 }
2866 my $fh;
2867 open($fh, ">$result");
2868 if (not defined $fh) {
2869 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7);
2870 return undef;
2871 }
2872 if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2873 my $ldap_handle = &get_ldap_handle($session_id);
2874 my $mesg=$ldap_handle->search(
2875 base => $main::ldap_server_dn,
2876 scope => 'base',
2877 attrs => 'FAIrepository',
2878 filter => 'objectClass=FAIrepositoryServer'
2879 );
2880 if($mesg->count) {
2881 foreach my $entry(@{$mesg->{'entries'}}) {
2882 foreach my $value(@{$entry->get_value('FAIrepository', asref => 1)}) {
2883 my ($server, $tag, $release, $sections)= split /\|/, $value;
2884 my $line = "deb $server $release";
2885 $sections =~ s/,/ /g;
2886 $line.= " $sections";
2887 print $fh $line."\n";
2888 }
2889 }
2890 }
2891 &release_ldap_handle($ldap_handle);
2892 } else {
2893 if (defined $main::ldap_server_dn){
2894 &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1);
2895 } else {
2896 &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2897 }
2898 }
2899 close($fh);
2901 return $result;
2902 }
2905 sub run_create_packages_list_db {
2906 my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2907 my $session_id = $session->ID;
2908 my $task = POE::Wheel::Run->new(
2909 Priority => +20,
2910 Program => sub {&create_packages_list_db(undef, $session_id)},
2911 StdoutEvent => "session_run_result",
2912 StderrEvent => "session_run_debug",
2913 CloseEvent => "session_run_done",
2914 );
2915 $heap->{task}->{ $task->ID } = $task;
2916 }
2919 sub create_packages_list_db {
2920 my ($sources_file, $session_id) = @_;
2922 # it should not be possible to trigger a recreation of packages_list_db
2923 # while packages_list_db is under construction, so set flag packages_list_under_construction
2924 # which is tested befor recreation can be started
2925 if (-r $packages_list_under_construction) {
2926 daemon_log("$session_id WARNING: packages_list_db is right now under construction, please wait until this process is finished", 3);
2927 return;
2928 } else {
2929 daemon_log("$session_id INFO: create_packages_list_db: start", 5);
2930 # set packages_list_under_construction to true
2931 system("touch $packages_list_under_construction");
2932 @packages_list_statements=();
2933 }
2935 if (not defined $session_id) { $session_id = 0; }
2937 if (not defined $sources_file) {
2938 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5);
2939 $sources_file = &create_sources_list($session_id);
2940 }
2942 if (not defined $sources_file) {
2943 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1);
2944 unlink($packages_list_under_construction);
2945 return;
2946 }
2948 my $line;
2950 open(CONFIG, "<$sources_file") or do {
2951 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2952 unlink($packages_list_under_construction);
2953 return;
2954 };
2956 # Read lines
2957 while ($line = <CONFIG>){
2958 # Unify
2959 chop($line);
2960 $line =~ s/^\s+//;
2961 $line =~ s/^\s+/ /;
2963 # Strip comments
2964 $line =~ s/#.*$//g;
2966 # Skip empty lines
2967 if ($line =~ /^\s*$/){
2968 next;
2969 }
2971 # Interpret deb line
2972 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2973 my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2974 my $section;
2975 foreach $section (split(' ', $sections)){
2976 &parse_package_info( $baseurl, $dist, $section, $session_id );
2977 }
2978 }
2979 }
2981 close (CONFIG);
2983 if(keys(%repo_dirs)) {
2984 find(\&cleanup_and_extract, keys( %repo_dirs ));
2985 &main::strip_packages_list_statements();
2986 $packages_list_db->exec_statementlist(\@packages_list_statements);
2987 }
2988 unlink($packages_list_under_construction);
2989 daemon_log("$session_id INFO: create_packages_list_db: finished", 5);
2990 return;
2991 }
2993 # This function should do some intensive task to minimize the db-traffic
2994 sub strip_packages_list_statements {
2995 my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2996 my @new_statement_list=();
2997 my $hash;
2998 my $insert_hash;
2999 my $update_hash;
3000 my $delete_hash;
3001 my $known_packages_hash;
3002 my $local_timestamp=get_time();
3004 foreach my $existing_entry (@existing_entries) {
3005 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
3006 }
3008 foreach my $statement (@packages_list_statements) {
3009 if($statement =~ /^INSERT/i) {
3010 # Assign the values from the insert statement
3011 my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~
3012 /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
3013 if(exists($hash->{$distribution}->{$package}->{$version})) {
3014 # If section or description has changed, update the DB
3015 if(
3016 (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or
3017 (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
3018 ) {
3019 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
3020 } else {
3021 # package is already present in database. cache this knowledge for later use
3022 @{$known_packages_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
3023 }
3024 } else {
3025 # Insert a non-existing entry to db
3026 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
3027 }
3028 } elsif ($statement =~ /^UPDATE/i) {
3029 my ($template,$package,$version) = ($1,$2,$3) if $statement =~
3030 /^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;
3031 foreach my $distribution (keys %{$hash}) {
3032 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
3033 # update the insertion hash to execute only one query per package (insert instead insert+update)
3034 @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
3035 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
3036 if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
3037 my $section;
3038 my $description;
3039 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
3040 length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
3041 $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
3042 }
3043 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
3044 $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
3045 }
3046 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
3047 }
3048 }
3049 }
3050 }
3051 }
3053 # Check for orphaned entries
3054 foreach my $existing_entry (@existing_entries) {
3055 my $distribution= @{$existing_entry}[0];
3056 my $package= @{$existing_entry}[1];
3057 my $version= @{$existing_entry}[2];
3058 my $section= @{$existing_entry}[3];
3060 if(
3061 exists($insert_hash->{$distribution}->{$package}->{$version}) ||
3062 exists($update_hash->{$distribution}->{$package}->{$version}) ||
3063 exists($known_packages_hash->{$distribution}->{$package}->{$version})
3064 ) {
3065 next;
3066 } else {
3067 # Insert entry to delete hash
3068 @{$delete_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section);
3069 }
3070 }
3072 # unroll the insert hash
3073 foreach my $distribution (keys %{$insert_hash}) {
3074 foreach my $package (keys %{$insert_hash->{$distribution}}) {
3075 foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
3076 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
3077 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
3078 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
3079 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
3080 ."'$local_timestamp')";
3081 }
3082 }
3083 }
3085 # unroll the update hash
3086 foreach my $distribution (keys %{$update_hash}) {
3087 foreach my $package (keys %{$update_hash->{$distribution}}) {
3088 foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
3089 my $set = "";
3090 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
3091 $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
3092 }
3093 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
3094 $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
3095 }
3096 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
3097 $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
3098 }
3099 if(defined($set) and length($set) > 0) {
3100 $set .= "timestamp = '$local_timestamp'";
3101 } else {
3102 next;
3103 }
3104 push @new_statement_list,
3105 "UPDATE $main::packages_list_tn SET $set WHERE"
3106 ." distribution = '$distribution'"
3107 ." AND package = '$package'"
3108 ." AND version = '$version'";
3109 }
3110 }
3111 }
3113 # unroll the delete hash
3114 foreach my $distribution (keys %{$delete_hash}) {
3115 foreach my $package (keys %{$delete_hash->{$distribution}}) {
3116 foreach my $version (keys %{$delete_hash->{$distribution}->{$package}}) {
3117 my $section = @{$delete_hash->{$distribution}->{$package}->{$version}}[3];
3118 push @new_statement_list, "DELETE FROM $main::packages_list_tn WHERE distribution='$distribution' AND package='$package' AND version='$version' AND section='$section'";
3119 }
3120 }
3121 }
3123 unshift(@new_statement_list, "VACUUM");
3125 @packages_list_statements = @new_statement_list;
3126 }
3129 sub parse_package_info {
3130 my ($baseurl, $dist, $section, $session_id)= @_;
3131 my ($package);
3132 if (not defined $session_id) { $session_id = 0; }
3133 my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
3134 $repo_dirs{ "${repo_path}/pool" } = 1;
3136 foreach $package ("Packages.gz"){
3137 daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 266);
3138 get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
3139 parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
3140 }
3142 }
3145 sub get_package {
3146 my ($url, $dest, $session_id)= @_;
3147 if (not defined $session_id) { $session_id = 0; }
3149 my $tpath = dirname($dest);
3150 -d "$tpath" || mkpath "$tpath";
3152 # This is ugly, but I've no time to take a look at "how it works in perl"
3153 if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
3154 system("gunzip -cd '$dest' > '$dest.in'");
3155 daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 266);
3156 unlink($dest);
3157 daemon_log("$session_id DEBUG: delete file '$dest'", 266);
3158 } else {
3159 daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' into '$dest' failed!", 1);
3160 }
3161 return 0;
3162 }
3165 sub parse_package {
3166 my ($path, $dist, $srv_path, $session_id)= @_;
3167 if (not defined $session_id) { $session_id = 0;}
3168 my ($package, $version, $section, $description);
3169 my $PACKAGES;
3170 my $timestamp = &get_time();
3172 if(not stat("$path.in")) {
3173 daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
3174 return;
3175 }
3177 open($PACKAGES, "<$path.in");
3178 if(not defined($PACKAGES)) {
3179 daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1);
3180 return;
3181 }
3183 # Read lines
3184 while (<$PACKAGES>){
3185 my $line = $_;
3186 # Unify
3187 chop($line);
3189 # Use empty lines as a trigger
3190 if ($line =~ /^\s*$/){
3191 my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
3192 push(@packages_list_statements, $sql);
3193 $package = "none";
3194 $version = "none";
3195 $section = "none";
3196 $description = "none";
3197 next;
3198 }
3200 # Trigger for package name
3201 if ($line =~ /^Package:\s/){
3202 ($package)= ($line =~ /^Package: (.*)$/);
3203 next;
3204 }
3206 # Trigger for version
3207 if ($line =~ /^Version:\s/){
3208 ($version)= ($line =~ /^Version: (.*)$/);
3209 next;
3210 }
3212 # Trigger for description
3213 if ($line =~ /^Description:\s/){
3214 ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
3215 next;
3216 }
3218 # Trigger for section
3219 if ($line =~ /^Section:\s/){
3220 ($section)= ($line =~ /^Section: (.*)$/);
3221 next;
3222 }
3224 # Trigger for filename
3225 if ($line =~ /^Filename:\s/){
3226 my ($filename) = ($line =~ /^Filename: (.*)$/);
3227 store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
3228 next;
3229 }
3230 }
3232 close( $PACKAGES );
3233 unlink( "$path.in" );
3234 }
3237 sub store_fileinfo {
3238 my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
3240 my %fileinfo = (
3241 'package' => $package,
3242 'dist' => $dist,
3243 'version' => $vers,
3244 );
3246 $repo_files{ "${srvdir}/$file" } = \%fileinfo;
3247 }
3250 sub cleanup_and_extract {
3251 my $fileinfo = $repo_files{ $File::Find::name };
3253 if( defined $fileinfo ) {
3254 my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
3255 my $sql;
3256 my $package = $fileinfo->{ 'package' };
3257 my $newver = $fileinfo->{ 'version' };
3259 mkpath($dir);
3260 system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
3262 if( -f "$dir/DEBIAN/templates" ) {
3264 daemon_log("0 DEBUG: Found debconf templates in '$package' - $newver", 266);
3266 my $tmpl= ""; {
3267 local $/=undef;
3268 open FILE, "$dir/DEBIAN/templates";
3269 $tmpl = &encode_base64(<FILE>);
3270 close FILE;
3271 }
3272 rmtree("$dir/DEBIAN/templates");
3274 $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
3275 push @packages_list_statements, $sql;
3276 }
3277 }
3279 return;
3280 }
3283 sub prepare_server_registration
3284 {
3285 # Add foreign server from cfg file
3286 my @foreign_server_list;
3287 if ($foreign_server_string ne "") {
3288 my @cfg_foreign_server_list = split(",", $foreign_server_string);
3289 foreach my $foreign_server (@cfg_foreign_server_list) {
3290 push(@foreign_server_list, $foreign_server);
3291 }
3293 daemon_log("0 INFO: found foreign server in config file: ".join(", ", @foreign_server_list), 5);
3294 }
3296 # Perform a DNS lookup for server registration if flag is true
3297 if ($dns_lookup eq "true") {
3298 # Add foreign server from dns
3299 my @tmp_servers;
3300 if (not $server_domain) {
3301 # Try our DNS Searchlist
3302 for my $domain(get_dns_domains()) {
3303 chomp($domain);
3304 my ($tmp_domains, $error_string) = &get_server_addresses($domain);
3305 if(@$tmp_domains) {
3306 for my $tmp_server(@$tmp_domains) {
3307 push @tmp_servers, $tmp_server;
3308 }
3309 }
3310 }
3311 if(@tmp_servers && length(@tmp_servers)==0) {
3312 daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3313 }
3314 } else {
3315 @tmp_servers = &get_server_addresses($server_domain);
3316 if( 0 == @tmp_servers ) {
3317 daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3318 }
3319 }
3321 daemon_log("0 INFO: found foreign server via DNS ".join(", ", @tmp_servers), 5);
3323 foreach my $server (@tmp_servers) {
3324 unshift(@foreign_server_list, $server);
3325 }
3326 } else {
3327 daemon_log("0 INFO: DNS lookup for server registration is disabled", 5);
3328 }
3330 # eliminate duplicate entries
3331 @foreign_server_list = &del_doubles(@foreign_server_list);
3332 my $all_foreign_server = join(", ", @foreign_server_list);
3333 daemon_log("0 INFO: found foreign server in config file and DNS: '$all_foreign_server'", 5);
3335 # add all found foreign servers to known_server
3336 my $cur_timestamp = &get_time();
3337 foreach my $foreign_server (@foreign_server_list) {
3339 # do not add myself to known_server_db
3340 if (&is_local($foreign_server)) { next; }
3341 ######################################
3343 my $res = $known_server_db->add_dbentry( {table=>$known_server_tn,
3344 primkey=>['hostname'],
3345 hostname=>$foreign_server,
3346 macaddress=>"",
3347 status=>'not_yet_registered',
3348 hostkey=>"none",
3349 loaded_modules => "none",
3350 timestamp=>$cur_timestamp,
3351 update_time=>'19700101000000',
3352 } );
3353 }
3354 }
3356 sub register_at_foreign_servers {
3357 my ($kernel) = $_[KERNEL];
3359 # Update status and update-time of all si-server with expired update_time and
3360 # block them for race conditional registration processes of other si-servers.
3361 my $act_time = &get_time();
3362 my $block_statement = "UPDATE $known_server_tn SET status='new_server',update_time='19700101000000' WHERE (CAST(update_time AS UNSIGNED))<$act_time ";
3363 my $block_res = $known_server_db->exec_statement($block_statement);
3365 # Fetch all si-server from db where update_time is younger than act_time
3366 my $fetch_statement = "SELECT * FROM $known_server_tn WHERE update_time='19700101000000'";
3367 my $fetch_res = $known_server_db->exec_statement($fetch_statement);
3369 # Detect already connected clients. Will be added to registration msg later.
3370 my $client_sql = "SELECT * FROM $known_clients_tn";
3371 my $client_res = $known_clients_db->exec_statement($client_sql);
3373 # Send registration messag to all fetched si-server
3374 foreach my $hit (@$fetch_res) {
3375 my $hostname = @$hit[0];
3376 my $hostkey = &create_passwd;
3378 # Add already connected clients to registration message
3379 my $myhash = &create_xml_hash('new_server', $server_address, $hostname);
3380 &add_content2xml_hash($myhash, 'key', $hostkey);
3381 map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res);
3383 # Add locally loaded gosa-si modules to registration message
3384 my $loaded_modules = {};
3385 while (my ($package, $pck_info) = each %$known_modules) {
3386 next if ((!defined(@$pck_info[2])) || (!(ref (@$pck_info[2]) eq 'HASH')));
3387 foreach my $act_module (keys(%{@$pck_info[2]})) {
3388 $loaded_modules->{$act_module} = "";
3389 }
3390 }
3391 map(&add_content2xml_hash($myhash, "loaded_modules", $_), keys(%$loaded_modules));
3393 # Add macaddress to registration message
3394 my ($host_ip, $host_port) = split(/:/, $hostname);
3395 my $local_ip = &get_local_ip_for_remote_ip($host_ip);
3396 my $network_interface= &get_interface_for_ip($local_ip);
3397 my $host_mac = &get_mac_for_interface($network_interface);
3398 &add_content2xml_hash($myhash, 'macaddress', $host_mac);
3400 # Build registration message and send it
3401 my $foreign_server_msg = &create_xml_string($myhash);
3402 my $error = &send_msg_to_target($foreign_server_msg, $hostname, $ServerPackages_key, "new_server", 0);
3403 }
3406 # After n sec perform a check of all server registration processes
3407 $kernel->delay_set("control_server_registration", 2);
3409 return;
3410 }
3413 sub control_server_registration {
3414 my ($kernel) = $_[KERNEL];
3416 # Check if all registration processes succeed or not
3417 my $select_statement = "SELECT * FROM $known_server_tn WHERE status='new_server'";
3418 my $select_res = $known_server_db->exec_statement($select_statement);
3420 # If at least one registration process failed, maybe in case of a race condition
3421 # with a foreign registration process
3422 if (@$select_res > 0)
3423 {
3424 # Release block statement 'new_server' to make the server accessible
3425 # for foreign registration processes
3426 my $update_statement = "UPDATE $known_server_tn SET status='waiting' WHERE status='new_server'";
3427 my $update_res = $known_server_db->exec_statement($update_statement);
3429 # Set a random delay to avoid the registration race condition
3430 my $new_foreign_servers_register_delay = int(rand(4))+1;
3431 $kernel->delay_set("register_at_foreign_servers", $new_foreign_servers_register_delay);
3432 }
3433 # If all registration processes succeed
3434 else
3435 {
3436 $kernel->delay_set("register_at_foreign_servers", $foreign_servers_register_delay);
3437 }
3439 return;
3440 }
3443 #==== MAIN = main ==============================================================
3444 # parse commandline options
3445 Getopt::Long::Configure( "bundling" );
3446 GetOptions("h|help" => \&usage,
3447 "c|config=s" => \$cfg_file,
3448 "f|foreground" => \$foreground,
3449 "v|verbose+" => \$verbose,
3450 "no-arp+" => \$no_arp,
3451 "d=s" => \$debug_parts,
3452 ) or &usage("", 1);
3454 # read and set config parameters
3455 &check_cmdline_param ;
3456 &read_configfile($cfg_file, %cfg_defaults);
3457 &check_pid;
3459 $SIG{CHLD} = 'IGNORE';
3461 # forward error messages to logfile
3462 if( ! $foreground ) {
3463 open( STDIN, '+>/dev/null' );
3464 open( STDOUT, '+>&STDIN' );
3465 open( STDERR, '+>&STDIN' );
3466 }
3468 # Just fork, if we are not in foreground mode
3469 if( ! $foreground ) {
3470 chdir '/' or die "Can't chdir to /: $!";
3471 $pid = fork;
3472 setsid or die "Can't start a new session: $!";
3473 umask 0;
3474 } else {
3475 $pid = $$;
3476 }
3478 # Do something useful - put our PID into the pid_file
3479 if( 0 != $pid ) {
3480 open( LOCK_FILE, ">$pid_file" );
3481 print LOCK_FILE "$pid\n";
3482 close( LOCK_FILE );
3483 if( !$foreground ) {
3484 exit( 0 )
3485 };
3486 }
3488 # parse head url and revision from svn
3489 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
3490 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
3491 $server_headURL = defined $1 ? $1 : 'unknown' ;
3492 $server_revision = defined $2 ? $2 : 'unknown' ;
3493 if ($server_headURL =~ /\/tag\// ||
3494 $server_headURL =~ /\/branches\// ) {
3495 $server_status = "stable";
3496 } else {
3497 $server_status = "developmental" ;
3498 }
3499 # Prepare log file and set permissions
3500 $root_uid = getpwnam('root');
3501 $adm_gid = getgrnam('adm');
3502 open(FH, ">>$log_file");
3503 close FH;
3504 chmod(0440, $log_file);
3505 chown($root_uid, $adm_gid, $log_file);
3506 chown($root_uid, $adm_gid, "/var/lib/gosa-si");
3508 daemon_log(" ", 1);
3509 daemon_log("$0 started!", 1);
3510 daemon_log("status: $server_status", 1);
3511 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1);
3513 # Buildup data bases
3514 {
3515 no strict "refs";
3517 if ($db_module eq "DBmysql") {
3518 # connect to incoming_db
3519 $incoming_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3521 # connect to gosa-si job queue
3522 $job_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3524 # connect to known_clients_db
3525 $known_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3527 # connect to foreign_clients_db
3528 $foreign_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3530 # connect to known_server_db
3531 $known_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3533 # connect to login_usr_db
3534 $login_users_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3536 # connect to fai_server_db
3537 $fai_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3539 # connect to fai_release_db
3540 $fai_release_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3542 # connect to packages_list_db
3543 $packages_list_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3545 # connect to messaging_db
3546 $messaging_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3548 } elsif ($db_module eq "DBsqlite") {
3549 # connect to incoming_db
3550 unlink($incoming_file_name);
3551 $incoming_db = GOSA::DBsqlite->new($incoming_file_name);
3552 chmod(0640, $incoming_file_name);
3553 chown($root_uid, $adm_gid, $incoming_file_name);
3555 # connect to gosa-si job queue
3556 $job_db = GOSA::DBsqlite->new($job_queue_file_name);
3557 chmod(0640, $job_queue_file_name);
3558 chown($root_uid, $adm_gid, $job_queue_file_name);
3560 # connect to known_clients_db
3561 #unlink($known_clients_file_name);
3562 $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
3563 chmod(0640, $known_clients_file_name);
3564 chown($root_uid, $adm_gid, $known_clients_file_name);
3566 # connect to foreign_clients_db
3567 #unlink($foreign_clients_file_name);
3568 $foreign_clients_db = GOSA::DBsqlite->new($foreign_clients_file_name);
3569 chmod(0640, $foreign_clients_file_name);
3570 chown($root_uid, $adm_gid, $foreign_clients_file_name);
3572 # connect to known_server_db
3573 unlink($known_server_file_name); # do not delete, gosa-si-server should be forced to check config file and dns at each start
3574 $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
3575 chmod(0640, $known_server_file_name);
3576 chown($root_uid, $adm_gid, $known_server_file_name);
3578 # connect to login_usr_db
3579 #unlink($login_users_file_name);
3580 $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
3581 chmod(0640, $login_users_file_name);
3582 chown($root_uid, $adm_gid, $login_users_file_name);
3584 # connect to fai_server_db
3585 unlink($fai_server_file_name);
3586 $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
3587 chmod(0640, $fai_server_file_name);
3588 chown($root_uid, $adm_gid, $fai_server_file_name);
3590 # connect to fai_release_db
3591 unlink($fai_release_file_name);
3592 $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
3593 chmod(0640, $fai_release_file_name);
3594 chown($root_uid, $adm_gid, $fai_release_file_name);
3596 # connect to packages_list_db
3597 unlink($packages_list_under_construction);
3598 $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
3599 chmod(0640, $packages_list_file_name);
3600 chown($root_uid, $adm_gid, $packages_list_file_name);
3602 # connect to messaging_db
3603 #unlink($messaging_file_name);
3604 $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
3605 chmod(0640, $messaging_file_name);
3606 chown($root_uid, $adm_gid, $messaging_file_name);
3607 }
3608 }
3610 # Creating tables
3611 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
3612 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
3613 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
3614 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
3615 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
3616 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
3617 $foreign_clients_db->create_table($foreign_clients_tn, \@foreign_clients_col_names);
3618 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
3619 $incoming_db->create_table($incoming_tn, \@incoming_col_names);
3620 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
3622 # create xml object used for en/decrypting
3623 $xml = new XML::Simple();
3625 # Import all modules
3626 &import_modules;
3628 # Check wether all modules are gosa-si valid passwd check
3629 &password_check;
3631 # Check DNS and config file for server registration
3632 if ($serverPackages_enabled eq "true") { &prepare_server_registration; }
3634 # Create functions hash
3635 while (my ($module, @mod_info) = each %$known_modules)
3636 {
3637 while (my ($plugin, $functions) = each %{$mod_info[0][2]})
3638 {
3639 while (my ($function, $nothing) = each %$functions )
3640 {
3641 $known_functions->{$function} = $nothing;
3642 }
3643 }
3644 }
3646 # Prepare for using Opsi
3647 if ($opsi_enabled eq "true") {
3648 use JSON::RPC::Client;
3649 use XML::Quote qw(:all);
3650 $opsi_url= "https://".$opsi_admin.":".$opsi_password."@".$opsi_server.":4447/rpc";
3651 $opsi_client = new JSON::RPC::Client;
3652 }
3655 POE::Component::Server::TCP->new(
3656 Alias => "TCP_SERVER",
3657 Port => $server_port,
3658 ClientInput => sub {
3659 my ($kernel, $input, $heap, $session) = @_[KERNEL, ARG0, HEAP, SESSION];
3660 my $session_id = $session->ID;
3661 if ($input =~ /;([\d\.]+):([\d]+)$/)
3662 {
3663 # Messages from other servers should be blocked if config option is set
3664 if (($2 eq $server_port) && ($serverPackages_enabled eq "false"))
3665 {
3666 return;
3667 }
3668 &daemon_log("$session_id DEBUG: incoming message from '$1:$2'", 11);
3669 }
3670 else
3671 {
3672 my $remote_ip = $heap->{'remote_ip'};
3673 &daemon_log("$session_id DEBUG: incoming message from '$remote_ip'", 11);
3674 }
3675 push(@msgs_to_decrypt, $input);
3676 $kernel->yield("msg_to_decrypt");
3677 },
3678 InlineStates => {
3679 msg_to_decrypt => \&msg_to_decrypt,
3680 next_task => \&next_task,
3681 task_result => \&handle_task_result,
3682 task_done => \&handle_task_done,
3683 task_debug => \&handle_task_debug,
3684 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!" },
3685 }
3686 );
3688 daemon_log("0 INFO: start socket for incoming xml messages at port '$server_port' ", 1);
3690 # create session for repeatedly checking the job queue for jobs
3691 POE::Session->create(
3692 inline_states => {
3693 _start => \&session_start,
3694 register_at_foreign_servers => \®ister_at_foreign_servers,
3695 control_server_registration => \&control_server_registration,
3696 sig_handler => \&sig_handler,
3697 next_task => \&next_task,
3698 task_result => \&handle_task_result,
3699 task_done => \&handle_task_done,
3700 task_debug => \&handle_task_debug,
3701 watch_for_next_tasks => \&watch_for_next_tasks,
3702 watch_for_new_messages => \&watch_for_new_messages,
3703 watch_for_delivery_messages => \&watch_for_delivery_messages,
3704 watch_for_done_messages => \&watch_for_done_messages,
3705 watch_for_new_jobs => \&watch_for_new_jobs,
3706 watch_for_modified_jobs => \&watch_for_modified_jobs,
3707 watch_for_done_jobs => \&watch_for_done_jobs,
3708 watch_for_opsi_jobs => \&watch_for_opsi_jobs,
3709 watch_for_old_known_clients => \&watch_for_old_known_clients,
3710 create_packages_list_db => \&run_create_packages_list_db,
3711 create_fai_server_db => \&run_create_fai_server_db,
3712 create_fai_release_db => \&run_create_fai_release_db,
3713 recreate_packages_db => \&run_recreate_packages_db,
3714 session_run_result => \&session_run_result,
3715 session_run_debug => \&session_run_debug,
3716 session_run_done => \&session_run_done,
3717 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!" },
3718 }
3719 );
3722 POE::Kernel->run();
3723 exit;