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