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