1 package server_server_com;
2 use Exporter;
3 @ISA = qw(Exporter);
4 my @events = (
5 'new_server',
6 'confirm_new_server',
7 'new_foreign_client',
8 'trigger_wake',
9 'foreign_job_updates',
10 );
11 @EXPORT = @events;
13 use strict;
14 use warnings;
15 use Data::Dumper;
16 use GOSA::GosaSupportDaemon;
17 use Time::HiRes qw( usleep);
18 use Socket;
21 BEGIN {}
23 END {}
25 ### Start ######################################################################
27 sub get_events {
28 return \@events;
29 }
32 sub foreign_job_updates {
33 my ($msg, $msg_hash, $session_id) = @_ ;
34 my $header = @{$msg_hash->{'header'}}[0];
35 my $source = @{$msg_hash->{'source'}}[0];
36 my $target = @{$msg_hash->{'target'}}[0];
38 my @act_keys = keys %$msg_hash;
39 my @jobs;
40 foreach my $key (@act_keys) {
41 if ($key =~ /answer\d+/ ) { push(@jobs, $key); }
42 }
44 foreach my $foreign_job (@jobs) {
46 # add job to job queue
47 my $func_dic = {table=>$main::job_queue_tn,
48 primkey=>['macaddress', 'headertag'],
49 timestamp=>@{@{$msg_hash->{$foreign_job}}[0]->{'timestamp'}}[0],
50 status=>@{@{$msg_hash->{$foreign_job}}[0]->{'status'}}[0],
51 result=>@{@{$msg_hash->{$foreign_job}}[0]->{'result'}}[0],
52 progress=>@{@{$msg_hash->{$foreign_job}}[0]->{'progress'}}[0],
53 headertag=>@{@{$msg_hash->{$foreign_job}}[0]->{'headertag'}}[0],
54 targettag=>@{@{$msg_hash->{$foreign_job}}[0]->{'targettag'}}[0],
55 xmlmessage=>@{@{$msg_hash->{$foreign_job}}[0]->{'xmlmessage'}}[0],
56 macaddress=>@{@{$msg_hash->{$foreign_job}}[0]->{'macaddress'}}[0],
57 plainname=>@{@{$msg_hash->{$foreign_job}}[0]->{'plainname'}}[0],
58 siserver=>$source,
59 modified=>"0",
60 };
61 my $res = $main::job_db->add_dbentry($func_dic);
62 if (not $res == 0) {
63 &main::daemon_log("$session_id ERROR: ServerPackages: process_job_msg: $res", 1);
64 } else {
65 &main::daemon_log("$session_id INFO: ServerPackages: $header, job '".@{@{$msg_hash->{$foreign_job}}[0]->{'headertag'}}[0].
66 "' successfully added to job queue", 5);
67 }
68 }
70 return;
71 }
74 sub new_server {
75 my ($msg, $msg_hash, $session_id) = @_ ;
76 my $header = @{$msg_hash->{'header'}}[0];
77 my $source = @{$msg_hash->{'source'}}[0];
78 my $target = @{$msg_hash->{'target'}}[0];
79 my $key = @{$msg_hash->{'key'}}[0];
80 my @clients = exists $msg_hash->{'client'} ? @{$msg_hash->{'client'}} : qw();
81 my @loaded_modules = exists $msg_hash->{'loaded_modules'} ? @{$msg_hash->{'loaded_modules'}} : qw();
83 # sanity check
84 if (ref $key eq 'HASH') {
85 &main::daemon_log("$session_id ERROR: 'new_server'-message from host '$source' contains no key!", 1);
86 return;
87 }
89 # add foreign server to known_server_db
90 my $func_dic = {table=>$main::known_server_tn,
91 primkey=>['hostname'],
92 hostname => $source,
93 status => "new_server",
94 hostkey => $key,
95 loaded_modules => join(',', @loaded_modules),
96 timestamp=>&get_time(),
97 };
98 my $res = $main::known_server_db->add_dbentry($func_dic);
99 if (not $res == 0) {
100 &main::daemon_log("$session_id ERROR: server_server_com.pm: cannot add server to known_server_db: $res", 1);
101 } else {
102 &main::daemon_log("$session_id INFO: server_server_com.pm: server '$source' successfully added to known_server_db", 5);
103 }
105 # delete all entries at foreign_clients_db coresponding to this server
106 my $del_sql = "DELETE FROM $main::foreign_clients_tn WHERE regserver='$source' ";
107 my $del_res = $main::foreign_clients_db->exec_statement($del_sql);
109 # add clients of foreign server to known_foreign_clients_db
110 my @sql_list;
111 foreach my $client (@clients) {
112 my @client_details = split(/,/, $client);
114 # workaround to avoid double entries in foreign_clients_db
115 my $del_sql = "DELETE FROM $main::foreign_clients_tn WHERE hostname='".$client_details[0]."'";
116 push(@sql_list, $del_sql);
118 my $sql = "INSERT INTO $main::foreign_clients_tn VALUES ("
119 ."'".$client_details[0]."'," # hostname
120 ."'".$client_details[1]."'," # macaddress
121 ."'".$source."'," # regserver
122 ."'".&get_time()."')"; # timestamp
123 push(@sql_list, $sql);
124 }
125 if (@sql_list) {
126 my $len = @sql_list;
127 $len /= 2;
128 &main::daemon_log("$session_id DEBUG: Inserting ".$len." entries to foreign_clients_db", 8);
129 my $res = $main::foreign_clients_db->exec_statementlist(\@sql_list);
130 }
132 # fetch all registered clients
133 my $client_sql = "SELECT * FROM $main::known_clients_tn";
134 my $client_res = $main::known_clients_db->exec_statement($client_sql);
137 # add already connected clients to registration message
138 my $myhash = &create_xml_hash('confirm_new_server', $main::server_address, $source);
139 &add_content2xml_hash($myhash, 'key', $key);
140 map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res);
142 # build registration message and send it
143 my $out_msg = &create_xml_string($myhash);
144 my $error = &main::send_msg_to_target($out_msg, $source, $main::ServerPackages_key, 'confirm_new_server', $session_id);
146 return;
147 }
150 sub confirm_new_server {
151 my ($msg, $msg_hash, $session_id) = @_ ;
152 my $header = @{$msg_hash->{'header'}}[0];
153 my $source = @{$msg_hash->{'source'}}[0];
154 my $key = @{$msg_hash->{'key'}}[0];
155 my @clients = exists $msg_hash->{'client'} ? @{$msg_hash->{'client'}} : qw();
157 my $sql = "UPDATE $main::known_server_tn SET status='$header', hostkey='$key' WHERE hostname='$source'";
158 my $res = $main::known_server_db->update_dbentry($sql);
160 # add clients of foreign server to known_foreign_clients_db
161 my @sql_list;
162 foreach my $client (@clients) {
163 my @client_details = split(/,/, $client);
165 # workaround to avoid double entries in foreign_clients_db
166 my $del_sql = "DELETE FROM $main::foreign_clients_tn WHERE hostname='".$client_details[0]."'";
167 push(@sql_list, $del_sql);
169 my $sql = "INSERT INTO $main::foreign_clients_tn VALUES ("
170 ."'".$client_details[0]."'," # hostname
171 ."'".$client_details[1]."'," # macaddress
172 ."'".$source."'," # regserver
173 ."'".&get_time()."')"; # timestamp
174 push(@sql_list, $sql);
175 }
176 if (@sql_list) {
177 my $len = @sql_list;
178 $len /= 2;
179 &main::daemon_log("$session_id DEBUG: Inserting ".$len." entries to foreign_clients_db", 8);
180 my $res = $main::foreign_clients_db->exec_statementlist(\@sql_list);
181 }
184 return;
185 }
188 sub new_foreign_client {
189 my ($msg, $msg_hash, $session_id) = @_ ;
190 my $header = @{$msg_hash->{'header'}}[0];
191 my $source = @{$msg_hash->{'source'}}[0];
192 my $hostname = @{$msg_hash->{'client'}}[0];
193 my $macaddress = @{$msg_hash->{'macaddress'}}[0];
194 # if new client is known in known_clients_db
195 my $check_sql = "SELECT * FROM $main::known_clients_tn WHERE (macaddress LIKE '$macaddress')";
196 my $check_res = $main::known_clients_db->select_dbentry($check_sql);
198 if( (keys(%$check_res) == 1) ) {
199 my $host_key = $check_res->{1}->{'hostkey'};
201 # check if new client is still alive
202 my $client_hash = &create_xml_hash("ping", $main::server_address, $hostname);
203 &add_content2xml_hash($client_hash, 'session_id', $session_id);
204 my $client_msg = &create_xml_string($client_hash);
205 my $error = &main::send_msg_to_target($client_msg, $hostname, $host_key, 'ping', $session_id);
206 my $message_id;
207 my $i = 0;
208 while (1) {
209 $i++;
210 my $sql = "SELECT * FROM $main::incoming_tn WHERE headertag='answer_$session_id'";
211 my $res = $main::incoming_db->exec_statement($sql);
212 if (ref @$res[0] eq "ARRAY") {
213 $message_id = @{@$res[0]}[0];
214 last;
215 }
217 # do not run into a endless loop
218 if ($i > 50) { last; }
219 usleep(100000);
220 }
222 # client is alive
223 # -> new_foreign_client will be ignored
224 if (defined $message_id) {
225 &main::daemon_log("$session_id ERROR: At new_foreign_clients: host '$hostname' is reported as a new foreign client, ".
226 "but the host is still registered at this server. So, the new_foreign_client-msg will be ignored: $msg", 1);
227 }
228 }
231 # new client is not found in known_clients_db or
232 # new client is dead -> new_client-msg from foreign server is valid
233 # -> client will be deleted from known_clients_db
234 # -> inserted to foreign_clients_db
236 my $del_sql = "DELETE FROM $main::known_clients_tn WHERE (hostname='$hostname')";
237 my $del_res = $main::known_clients_db->exec_statement($del_sql);
238 my $func_dic = { table => $main::foreign_clients_tn,
239 primkey => ['hostname'],
240 hostname => $hostname,
241 macaddress => $macaddress,
242 regserver => $source,
243 timestamp => &get_time(),
244 };
245 my $res = $main::foreign_clients_db->add_dbentry($func_dic);
246 if (not $res == 0) {
247 &main::daemon_log("$session_id ERROR: server_server_com.pm: cannot add server to foreign_clients_db: $res", 1);
248 } else {
249 &main::daemon_log("$session_id INFO: server_server_com.pm: client '$hostname' successfully added to foreign_clients_db", 5);
250 }
252 return;
253 }
256 sub trigger_wake {
257 my ($msg, $msg_hash, $session_id) = @_ ;
259 foreach (@{$msg_hash->{macAddress}}){
260 &main::daemon_log("$session_id INFO: trigger wake for $_", 5);
261 my $host = $_;
262 my $ipaddr = '255.255.255.255';
263 my $port = getservbyname('discard', 'udp');
264 if (not defined $port) {
265 &main::daemon_log("$session_id ERROR: cannot determine port for wol $_: 'getservbyname('discard', 'udp')' failed!",1);
266 next;
267 }
269 my ($raddr, $them, $proto);
270 my ($hwaddr, $hwaddr_re, $pkt);
272 # get the hardware address (ethernet address)
273 $hwaddr_re = join(':', ('[0-9A-Fa-f]{1,2}') x 6);
274 if ($host =~ m/^$hwaddr_re$/) {
275 $hwaddr = $host;
276 } else {
277 &main::daemon_log("$session_id ERROR: trigger_wake called with non mac address", 1);
278 }
280 # Generate magic sequence
281 foreach (split /:/, $hwaddr) {
282 $pkt .= chr(hex($_));
283 }
284 $pkt = chr(0xFF) x 6 . $pkt x 16 . $main::wake_on_lan_passwd;
286 # Allocate socket and send packet
288 $raddr = gethostbyname($ipaddr);
289 if (not defined $raddr) {
290 &main::daemon_log("$session_id ERROR: cannot determine raddr for wol $_: 'gethostbyname($ipaddr)' failed!", 1);
291 next;
292 }
294 $them = pack_sockaddr_in($port, $raddr);
295 $proto = getprotobyname('udp');
297 socket(S, AF_INET, SOCK_DGRAM, $proto) or die "socket : $!";
298 setsockopt(S, SOL_SOCKET, SO_BROADCAST, 1) or die "setsockopt : $!";
299 send(S, $pkt, 0, $them) or die "send : $!";
300 close S;
301 }
303 return;
304 }
307 1;