Code

874aaa472da195140347bb9418882121e10a1eec
[gosa.git] / gosa-si / modules / ServerPackages.pm
1 package ServerPackages;
3 use Exporter;
4 @ISA = ("Exporter");
6 # Each module has to have a function 'process_incoming_msg'. This function works as a interface to gosa-sd and recieves the msg hash from gosa-sd. 'process_incoming_function checks, wether it has a function to process the incoming msg and forward the msg to it. 
9 use strict;
10 use warnings;
11 use GosaSupportDaemon;
12 use IO::Socket::INET;
13 use XML::Simple;
15 BEGIN{}
16 END {}
19 my ($server_activ, $server_port, $server_passwd, $max_clients);
20 my ($bus_activ, $bus_passwd, $bus_ip, $bus_port);
21 my $server;
22 my $no_bus;
24 my %cfg_defaults =
25 ("server" =>
26     {"server_activ" => [\$server_activ, "on"],
27     "server_port" => [\$server_port, "20081"],
28     "server_passwd" => [\$server_passwd, ""],
29     "max_clients" => [\$max_clients, 100],
30     },
31 "bus" =>
32     {"bus_activ" => [\$bus_activ, "on"],
33     "bus_passwd" => [\$bus_passwd, ""],
34     "bus_ip" => [\$bus_ip, ""],
35     "bus_port" => [\$bus_port, "20080"],
36     },
37 );
39 ### START #####################################################################
42 # read configfile and import variables
43 &read_configfile();
45 # detect own ip and mac address
46 my ($server_ip, $server_mac_address) = &get_ip_and_mac(); 
47 if (not defined $server_ip) {
48     die "EXIT: ip address of $0 could not be detected";
49 }
50 &main::daemon_log("server ip address detected: $server_ip", 1);
51 &main::daemon_log("server mac address detected: $server_mac_address", 1);
53 # complete addresses
54 my $server_address = "$server_ip:$server_port";
55 my $bus_address = "$bus_ip:$bus_port";
57 # create general settings for this module
58 my $xml = new XML::Simple();
60 # open server socket
61 if($server_activ eq "on"){
62     &main::daemon_log(" ", 1);
63     $server = IO::Socket::INET->new(LocalPort => $server_port,
64             Type => SOCK_STREAM,
65             Reuse => 1,
66             Listen => 20,
67             ); 
68     if(not defined $server){
69         &main::daemon_log("cannot be a tcp server at $server_port : $@");
70     } else {
71         &main::daemon_log("start server: $server_address", 1);
72     }
73 }
75 # register at bus
76 if ($main::no_bus > 0) {
77     $bus_activ = "off"
78 }
79 if($bus_activ eq "on") {
80     &main::daemon_log(" ", 1);
81     &register_at_bus();
82 }
84 ### functions #################################################################
86 #sub get_module_tags {
87 #    
88 #    # lese config file aus dort gibt es eine section Basic
89 #    # dort stehen drei packettypen, für die sich das modul anmelden kann, gosa-admin-packages, 
90 #    #   server-packages, client-packages
91 #    my %tag_hash = (gosa_admin_packages => "yes", 
92 #                    server_packages => "yes", 
93 #                    client_packages => "yes",
94 #                    );
95 #    return \%tag_hash;
96 #}
99 sub get_module_info {
100     my @info = ($server_address,
101                 $server_passwd,
102                 $server,
103                 $server_activ,
104                 "socket",
105                 );
106     return \@info;
110 #===  FUNCTION  ================================================================
111 #         NAME:  read_configfile
112 #   PARAMETERS:  cfg_file - string -
113 #      RETURNS:  nothing
114 #  DESCRIPTION:  read cfg_file and set variables
115 #===============================================================================
116 sub read_configfile {
117     my $cfg;
118     if( defined( $main::cfg_file) && ( length($main::cfg_file) > 0 )) {
119         if( -r $main::cfg_file ) {
120             $cfg = Config::IniFiles->new( -file => $main::cfg_file );
121         } else {
122             print STDERR "Couldn't read config file!";
123         }
124     } else {
125         $cfg = Config::IniFiles->new() ;
126     }
127     foreach my $section (keys %cfg_defaults) {
128         foreach my $param (keys %{$cfg_defaults{ $section }}) {
129             my $pinfo = $cfg_defaults{ $section }{ $param };
130             ${@$pinfo[0]} = $cfg->val( $section, $param, @$pinfo[1] );
131         }
132     }
136 #===  FUNCTION  ================================================================
137 #         NAME:  get_ip_and_mac 
138 #   PARAMETERS:  nothing
139 #      RETURNS:  (ip, mac) 
140 #  DESCRIPTION:  executes /sbin/ifconfig and parses the output, the first occurence 
141 #                of a inet address is returned as well as the mac address in the line
142 #                above the inet address
143 #===============================================================================
144 sub get_ip_and_mac {
145     my $ip = "0.0.0.0.0"; # Defualt-IP
146     my $mac = "00:00:00:00:00:00";  # Default-MAC
147     my @ifconfig = qx(/sbin/ifconfig);
148     foreach(@ifconfig) {
149         if (/Hardware Adresse (\S{2}):(\S{2}):(\S{2}):(\S{2}):(\S{2}):(\S{2})/) {
150             $mac = "$1:$2:$3:$4:$5:$6";
151             next;
152         }
153         if (/inet Adresse:(\d+).(\d+).(\d+).(\d+)/) {
154             $ip = "$1.$2.$3.$4";
155             last;
156         }
157     }
158     return ($ip, $mac);
162 #===  FUNCTION  ================================================================
163 #         NAME:  open_socket
164 #   PARAMETERS:  PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
165 #                [PeerPort] string necessary if port not appended by PeerAddr
166 #      RETURNS:  socket IO::Socket::INET
167 #  DESCRIPTION:  open a socket to PeerAddr
168 #===============================================================================
169 sub open_socket {
170     my ($PeerAddr, $PeerPort) = @_ ;
171     if(defined($PeerPort)){
172         $PeerAddr = $PeerAddr.":".$PeerPort;
173     }
174     my $socket;
175     $socket = new IO::Socket::INET(PeerAddr => $PeerAddr ,
176             Porto => "tcp" ,
177             Type => SOCK_STREAM,
178             Timeout => 5,
179             );
180     if(not defined $socket) {
181         return;
182     }
183     &main::daemon_log("open_socket to: $PeerAddr", 7);
184     return $socket;
187 #===  FUNCTION  ================================================================
188 #         NAME:  register_at_bus
189 #   PARAMETERS:  nothing
190 #      RETURNS:  nothing
191 #  DESCRIPTION:  creates an entry in known_daemons and send a 'here_i_am' msg to bus
192 #===============================================================================
193 sub register_at_bus {
195     # create known_daemons entry
196     &main::create_known_daemon($bus_address);
197     &main::add_content2known_daemons(hostname=>$bus_address, status=>"register_at_bus", passwd=>$bus_passwd);
199     my $msg_hash = &create_xml_hash("here_i_am", $server_address, $bus_address);
200     my $answer = "";
201     $answer = &send_msg_hash2address($msg_hash, $bus_address);
202     if ($answer == 0) {
203         &main::daemon_log("register at bus: $bus_address", 1);
204     } else {
205         &main::daemon_log("unable to send 'register'-msg to bus: $bus_address", 1);
206     }
207     return;
210 #===  FUNCTION  ================================================================
211 #         NAME:  process_incoming_msg
212 #   PARAMETERS:  crypted_msg - string - incoming crypted message
213 #      RETURNS:  nothing
214 #  DESCRIPTION:  handels the proceeded distribution to the appropriated functions
215 #===============================================================================
216 sub process_incoming_msg {
217     my ($crypted_msg) = @_ ;
218     if(not defined $crypted_msg) {
219         &main::daemon_log("function 'process_incoming_msg': got no msg", 7);
220     }
222     $crypted_msg =~ /^([\s\S]*?)\.(\d{1,3}?)\.(\d{1,3}?)\.(\d{1,3}?)\.(\d{1,3}?)$/;
223     $crypted_msg = $1;
224     my $host = sprintf("%s.%s.%s.%s", $2, $3, $4, $5);
226     # collect addresses from possible incoming clients
227     my @valid_keys;
228     my @host_keys = keys %$main::known_daemons;
229     foreach my $host_key (@host_keys) {    
230         if($host_key =~ "^$host") {
231             push(@valid_keys, $host_key);
232         }
233     }
234     my @client_keys = keys %$main::known_clients;
235     foreach my $client_key (@client_keys) {
236         if($client_key =~ "^$host"){
237             push(@valid_keys, $client_key);
238         }
239     }
240     push(@valid_keys, $server_address);
241     
242     my $l = @valid_keys;
243     my $msg_hash;
244     my $msg_flag = 0;    
245     my $msg = "";
247     # determine the correct passwd for deciphering of the incoming msgs
248     foreach my $host_key (@valid_keys) {
249         eval{
250             &main::daemon_log("ServerPackage: host_key: $host_key", 7);
251             my $key_passwd;
252             if (exists $main::known_daemons->{$host_key}) {
253                 $key_passwd = $main::known_daemons->{$host_key}->{passwd};
254             } elsif (exists $main::known_clients->{$host_key}) {
255                 $key_passwd = $main::known_clients->{$host_key}->{passwd};
256             } elsif ($host_key eq $server_address) {
257                 $key_passwd = $server_passwd;
258             } 
259             &main::daemon_log("ServerPackage: key_passwd: $key_passwd", 7);
260             my $key_cipher = &create_ciphering($key_passwd);
261             $msg = &decrypt_msg($crypted_msg, $key_cipher);
262             &main::daemon_log("ServerPackages: decrypted msg: $msg", 7);
263             $msg_hash = $xml->XMLin($msg, ForceArray=>1);
264             #my $tmp = printf Dumper $msg_hash;
265             #&main::daemon_log("DEBUG: ServerPackages: xml hash: $tmp", 7);
266         };
267         if($@) {
268             &main::daemon_log("ServerPackage: key raise error: $@", 7);
269             $msg_flag += 1;
270         } else {
271             last;
272         }
273     } 
274     
275     if($msg_flag >= $l)  {
276         &main::daemon_log("WARNING: ServerPackage do not understand the message:", 5);
277         &main::daemon_log("$@", 7);
278         return;
279     }
281     # process incoming msg
282     my $header = @{$msg_hash->{header}}[0]; 
283     my $source = @{$msg_hash->{source}}[0];
285     &main::daemon_log("recieve '$header' at ServerPackages from $host", 1);
286 #    &main::daemon_log("ServerPackages: msg from host:", 5);
287 #    &main::daemon_log("\t$host", 5);
288 #    &main::daemon_log("ServerPackages: header from msg:", 5);
289 #    &main::daemon_log("\t$header", 5);
290     &main::daemon_log("ServerPackages: msg to process:", 5);
291     &main::daemon_log("\t$msg", 5);
293     my @targets = @{$msg_hash->{target}};
294     my $len_targets = @targets;
295     if ($len_targets == 0){     
296         &main::daemon_log("ERROR: ServerPackages: no target specified for msg $header", 1);
298     }  elsif ($len_targets == 1){
299         # we have only one target symbol
301         my $target = $targets[0];
302         &main::daemon_log("SeverPackages: msg is for:", 7);
303         &main::daemon_log("\t$target", 7);
305         if ($target eq $server_address) {
306             # msg is for server
307             if ($header eq 'new_passwd'){ &new_passwd($msg_hash)}
308             elsif ($header eq 'here_i_am') { &here_i_am($msg_hash)}
309             elsif ($header eq 'who_has') { &who_has($msg_hash) }
310             elsif ($header eq 'who_has_i_do') { &who_has_i_do($msg_hash)}
311             elsif ($header eq 'update_status') { &update_status($msg_hash) }
312             elsif ($header eq 'got_ping') { &got_ping($msg_hash)}
313             elsif ($header eq 'get_load') { &execute_actions($msg_hash)}
314             else { &main::daemon_log("ERROR: ServerPackages: no function assigned to this msg", 5) }
316         
317        } elsif ($target eq "*") {
318             # msg is for all clients
320             my @target_addresses = keys(%$main::known_clients);
321             foreach my $target_address (@target_addresses) {
322                 if ($target_address eq $source) { next; }
323                 $msg_hash->{target} = [$target_address];
324                 &send_msg_hash2address($msg_hash, $target_address);
325             }           
326         } else {
327             # msg is for one host
329             if (exists $main::known_clients->{$target}) {
330                 &send_msg_hash2address($msg_hash, $target);
331             } elsif (exists $main::known_daemons->{$target}) {
332                 # target is known
333                 &send_msg_hash2address($msg_hash, $target);
334             } else {
335                 # target is not known
336                 &main::daemon_log("ERROR: ServerPackages: target $target is not known neither in known_clients nor in known_daemons", 1);
337             }
338         }
339     }
341     return ;
345 #===  FUNCTION  ================================================================
346 #         NAME:  got_ping
347 #   PARAMETERS:  msg_hash - hash - hash from function create_xml_hash
348 #      RETURNS:  nothing
349 #  DESCRIPTION:  process this incoming message
350 #===============================================================================
351 sub got_ping {
352     my ($msg_hash) = @_;
353     
354     my $source = @{$msg_hash->{source}}[0];
355     my $target = @{$msg_hash->{target}}[0];
356     my $header = @{$msg_hash->{header}}[0];
357     
358     if(exists $main::known_daemons->{$source}) {
359         &main::add_content2known_daemons(hostname=>$source, status=>$header);
360     } else {
361         &main::add_content2known_clients(hostname=>$source, status=>$header);
362     }
363     
364     return;
368 #===  FUNCTION  ================================================================
369 #         NAME:  new_passwd
370 #   PARAMETERS:  msg_hash - ref - hash from function create_xml_hash
371 #      RETURNS:  nothing
372 #  DESCRIPTION:  process this incoming message
373 #===============================================================================
374 sub new_passwd {
375     my ($msg_hash) = @_;
377     my $source = @{$msg_hash->{source}}[0];
378     my $passwd = @{$msg_hash->{new_passwd}}[0];
380     if (exists $main::known_daemons->{$source}) {
381         &main::add_content2known_daemons(hostname=>$source, status=>"new_passwd", passwd=>$passwd);
382         my $hash = &create_xml_hash("confirm_new_passwd", $server_address, $source);
383         &send_msg_hash2address($hash, $source);
385     } elsif (exists $main::known_clients->{$source}) {
386         &main::add_content2known_clients(hostname=>$source, status=>"new_passwd", passwd=>$passwd);
388     } else {
389         &main::daemon_log("ERROR: $source not known, neither in known_daemons nor in known_clients", 1)   
390     }
392     return;
396 #===  FUNCTION  ================================================================
397 #         NAME:  here_i_am
398 #   PARAMETERS:  msg_hash - hash - hash from function create_xml_hash
399 #      RETURNS:  nothing
400 #  DESCRIPTION:  process this incoming message
401 #===============================================================================
402 sub here_i_am {
403     my ($msg_hash) = @_;
405     my $source = @{$msg_hash->{source}}[0];
406     my $mac_address = @{$msg_hash->{mac_address}}[0];
407     my $out_hash;
409     # number of known clients
410     my $nu_clients = keys %$main::known_clients;
412     # check wether client address or mac address is already known
413     if (exists $main::known_clients->{$source}) {
414         &main::daemon_log("WARNING: $source is already known as a client", 1);
415         &main::daemon_log("WARNING: values for $source are being overwritten", 1);   
416         $nu_clients --;
417     }
419     # number of actual activ clients
420     my $act_nu_clients = $nu_clients;
422     &main::daemon_log("number of actual activ clients: $act_nu_clients", 5);
423     &main::daemon_log("number of maximal allowed clients: $max_clients", 5);
425     if($max_clients <= $act_nu_clients) {
426         my $out_hash = &create_xml_hash("denied", $server_address, $source);
427         &add_content2xml_hash($out_hash, "denied", "I_cannot_take_any_more_clients!");
428         my $passwd = @{$msg_hash->{new_passwd}}[0]; 
429         &send_msg_hash2address($out_hash, $source, $passwd);
430         return;
431     }
432     
433     # new client accepted
434     my $new_passwd = @{$msg_hash->{new_passwd}}[0];
436     # create known_daemons entry
437     my $events = @{$msg_hash->{events}}[0];
438     &main::create_known_client($source);
439     &main::add_content2known_clients(hostname=>$source, events=>$events, mac_address=>$mac_address, 
440                                 status=>"registered", passwd=>$new_passwd);
442     # return acknowledgement to client
443     $out_hash = &create_xml_hash("registered", $server_address, $source);
444     &send_msg_hash2address($out_hash, $source);
446     # notify registered client to bus
447     $out_hash = &create_xml_hash("new_client", $server_address, $bus_address, $source);
448     #&main::send_msg_hash2bus($out_hash);
449     &send_msg_hash2address($out_hash, $bus_address);
451     # give the new client his ldap config
452     &new_ldap_config($source);
454     return;
458 #===  FUNCTION  ================================================================
459 #         NAME:  who_has
460 #   PARAMETERS:  msg_hash - hash - hash from function create_xml_hash
461 #      RETURNS:  nothing 
462 #  DESCRIPTION:  process this incoming message
463 #===============================================================================
464 sub who_has {
465     my ($msg_hash) = @_ ;
466     
467     # what is your search pattern
468     my $search_pattern = @{$msg_hash->{who_has}}[0];
469     my $search_element = @{$msg_hash->{$search_pattern}}[0];
470     &main::daemon_log("who_has-msg looking for $search_pattern $search_element", 7);
472     # scanning known_clients for search_pattern
473     my @host_addresses = keys %$main::known_clients;
474     my $known_clients_entries = length @host_addresses;
475     my $host_address;
476     foreach my $host (@host_addresses) {
477         my $client_element = $main::known_clients->{$host}->{$search_pattern};
478         if ($search_element eq $client_element) {
479             $host_address = $host;
480             last;
481         }
482     }
483         
484     # search was successful
485     if (defined $host_address) {
486         my $source = @{$msg_hash->{source}}[0];
487         my $out_msg = &create_xml_hash("who_has_i_do", $server_address, $source, "mac_address");
488         &add_content2xml_hash($out_msg, "mac_address", $search_element);
489         &send_msg_hash2address($out_msg, $bus_address);
490     }
491     return;
495 sub who_has_i_do {
496     my ($msg_hash) = @_ ;
497     my $header = @{$msg_hash->{header}}[0];
498     my $source = @{$msg_hash->{source}}[0];
499     my $search_param = @{$msg_hash->{$header}}[0];
500     my $search_value = @{$msg_hash->{$search_param}}[0];
501     print "\ngot msg $header:\nserver $source has client with $search_param $search_value\n";
505 #===  FUNCTION  ================================================================
506 #         NAME:  new_ldap_config
507 #   PARAMETERS:  address - string - ip address and port of a host
508 #      RETURNS:  nothing
509 #  DESCRIPTION:  send to address the ldap configuration found for dn gotoLdapServer
510 #===============================================================================
511 sub new_ldap_config {
512     my ($address) = @_ ;
513     
514     if (not exists $main::known_clients->{$address}) {
515         &main::daemon_log("ERROR: $address does not exist in known_clients, cannot send him his ldap config", 1);
516         return;
517     }
518     
519     my $mac_address = $main::known_clients->{$address}->{"mac_address"};
520     if (not defined $mac_address) {
521         &main::daemon_log("ERROR: no mac address found for client $address", 1);
522         return;
523     }
525     # fetch dn
526     my $goHard_cmd = "ldapsearch -x '(&(objectClass=goHard)(macAddress=00:11:22:33:44:57))' dn gotoLdapServer";
527     my $dn;
528     my @gotoLdapServer;
529     open (PIPE, "$goHard_cmd 2>&1 |");
530 #    my $rbits = "";
531 #    vec($rbits, fileno PIPE, 1) = 1;
532 #    my $rout;
533 #    my $nf = select($rout=$rbits, undef, undef, $ldap_timeout);
534     while(<PIPE>) {
535         chomp $_;
536         # If it's a comment, goto next
537         if ($_ =~ m/^[#]/) { next;}
538         if ($_ =~ m/^dn: ([\S]+?)$/) {
539             $dn = $1;
540         } elsif ($_ =~ m/^gotoLdapServer: ([\S]+?)$/) {
541             push(@gotoLdapServer, $1);
542         }
543     }
544     close(PIPE);
545     
546     # no dn found
547     if (not defined $dn) {
548         &main::daemon_log("ERROR: no dn arose from command: $goHard_cmd", 1);
549         return;
550     }
551     
552     # no gotoLdapServer found
553     my $gosaGroupOfNames_cmd = "ldapsearch -x '(&(objectClass=gosaGroupOfNames)(member=$dn))' gotoLdapServer";
554     if (@gotoLdapServer == 0) {
555         open (PIPE, "$gosaGroupOfNames_cmd 2>&1 |");
556         while(<PIPE>) {
557             chomp $_;
558             if ($_ =~ m/^[#]/) { next; }
559             if ($_ =~ m/^gotoLdapServer: ([\S]+?)$/) {
560                 push(@gotoLdapServer, $1);
561             }
562         }
563         close(PIPE);
564     }
566     # still no gotoLdapServer found
567     if (@gotoLdapServer == 0) {
568         &main::daemon_log("ERROR: cannot find gotoLdapServer entry in command: $gosaGroupOfNames_cmd", 1);
569         return;
570     }
572     # sort @gotoLdapServer and then split of ranking
573     my @sorted_gotoLdapServer = sort(@gotoLdapServer);
574     @gotoLdapServer = reverse(@sorted_gotoLdapServer);
575     foreach (@gotoLdapServer) {
576         $_ =~ s/^\d://;
577     }
579     my $t = join(" ", @gotoLdapServer);
580  
581     my $out_hash = &create_xml_hash("new_ldap_config", $server_address, $address);
582     map(&add_content2xml_hash($out_hash, "new_ldap_config", $_), @gotoLdapServer);
583     &send_msg_hash2address($out_hash, $address);
585     return;
589 #===  FUNCTION  ================================================================
590 #         NAME:  execute_actions
591 #   PARAMETERS:  msg_hash - hash - hash from function create_xml_hash
592 #      RETURNS:  nothing
593 #  DESCRIPTION:  invokes the script specified in msg_hash which is located under
594 #                /etc/gosad/actions
595 #===============================================================================
596 sub execute_actions {
597     my ($msg_hash) = @_ ;
598     my $configdir= '/etc/gosad/actions/';
599     my $result;
601     my $header = @{$msg_hash->{header}}[0];
602     my $source = @{$msg_hash->{source}}[0];
603     my $target = @{$msg_hash->{target}}[0];
604  
605     if((not defined $source)
606             && (not defined $target)
607             && (not defined $header)) {
608         &main::daemon_log("ERROR: Entries missing in XML msg for gosad actions under /etc/gosad/actions");
609     } else {
610         my $parameters="";
611         my @params = @{$msg_hash->{$header}};
612         my $params = join(", ", @params);
613         &main::daemon_log("execute_actions: got parameters: $params", 5);
615         if (@params) {
616             foreach my $param (@params) {
617                 my $param_value = (&get_content_from_xml_hash($msg_hash, $param))[0];
618                 &main::daemon_log("execute_actions: parameter -> value: $param -> $param_value", 7);
619                 $parameters.= " ".$param_value;
620             }
621         }
623         my $cmd= $configdir.$header."$parameters";
624         &main::daemon_log("execute_actions: executing cmd: $cmd", 7);
625         $result= "";
626         open(PIPE, "$cmd 2>&1 |");
627         while(<PIPE>) {
628             $result.=$_;
629         }
630         close(PIPE);
631     }
633     # process the event result
636     return;
640 1;