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