1 package server_server_com;
2 use Exporter;
3 @ISA = qw(Exporter);
4 my @events = (
5 'information_sharing',
6 'new_server',
7 'confirm_new_server',
8 'new_foreign_client',
9 'trigger_wake',
10 'foreign_job_updates',
11 'confirm_usr_msg',
12 );
13 @EXPORT = @events;
15 use strict;
16 use warnings;
17 use Data::Dumper;
18 use GOSA::GosaSupportDaemon;
19 use Time::HiRes qw( usleep);
20 use Socket;
23 BEGIN {}
25 END {}
27 ### Start ######################################################################
29 sub get_events {
30 return \@events;
31 }
34 sub information_sharing {
35 my ($msg, $msg_hash, $session_id) = @_ ;
36 my $header = @{$msg_hash->{'header'}}[0];
37 my $source = @{$msg_hash->{'source'}}[0];
38 my $target = @{$msg_hash->{'target'}}[0];
40 # Handling of msg tag 'new_user'
41 if (exists $msg_hash->{'new_user'}) {
42 my $new_user_list = $msg_hash->{'new_user'};
44 # Sanity check of new_user_list
45 if (ref($new_user_list) eq 'HASH') {
46 &main::daemon_log("$session_id ERROR: 'new_user'-tag in incoming msg has no content!", 1);
48 } else {
49 my @user_list;
50 # Add each user to login_users_db
51 foreach my $new_user_info (@$new_user_list) {
52 my ($client, $user) = split(/;/, $new_user_info);
53 my %add_hash = ( table=>$main::login_users_tn,
54 primkey=> ['client', 'user'],
55 client=>$client,
56 user=>$user,
57 timestamp=>&get_time,
58 regserver=>$source,
59 );
60 my ($res, $error_str) = $main::login_users_db->add_dbentry( \%add_hash );
61 if ($res != 0)
62 {
63 &main::daemon_log("$session_id ERROR: cannot add entry to known_clients: $error_str", 1);
64 }
65 else
66 {
67 push(@user_list, "'$user' at '$client'");
68 }
69 }
70 &main::daemon_log("$session_id INFO: server '$source' reports the following logged in user: ".join(", ", @user_list), 5);
71 }
72 }
74 # Handling of msg tag 'user_db'
75 if (exists $msg_hash->{'user_db'}) {
76 my $user_db_list = $msg_hash->{'user_db'};
78 # Sanity check of user_db_list
79 if (ref($user_db_list) eq 'HASH') {
80 &main::daemon_log("$session_id ERROR: 'user_db'-tag in incoming msg has no content!", 1);
82 } else {
83 # Delete all old login information
84 my $sql = "DELETE FROM $main::login_users_tn WHERE regserver='$source'";
85 my $res = $main::login_users_db->exec_statement($sql);
87 # Add each user to login_users_db
88 my @user_list;
89 foreach my $user_db_info (@$user_db_list) {
90 my ($client, $user) = split(/;/, $user_db_info);
91 my %add_hash = ( table=>$main::login_users_tn,
92 primkey=> ['client', 'user'],
93 client=>$client,
94 user=>$user,
95 timestamp=>&get_time,
96 regserver=>$source,
97 );
98 my ($res, $error_str) = $main::login_users_db->add_dbentry( \%add_hash );
99 if ($res != 0) {
100 &main::daemon_log("$session_id ERROR: cannot add entry to known_clients: $error_str", 1);
101 }
102 else
103 {
104 push(@user_list, "'$user' at '$client'");
105 }
106 }
107 &main::daemon_log("$session_id INFO: server '$source' reports the following logged in user: ".join(", ", @user_list), 5);
108 }
109 }
111 return;
112 }
114 sub foreign_job_updates {
115 my ($msg, $msg_hash, $session_id) = @_ ;
116 my $header = @{$msg_hash->{'header'}}[0];
117 my $source = @{$msg_hash->{'source'}}[0];
118 my $target = @{$msg_hash->{'target'}}[0];
120 my @act_keys = keys %$msg_hash;
121 my @jobs;
122 foreach my $key (@act_keys) {
123 if ($key =~ /answer\d+/ ) { push(@jobs, $key); }
124 }
126 foreach my $foreign_job (@jobs) {
128 # add job to job queue
129 my $func_dic = {table=>$main::job_queue_tn,
130 primkey=>['macaddress', 'headertag'],
131 timestamp=>@{@{$msg_hash->{$foreign_job}}[0]->{'timestamp'}}[0],
132 status=>@{@{$msg_hash->{$foreign_job}}[0]->{'status'}}[0],
133 result=>@{@{$msg_hash->{$foreign_job}}[0]->{'result'}}[0],
134 progress=>@{@{$msg_hash->{$foreign_job}}[0]->{'progress'}}[0],
135 headertag=>@{@{$msg_hash->{$foreign_job}}[0]->{'headertag'}}[0],
136 targettag=>@{@{$msg_hash->{$foreign_job}}[0]->{'targettag'}}[0],
137 xmlmessage=>@{@{$msg_hash->{$foreign_job}}[0]->{'xmlmessage'}}[0],
138 macaddress=>@{@{$msg_hash->{$foreign_job}}[0]->{'macaddress'}}[0],
139 plainname=>@{@{$msg_hash->{$foreign_job}}[0]->{'plainname'}}[0],
140 siserver=>$source,
141 modified=>"0",
142 };
143 my $res = $main::job_db->add_dbentry($func_dic);
144 if (not $res == 0) {
145 &main::daemon_log("$session_id ERROR: ServerPackages: process_job_msg: $res", 1);
146 } else {
147 &main::daemon_log("$session_id INFO: ServerPackages: $header, job '".@{@{$msg_hash->{$foreign_job}}[0]->{'headertag'}}[0].
148 "' successfully added to job queue", 5);
149 }
150 }
152 return;
153 }
156 sub new_server {
157 my ($msg, $msg_hash, $session_id) = @_ ;
158 my $header = @{$msg_hash->{'header'}}[0];
159 my $source = @{$msg_hash->{'source'}}[0];
160 my $target = @{$msg_hash->{'target'}}[0];
161 my $key = @{$msg_hash->{'key'}}[0];
162 my $mac = exists $msg_hash->{'macaddress'} ? @{$msg_hash->{'macaddress'}}[0] : "" ;
163 my @clients = exists $msg_hash->{'client'} ? @{$msg_hash->{'client'}} : qw();
164 my @loaded_modules = exists $msg_hash->{'loaded_modules'} ? @{$msg_hash->{'loaded_modules'}} : qw();
166 # Ignor message if I'm already within a registration process for server $source
167 my $check_statement = "SELECT * FROM $main::known_server_tn WHERE status='new_server' AND hostname='$source'";
168 &main::daemon_log("$session_id DEBUG $check_statement", 7);
169 my $check_res = $main::known_server_db->select_dbentry($check_statement);
170 my $blocking_process = keys(%$check_res);
171 if ($blocking_process)
172 {
173 return;
174 }
176 # Sanity check
177 if (ref $key eq 'HASH') {
178 &main::daemon_log("$session_id ERROR: 'new_server'-message from host '$source' contains no key!", 1);
179 return;
180 }
181 # Add foreign server to known_server_db
182 my $new_update_time = &calc_timestamp(&get_time(), 'plus', $main::foreign_servers_register_delay);
183 my $func_dic = {table=>$main::known_server_tn,
184 primkey=>['hostname'],
185 hostname => $source,
186 macaddress => $mac,
187 status => "new_server",
188 hostkey => $key,
189 loaded_modules => join(',', @loaded_modules),
190 timestamp=>&get_time(),
191 update_time=>$new_update_time,
192 };
193 my $res = $main::known_server_db->add_dbentry($func_dic);
194 if (not $res == 0) {
195 &main::daemon_log("$session_id ERROR: server_server_com.pm: cannot add server to known_server_db: $res", 1);
196 } else {
197 &main::daemon_log("$session_id INFO: server_server_com.pm: server '$source' successfully added to known_server_db", 5);
198 }
200 # delete all entries at foreign_clients_db coresponding to this server
201 my $del_sql = "DELETE FROM $main::foreign_clients_tn WHERE regserver='$source' ";
202 my $del_res = $main::foreign_clients_db->exec_statement($del_sql);
204 # add clients of foreign server to known_foreign_clients_db
205 my @sql_list;
206 foreach my $client (@clients) {
207 my @client_details = split(/,/, $client);
209 # workaround to avoid double entries in foreign_clients_db
210 my $del_sql = "DELETE FROM $main::foreign_clients_tn WHERE hostname='".$client_details[0]."'";
211 push(@sql_list, $del_sql);
213 my $sql = "INSERT INTO $main::foreign_clients_tn VALUES ("
214 ."'".$client_details[0]."'," # hostname
215 ."'".$client_details[1]."'," # macaddress
216 ."'".$source."'," # regserver
217 ."'".&get_time()."')"; # timestamp
218 push(@sql_list, $sql);
219 }
220 if (@sql_list) {
221 my $len = @sql_list;
222 $len /= 2;
223 &main::daemon_log("$session_id DEBUG: Inserting ".$len." entries to foreign_clients_db", 8);
224 my $res = $main::foreign_clients_db->exec_statementlist(\@sql_list);
225 }
227 # fetch all registered clients
228 my $client_sql = "SELECT * FROM $main::known_clients_tn";
229 my $client_res = $main::known_clients_db->exec_statement($client_sql);
232 # add already connected clients to registration message
233 my $myhash = &create_xml_hash('confirm_new_server', $main::server_address, $source);
234 &add_content2xml_hash($myhash, 'key', $key);
235 map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res);
237 # add locally loaded gosa-si modules to registration message
238 my $loaded_modules = {};
239 while (my ($package, $pck_info) = each %$main::known_modules) {
240 foreach my $act_module (keys(%{@$pck_info[2]})) {
241 $loaded_modules->{$act_module} = "";
242 }
243 }
244 map(&add_content2xml_hash($myhash, "loaded_modules", $_), keys(%$loaded_modules));
246 # add macaddress to registration message
247 my ($host_ip, $host_port) = split(/:/, $source);
248 my $local_ip = &get_local_ip_for_remote_ip($host_ip);
249 my $network_interface= &get_interface_for_ip($local_ip);
250 my $host_mac = &get_mac_for_interface($network_interface);
251 &add_content2xml_hash($myhash, 'macaddress', $host_mac);
253 # build registration message and send it
254 my $out_msg = &create_xml_string($myhash);
255 my $error = &main::send_msg_to_target($out_msg, $source, $main::ServerPackages_key, 'confirm_new_server', $session_id);
257 return;
258 }
261 sub confirm_new_server {
262 my ($msg, $msg_hash, $session_id) = @_ ;
263 my $header = @{$msg_hash->{'header'}}[0];
264 my $source = @{$msg_hash->{'source'}}[0];
265 my $key = @{$msg_hash->{'key'}}[0];
266 my $mac = exists $msg_hash->{'macaddress'} ? @{$msg_hash->{'macaddress'}}[0] : "" ;
267 my @clients = exists $msg_hash->{'client'} ? @{$msg_hash->{'client'}} : qw();
268 my @loaded_modules = exists $msg_hash->{'loaded_modules'} ? @{$msg_hash->{'loaded_modules'}} : qw();
270 my $new_update_time = &calc_timestamp(&get_time(), 'plus', $main::foreign_servers_register_delay);
271 my $sql = "UPDATE $main::known_server_tn".
272 " SET status='$header', hostkey='$key', loaded_modules='".join(",",@loaded_modules)."', macaddress='$mac', update_time='$new_update_time'".
273 " WHERE hostname='$source'";
274 my $res = $main::known_server_db->update_dbentry($sql);
276 # add clients of foreign server to known_foreign_clients_db
277 my @sql_list;
278 foreach my $client (@clients) {
279 my @client_details = split(/,/, $client);
281 # workaround to avoid double entries in foreign_clients_db
282 my $del_sql = "DELETE FROM $main::foreign_clients_tn WHERE hostname='".$client_details[0]."'";
283 push(@sql_list, $del_sql);
285 my $sql = "INSERT INTO $main::foreign_clients_tn VALUES ("
286 ."'".$client_details[0]."'," # hostname
287 ."'".$client_details[1]."'," # macaddress
288 ."'".$source."'," # regserver
289 ."'".&get_time()."')"; # timestamp
290 push(@sql_list, $sql);
291 }
292 if (@sql_list) {
293 my $len = @sql_list;
294 $len /= 2;
295 &main::daemon_log("$session_id DEBUG: Inserting ".$len." entries to foreign_clients_db", 8);
296 my $res = $main::foreign_clients_db->exec_statementlist(\@sql_list);
297 }
300 return;
301 }
304 sub new_foreign_client {
305 my ($msg, $msg_hash, $session_id) = @_ ;
306 my $header = @{$msg_hash->{'header'}}[0];
307 my $source = @{$msg_hash->{'source'}}[0];
308 my $hostname = @{$msg_hash->{'client'}}[0];
309 my $macaddress = @{$msg_hash->{'macaddress'}}[0];
310 # if new client is known in known_clients_db
311 my $check_sql = "SELECT * FROM $main::known_clients_tn WHERE (macaddress LIKE '$macaddress')";
312 my $check_res = $main::known_clients_db->select_dbentry($check_sql);
314 if( (keys(%$check_res) == 1) ) {
315 my $host_key = $check_res->{1}->{'hostkey'};
317 # check if new client is still alive
318 my $client_hash = &create_xml_hash("ping", $main::server_address, $hostname);
319 &add_content2xml_hash($client_hash, 'session_id', $session_id);
320 my $client_msg = &create_xml_string($client_hash);
321 my $error = &main::send_msg_to_target($client_msg, $hostname, $host_key, 'ping', $session_id);
322 my $message_id;
323 my $i = 0;
324 while (1) {
325 $i++;
326 my $sql = "SELECT * FROM $main::incoming_tn WHERE headertag='answer_$session_id'";
327 my $res = $main::incoming_db->exec_statement($sql);
328 if (ref @$res[0] eq "ARRAY") {
329 $message_id = @{@$res[0]}[0];
330 last;
331 }
333 # do not run into a endless loop
334 if ($i > 50) { last; }
335 usleep(100000);
336 }
338 # client is alive
339 # -> new_foreign_client will be ignored
340 if (defined $message_id) {
341 &main::daemon_log("$session_id ERROR: At new_foreign_clients: host '$hostname' is reported as a new foreign client, ".
342 "but the host is still registered at this server. So, the new_foreign_client-msg will be ignored: $msg", 1);
343 }
344 }
347 # new client is not found in known_clients_db or
348 # new client is dead -> new_client-msg from foreign server is valid
349 # -> client will be deleted from known_clients_db
350 # -> inserted to foreign_clients_db
352 my $del_sql = "DELETE FROM $main::known_clients_tn WHERE (hostname='$hostname')";
353 my $del_res = $main::known_clients_db->exec_statement($del_sql);
354 my $func_dic = { table => $main::foreign_clients_tn,
355 primkey => ['hostname'],
356 hostname => $hostname,
357 macaddress => $macaddress,
358 regserver => $source,
359 timestamp => &get_time(),
360 };
361 my $res = $main::foreign_clients_db->add_dbentry($func_dic);
362 if (not $res == 0) {
363 &main::daemon_log("$session_id ERROR: server_server_com.pm: cannot add server to foreign_clients_db: $res", 1);
364 } else {
365 &main::daemon_log("$session_id INFO: server_server_com.pm: client '$hostname' successfully added to foreign_clients_db", 5);
366 }
368 return;
369 }
372 sub trigger_wake {
373 my ($msg, $msg_hash, $session_id) = @_ ;
375 foreach (@{$msg_hash->{'macaddress'}}){
376 &main::daemon_log("$session_id INFO: trigger wake for $_", 5);
377 my $host = $_;
378 my $ipaddr = '255.255.255.255';
379 my $port = getservbyname('discard', 'udp');
380 if (not defined $port) {
381 &main::daemon_log("$session_id ERROR: cannot determine port for wol $_: 'getservbyname('discard', 'udp')' failed!",1);
382 next;
383 }
385 my ($raddr, $them, $proto);
386 my ($hwaddr, $hwaddr_re, $pkt);
388 # get the hardware address (ethernet address)
389 $hwaddr_re = join(':', ('[0-9A-Fa-f]{1,2}') x 6);
390 if ($host =~ m/^$hwaddr_re$/) {
391 $hwaddr = $host;
392 } else {
393 &main::daemon_log("$session_id ERROR: trigger_wake called with non mac address", 1);
394 }
396 # Generate magic sequence
397 foreach (split /:/, $hwaddr) {
398 $pkt .= chr(hex($_));
399 }
400 $pkt = chr(0xFF) x 6 . $pkt x 16 . $main::wake_on_lan_passwd;
402 # Allocate socket and send packet
404 $raddr = gethostbyname($ipaddr);
405 if (not defined $raddr) {
406 &main::daemon_log("$session_id ERROR: cannot determine raddr for wol $_: 'gethostbyname($ipaddr)' failed!", 1);
407 next;
408 }
410 $them = pack_sockaddr_in($port, $raddr);
411 $proto = getprotobyname('udp');
413 socket(S, AF_INET, SOCK_DGRAM, $proto) or die "socket : $!";
414 setsockopt(S, SOL_SOCKET, SO_BROADCAST, 1) or die "setsockopt : $!";
415 send(S, $pkt, 0, $them) or die "send : $!";
416 close S;
417 }
419 return;
420 }
423 sub confirm_usr_msg {
424 my ($msg, $msg_hash, $session_id) = @_ ;
425 &clMessages::confirm_usr_msg($msg, $msg_hash, $session_id);
426 return;
427 }
430 1;