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 }
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 loaded_modules => join(',', @loaded_modules),
95 timestamp=>&get_time(),
96 };
97 my $res = $main::known_server_db->add_dbentry($func_dic);
98 if (not $res == 0) {
99 &main::daemon_log("$session_id ERROR: server_server_com.pm: cannot add server to known_server_db: $res", 1);
100 } else {
101 &main::daemon_log("$session_id INFO: server_server_com.pm: server '$source' successfully added to known_server_db", 5);
102 }
104 # delete all entries at foreign_clients_db coresponding to this server
105 my $del_sql = "DELETE FROM $main::foreign_clients_tn WHERE regserver='$source' ";
106 my $del_res = $main::foreign_clients_db->exec_statement($del_sql);
108 # add clients of foreign server to known_foreign_clients_db
109 my @sql_list;
110 foreach my $client (@clients) {
111 my @client_details = split(/,/, $client);
113 # workaround to avoid double entries in foreign_clients_db
114 my $del_sql = "DELETE FROM $main::foreign_clients_tn WHERE hostname='".$client_details[0]."'";
115 push(@sql_list, $del_sql);
117 my $sql = "INSERT INTO $main::foreign_clients_tn VALUES ("
118 ."'".$client_details[0]."'," # hostname
119 ."'".$client_details[1]."'," # macaddress
120 ."'".$source."'," # regserver
121 ."'".&get_time()."')"; # timestamp
122 push(@sql_list, $sql);
123 }
124 if (@sql_list) {
125 my $len = @sql_list;
126 $len /= 2;
127 &main::daemon_log("$session_id DEBUG: Inserting ".$len." entries to foreign_clients_db", 8);
128 my $res = $main::foreign_clients_db->exec_statementlist(\@sql_list);
129 }
131 # fetch all registered clients
132 my $client_sql = "SELECT * FROM $main::known_clients_tn";
133 my $client_res = $main::known_clients_db->exec_statement($client_sql);
136 # add already connected clients to registration message
137 my $myhash = &create_xml_hash('confirm_new_server', $main::server_address, $source);
138 &add_content2xml_hash($myhash, 'key', $key);
139 map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res);
141 # add locally loaded gosa-si modules to registration message
142 my $loaded_modules = {};
143 while (my ($package, $pck_info) = each %$main::known_modules) {
144 foreach my $act_module (keys(%{@$pck_info[2]})) {
145 $loaded_modules->{$act_module} = "";
146 }
147 }
148 map(&add_content2xml_hash($myhash, "loaded_modules", $_), keys(%$loaded_modules));
150 # build registration message and send it
151 my $out_msg = &create_xml_string($myhash);
152 my $error = &main::send_msg_to_target($out_msg, $source, $main::ServerPackages_key, 'confirm_new_server', $session_id);
154 return;
155 }
158 sub confirm_new_server {
159 my ($msg, $msg_hash, $session_id) = @_ ;
160 my $header = @{$msg_hash->{'header'}}[0];
161 my $source = @{$msg_hash->{'source'}}[0];
162 my $key = @{$msg_hash->{'key'}}[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 my $sql = "UPDATE $main::known_server_tn SET status='$header', hostkey='$key', loaded_modules='".join(",",@loaded_modules)."' WHERE hostname='$source'";
167 my $res = $main::known_server_db->update_dbentry($sql);
169 # add clients of foreign server to known_foreign_clients_db
170 my @sql_list;
171 foreach my $client (@clients) {
172 my @client_details = split(/,/, $client);
174 # workaround to avoid double entries in foreign_clients_db
175 my $del_sql = "DELETE FROM $main::foreign_clients_tn WHERE hostname='".$client_details[0]."'";
176 push(@sql_list, $del_sql);
178 my $sql = "INSERT INTO $main::foreign_clients_tn VALUES ("
179 ."'".$client_details[0]."'," # hostname
180 ."'".$client_details[1]."'," # macaddress
181 ."'".$source."'," # regserver
182 ."'".&get_time()."')"; # timestamp
183 push(@sql_list, $sql);
184 }
185 if (@sql_list) {
186 my $len = @sql_list;
187 $len /= 2;
188 &main::daemon_log("$session_id DEBUG: Inserting ".$len." entries to foreign_clients_db", 8);
189 my $res = $main::foreign_clients_db->exec_statementlist(\@sql_list);
190 }
193 return;
194 }
197 sub new_foreign_client {
198 my ($msg, $msg_hash, $session_id) = @_ ;
199 my $header = @{$msg_hash->{'header'}}[0];
200 my $source = @{$msg_hash->{'source'}}[0];
201 my $hostname = @{$msg_hash->{'client'}}[0];
202 my $macaddress = @{$msg_hash->{'macaddress'}}[0];
203 # if new client is known in known_clients_db
204 my $check_sql = "SELECT * FROM $main::known_clients_tn WHERE (macaddress LIKE '$macaddress')";
205 my $check_res = $main::known_clients_db->select_dbentry($check_sql);
207 if( (keys(%$check_res) == 1) ) {
208 my $host_key = $check_res->{1}->{'hostkey'};
210 # check if new client is still alive
211 my $client_hash = &create_xml_hash("ping", $main::server_address, $hostname);
212 &add_content2xml_hash($client_hash, 'session_id', $session_id);
213 my $client_msg = &create_xml_string($client_hash);
214 my $error = &main::send_msg_to_target($client_msg, $hostname, $host_key, 'ping', $session_id);
215 my $message_id;
216 my $i = 0;
217 while (1) {
218 $i++;
219 my $sql = "SELECT * FROM $main::incoming_tn WHERE headertag='answer_$session_id'";
220 my $res = $main::incoming_db->exec_statement($sql);
221 if (ref @$res[0] eq "ARRAY") {
222 $message_id = @{@$res[0]}[0];
223 last;
224 }
226 # do not run into a endless loop
227 if ($i > 50) { last; }
228 usleep(100000);
229 }
231 # client is alive
232 # -> new_foreign_client will be ignored
233 if (defined $message_id) {
234 &main::daemon_log("$session_id ERROR: At new_foreign_clients: host '$hostname' is reported as a new foreign client, ".
235 "but the host is still registered at this server. So, the new_foreign_client-msg will be ignored: $msg", 1);
236 }
237 }
240 # new client is not found in known_clients_db or
241 # new client is dead -> new_client-msg from foreign server is valid
242 # -> client will be deleted from known_clients_db
243 # -> inserted to foreign_clients_db
245 my $del_sql = "DELETE FROM $main::known_clients_tn WHERE (hostname='$hostname')";
246 my $del_res = $main::known_clients_db->exec_statement($del_sql);
247 my $func_dic = { table => $main::foreign_clients_tn,
248 primkey => ['hostname'],
249 hostname => $hostname,
250 macaddress => $macaddress,
251 regserver => $source,
252 timestamp => &get_time(),
253 };
254 my $res = $main::foreign_clients_db->add_dbentry($func_dic);
255 if (not $res == 0) {
256 &main::daemon_log("$session_id ERROR: server_server_com.pm: cannot add server to foreign_clients_db: $res", 1);
257 } else {
258 &main::daemon_log("$session_id INFO: server_server_com.pm: client '$hostname' successfully added to foreign_clients_db", 5);
259 }
261 return;
262 }
265 sub trigger_wake {
266 my ($msg, $msg_hash, $session_id) = @_ ;
268 foreach (@{$msg_hash->{macAddress}}){
269 &main::daemon_log("$session_id INFO: trigger wake for $_", 5);
270 my $host = $_;
271 my $ipaddr = '255.255.255.255';
272 my $port = getservbyname('discard', 'udp');
273 if (not defined $port) {
274 &main::daemon_log("$session_id ERROR: cannot determine port for wol $_: 'getservbyname('discard', 'udp')' failed!",1);
275 next;
276 }
278 my ($raddr, $them, $proto);
279 my ($hwaddr, $hwaddr_re, $pkt);
281 # get the hardware address (ethernet address)
282 $hwaddr_re = join(':', ('[0-9A-Fa-f]{1,2}') x 6);
283 if ($host =~ m/^$hwaddr_re$/) {
284 $hwaddr = $host;
285 } else {
286 &main::daemon_log("$session_id ERROR: trigger_wake called with non mac address", 1);
287 }
289 # Generate magic sequence
290 foreach (split /:/, $hwaddr) {
291 $pkt .= chr(hex($_));
292 }
293 $pkt = chr(0xFF) x 6 . $pkt x 16 . $main::wake_on_lan_passwd;
295 # Allocate socket and send packet
297 $raddr = gethostbyname($ipaddr);
298 if (not defined $raddr) {
299 &main::daemon_log("$session_id ERROR: cannot determine raddr for wol $_: 'gethostbyname($ipaddr)' failed!", 1);
300 next;
301 }
303 $them = pack_sockaddr_in($port, $raddr);
304 $proto = getprotobyname('udp');
306 socket(S, AF_INET, SOCK_DGRAM, $proto) or die "socket : $!";
307 setsockopt(S, SOL_SOCKET, SO_BROADCAST, 1) or die "setsockopt : $!";
308 send(S, $pkt, 0, $them) or die "send : $!";
309 close S;
310 }
312 return;
313 }
316 1;