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();
82 # sanity check
83 if (ref $key eq 'HASH') {
84 &main::daemon_log("$session_id ERROR: 'new_server'-message from host '$source' contains no key!", 1);
85 return;
86 }
88 # add foreign server to known_server_db
89 my $func_dic = {table=>$main::known_server_tn,
90 primkey=>['hostname'],
91 hostname => $source,
92 status => "new_server",
93 hostkey => $key,
94 timestamp=>&get_time(),
95 };
96 my $res = $main::known_server_db->add_dbentry($func_dic);
97 if (not $res == 0) {
98 &main::daemon_log("$session_id ERROR: server_server_com.pm: cannot add server to known_server_db: $res", 1);
99 } else {
100 &main::daemon_log("$session_id INFO: server_server_com.pm: server '$source' successfully added to known_server_db", 5);
101 }
103 # delete all entries at foreign_clients_db coresponding to this server
104 my $del_sql = "DELETE FROM $main::foreign_clients_tn WHERE regserver='$source' ";
105 my $del_res = $main::foreign_clients_db->exec_statement($del_sql);
107 # add clients of foreign server to known_foreign_clients_db
108 my @sql_list;
109 foreach my $client (@clients) {
110 my @client_details = split(/,/, $client);
112 # workaround to avoid double entries in foreign_clients_db
113 my $del_sql = "DELETE FROM $main::foreign_clients_tn WHERE hostname='".$client_details[0]."'";
114 push(@sql_list, $del_sql);
116 my $sql = "INSERT INTO $main::foreign_clients_tn VALUES ("
117 ."'".$client_details[0]."'," # hostname
118 ."'".$client_details[1]."'," # macaddress
119 ."'".$source."'," # regserver
120 ."'".&get_time()."')"; # timestamp
121 push(@sql_list, $sql);
122 }
123 if (@sql_list) {
124 my $len = @sql_list;
125 $len /= 2;
126 &main::daemon_log("$session_id DEBUG: Inserting ".$len." entries to foreign_clients_db", 8);
127 my $res = $main::foreign_clients_db->exec_statementlist(\@sql_list);
128 }
130 # fetch all registered clients
131 my $client_sql = "SELECT * FROM $main::known_clients_tn";
132 my $client_res = $main::known_clients_db->exec_statement($client_sql);
135 # add already connected clients to registration message
136 my $myhash = &create_xml_hash('confirm_new_server', $main::server_address, $source);
137 &add_content2xml_hash($myhash, 'key', $key);
138 map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res);
140 # build registration message and send it
141 my $out_msg = &create_xml_string($myhash);
144 # build confirm_new_server message
145 #my %data = ( key=>$key );
146 #my $out_msg = &build_msg('confirm_new_server', $main::server_address, $source, \%data);
147 my $error = &main::send_msg_to_target($out_msg, $source, $main::ServerPackages_key, 'confirm_new_server', $session_id);
149 return;
150 }
153 sub confirm_new_server {
154 my ($msg, $msg_hash, $session_id) = @_ ;
155 my $header = @{$msg_hash->{'header'}}[0];
156 my $source = @{$msg_hash->{'source'}}[0];
157 my $key = @{$msg_hash->{'key'}}[0];
158 my @clients = exists $msg_hash->{'client'} ? @{$msg_hash->{'client'}} : qw();
160 my $sql = "UPDATE $main::known_server_tn SET status='$header', hostkey='$key' WHERE hostname='$source'";
161 my $res = $main::known_server_db->update_dbentry($sql);
163 # add clients of foreign server to known_foreign_clients_db
164 my @sql_list;
165 foreach my $client (@clients) {
166 my @client_details = split(/,/, $client);
168 # workaround to avoid double entries in foreign_clients_db
169 my $del_sql = "DELETE FROM $main::foreign_clients_tn WHERE hostname='".$client_details[0]."'";
170 push(@sql_list, $del_sql);
172 my $sql = "INSERT INTO $main::foreign_clients_tn VALUES ("
173 ."'".$client_details[0]."'," # hostname
174 ."'".$client_details[1]."'," # macaddress
175 ."'".$source."'," # regserver
176 ."'".&get_time()."')"; # timestamp
177 push(@sql_list, $sql);
178 }
179 if (@sql_list) {
180 my $len = @sql_list;
181 $len /= 2;
182 &main::daemon_log("$session_id DEBUG: Inserting ".$len." entries to foreign_clients_db", 8);
183 my $res = $main::foreign_clients_db->exec_statementlist(\@sql_list);
184 }
187 return;
188 }
191 sub new_foreign_client {
192 my ($msg, $msg_hash, $session_id) = @_ ;
193 my $header = @{$msg_hash->{'header'}}[0];
194 my $source = @{$msg_hash->{'source'}}[0];
195 my $hostname = @{$msg_hash->{'client'}}[0];
196 my $macaddress = @{$msg_hash->{'macaddress'}}[0];
197 # if new client is known in known_clients_db
198 my $check_sql = "SELECT * FROM $main::known_clients_tn WHERE (macaddress LIKE '$macaddress')";
199 my $check_res = $main::known_clients_db->select_dbentry($check_sql);
201 if( (keys(%$check_res) == 1) ) {
202 my $host_key = $check_res->{1}->{'hostkey'};
204 # check if new client is still alive
205 my $client_hash = &create_xml_hash("ping", $main::server_address, $hostname);
206 &add_content2xml_hash($client_hash, 'session_id', $session_id);
207 my $client_msg = &create_xml_string($client_hash);
208 my $error = &main::send_msg_to_target($client_msg, $hostname, $host_key, 'ping', $session_id);
209 my $message_id;
210 my $i = 0;
211 while (1) {
212 $i++;
213 my $sql = "SELECT * FROM $main::incoming_tn WHERE headertag='answer_$session_id'";
214 my $res = $main::incoming_db->exec_statement($sql);
215 if (ref @$res[0] eq "ARRAY") {
216 $message_id = @{@$res[0]}[0];
217 last;
218 }
220 # do not run into a endless loop
221 if ($i > 50) { last; }
222 usleep(100000);
223 }
225 # client is alive
226 # -> new_foreign_client will be ignored
227 if (defined $message_id) {
228 &main::daemon_log("$session_id ERROR: At new_foreign_clients: host '$hostname' is reported as a new foreign client, ".
229 "but the host is still registered at this server. So, the new_foreign_client-msg will be ignored: $msg", 1);
230 }
231 }
234 # new client is not found in known_clients_db or
235 # new client is dead -> new_client-msg from foreign server is valid
236 # -> client will be deleted from known_clients_db
237 # -> inserted to foreign_clients_db
239 my $del_sql = "DELETE FROM $main::known_clients_tn WHERE (hostname='$hostname')";
240 my $del_res = $main::known_clients_db->exec_statement($del_sql);
241 my $func_dic = { table => $main::foreign_clients_tn,
242 primkey => ['hostname'],
243 hostname => $hostname,
244 macaddress => $macaddress,
245 regserver => $source,
246 timestamp => &get_time(),
247 };
248 my $res = $main::foreign_clients_db->add_dbentry($func_dic);
249 if (not $res == 0) {
250 &main::daemon_log("$session_id ERROR: server_server_com.pm: cannot add server to foreign_clients_db: $res", 1);
251 } else {
252 &main::daemon_log("$session_id INFO: server_server_com.pm: client '$hostname' successfully added to foreign_clients_db", 5);
253 }
255 return;
256 }
259 sub trigger_wake {
260 my ($msg, $msg_hash, $session_id) = @_ ;
262 foreach (@{$msg_hash->{macAddress}}){
263 &main::daemon_log("$session_id INFO: trigger wake for $_", 5);
264 my $host = $_;
265 my $ipaddr = '255.255.255.255';
266 my $port = getservbyname('discard', 'udp');
267 if (not defined $port) {
268 &main::daemon_log("$session_id ERROR: cannot determine port for wol $_: 'getservbyname('discard', 'udp')' failed!",1);
269 next;
270 }
272 my ($raddr, $them, $proto);
273 my ($hwaddr, $hwaddr_re, $pkt);
275 # get the hardware address (ethernet address)
276 $hwaddr_re = join(':', ('[0-9A-Fa-f]{1,2}') x 6);
277 if ($host =~ m/^$hwaddr_re$/) {
278 $hwaddr = $host;
279 } else {
280 &main::daemon_log("$session_id ERROR: trigger_wake called with non mac address", 1);
281 }
283 # Generate magic sequence
284 foreach (split /:/, $hwaddr) {
285 $pkt .= chr(hex($_));
286 }
287 $pkt = chr(0xFF) x 6 . $pkt x 16 . $main::wake_on_lan_passwd;
289 # Allocate socket and send packet
291 $raddr = gethostbyname($ipaddr);
292 if (not defined $raddr) {
293 &main::daemon_log("$session_id ERROR: cannot determine raddr for wol $_: 'gethostbyname($ipaddr)' failed!", 1);
294 next;
295 }
297 $them = pack_sockaddr_in($port, $raddr);
298 $proto = getprotobyname('udp');
300 socket(S, AF_INET, SOCK_DGRAM, $proto) or die "socket : $!";
301 setsockopt(S, SOL_SOCKET, SO_BROADCAST, 1) or die "setsockopt : $!";
302 send(S, $pkt, 0, $them) or die "send : $!";
303 close S;
304 }
306 return;
307 }
310 1;