Code

Not needed
[gosa.git] / gosa-si / modules / GosaPackages.pm
1 package GosaPackages;
3 use Exporter;
4 @ISA = ("Exporter");
6 use strict;
7 use warnings;
8 use GOSA::GosaSupportDaemon;
9 use IO::Socket::INET;
10 use XML::Simple;
11 use File::Spec;
12 use Data::Dumper;
13 use GOSA::DBsqlite;
14 use MIME::Base64;
16 my $op_hash = {
17     'eq' => '=',
18     'ne' => '!=',
19     'ge' => '>=',
20     'gt' => '>',
21     'le' => '<=',
22     'lt' => '<',
23 };
25 BEGIN{}
26 END{}
28 my ($server_activ, $server_ip, $server_mac_address, $server_port, $server_passwd, $max_clients, $server_event_dir);
29 my ($bus_activ, $bus_passwd, $bus_ip, $bus_port);
30 my ($gosa_activ, $gosa_ip, $gosa_mac_address, $gosa_port, $gosa_passwd, $network_interface);
31 my ($job_queue_timeout, $job_queue_file_name);
33 my $gosa_server;
35 my %cfg_defaults = 
36 ("general" =>
37     {"job_queue_file_name" => [\$job_queue_file_name, '/var/lib/gosa-si/jobs.db'],
38     },
39 "server" =>
40     {"server_activ" => [\$server_activ, "on"],
41     "server_ip" => [\$server_ip, "0.0.0.0"],
42     "server_port" => [\$server_port, "20081"],
43     "server_passwd" => [\$server_passwd, ""],
44     "max_clients" => [\$max_clients, 100],
45     "server_event_dir" => [\$server_event_dir, '/usr/lib/gosa-si/server/events'],
46     },
47 "bus" =>
48     {"bus_activ" => [\$bus_activ, "on"],
49     "bus_passwd" => [\$bus_passwd, ""],
50     "bus_ip" => [\$bus_ip, "0.0.0.0"],
51     "bus_port" => [\$bus_port, "20080"],
52     },
53 "gosa" =>
54     {"gosa_activ" => [\$gosa_activ, "on"],
55     "gosa_ip" => [\$gosa_ip, "0.0.0.0"],
56     "gosa_port" => [\$gosa_port, "20082"],
57     "gosa_passwd" => [\$gosa_passwd, "none"],
58     },
59 );
60  
62 ## START ##########################
64 # read configfile and import variables
65 &read_configfile();
66 $network_interface= &get_interface_for_ip($server_ip);
67 $gosa_mac_address= &get_mac($network_interface);
69 # complete addresses
70 my $server_address = "$server_ip:$server_port";
71 my $bus_address = "$bus_ip:$bus_port";
72 my $gosa_address = "$gosa_ip:$gosa_port";
74 # create general settings for this module
75 my $gosa_cipher = &create_ciphering($gosa_passwd);
76 my $xml = new XML::Simple();
79 ## FUNCTIONS #################################################################
81 sub get_module_info {
82     my @info = ($gosa_address,
83                 $gosa_passwd,
84                 $gosa_server,
85                 $gosa_activ,
86                 "socket",
87                 );
88     return \@info;
89 }
92 #===  FUNCTION  ================================================================
93 #         NAME:  read_configfile
94 #   PARAMETERS:  cfg_file - string -
95 #      RETURNS:  nothing
96 #  DESCRIPTION:  read cfg_file and set variables
97 #===============================================================================
98 sub read_configfile {
99     my $cfg;
100     if( defined( $main::cfg_file) && ( length($main::cfg_file) > 0 )) {
101         if( -r $main::cfg_file ) {
102             $cfg = Config::IniFiles->new( -file => $main::cfg_file );
103         } else {
104             print STDERR "Couldn't read config file!";
105         }
106     } else {
107         $cfg = Config::IniFiles->new() ;
108     }
109     foreach my $section (keys %cfg_defaults) {
110         foreach my $param (keys %{$cfg_defaults{ $section }}) {
111             my $pinfo = $cfg_defaults{ $section }{ $param };
112             ${@$pinfo[0]} = $cfg->val( $section, $param, @$pinfo[1] );
113         }
114     }
117 #===  FUNCTION  ================================================================
118 #         NAME:  get_interface_for_ip
119 #   PARAMETERS:  ip address (i.e. 192.168.0.1)
120 #      RETURNS:  array: list of interfaces if ip=0.0.0.0, matching interface if found, undef else
121 #  DESCRIPTION:  Uses proc fs (/proc/net/dev) to get list of interfaces.
122 #===============================================================================
123 sub get_interface_for_ip {
124         my $result;
125         my $ip= shift;
126         if ($ip && length($ip) > 0) {
127                 my @ifs= &get_interfaces();
128                 if($ip eq "0.0.0.0") {
129                         $result = "all";
130                 } else {
131                         foreach (@ifs) {
132                                 my $if=$_;
133                                 if(get_ip($if) eq $ip) {
134                                         $result = $if;
135                                 }
136                         }       
137                 }
138         }       
139         return $result;
142 #===  FUNCTION  ================================================================
143 #         NAME:  get_interfaces 
144 #   PARAMETERS:  none
145 #      RETURNS:  (list of interfaces) 
146 #  DESCRIPTION:  Uses proc fs (/proc/net/dev) to get list of interfaces.
147 #===============================================================================
148 sub get_interfaces {
149         my @result;
150         my $PROC_NET_DEV= ('/proc/net/dev');
152         open(PROC_NET_DEV, "<$PROC_NET_DEV")
153                 or die "Could not open $PROC_NET_DEV";
155         my @ifs = <PROC_NET_DEV>;
157         close(PROC_NET_DEV);
159         # Eat first two line
160         shift @ifs;
161         shift @ifs;
163         chomp @ifs;
164         foreach my $line(@ifs) {
165                 my $if= (split /:/, $line)[0];
166                 $if =~ s/^\s+//;
167                 push @result, $if;
168         }
170         return @result;
173 #===  FUNCTION  ================================================================
174 #         NAME:  get_mac 
175 #   PARAMETERS:  interface name (i.e. eth0)
176 #      RETURNS:  (mac address) 
177 #  DESCRIPTION:  Uses ioctl to get mac address directly from system.
178 #===============================================================================
179 sub get_mac {
180         my $ifreq= shift;
181         my $result;
182         if ($ifreq && length($ifreq) > 0) { 
183                 if($ifreq eq "all") {
184                         $result = "00:00:00:00:00:00";
185                 } else {
186                         my $SIOCGIFHWADDR= 0x8927;     # man 2 ioctl_list
188                         # A configured MAC Address should always override a guessed value
189                         if ($gosa_mac_address and length($gosa_mac_address) > 0) {
190                                 $result= $gosa_mac_address;
191                         }
193                         socket SOCKET, PF_INET, SOCK_DGRAM, getprotobyname('ip')
194                                 or die "socket: $!";
196                         if(ioctl SOCKET, $SIOCGIFHWADDR, $ifreq) {
197                                 my ($if, $mac)= unpack 'h36 H12', $ifreq;
199                                 if (length($mac) > 0) {
200                                         $mac=~ m/^([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])$/;
201                                         $mac= sprintf("%s:%s:%s:%s:%s:%s", $1, $2, $3, $4, $5, $6);
202                                         $result = $mac;
203                                 }
204                         }
205                 }
206         }
207         return $result;
210 #===  FUNCTION  ================================================================
211 #         NAME:  get_ip 
212 #   PARAMETERS:  interface name (i.e. eth0)
213 #      RETURNS:  (ip address) 
214 #  DESCRIPTION:  Uses ioctl to get ip address directly from system.
215 #===============================================================================
216 sub get_ip {
217         my $ifreq= shift;
218         my $result= "";
219         my $SIOCGIFADDR= 0x8915;       # man 2 ioctl_list
220         my $proto= getprotobyname('ip');
222         socket SOCKET, PF_INET, SOCK_DGRAM, $proto
223                 or die "socket: $!";
225         if(ioctl SOCKET, $SIOCGIFADDR, $ifreq) {
226                 my ($if, $sin)    = unpack 'a16 a16', $ifreq;
227                 my ($port, $addr) = sockaddr_in $sin;
228                 my $ip            = inet_ntoa $addr;
230                 if ($ip && length($ip) > 0) {
231                         $result = $ip;
232                 }
233         }
235         return $result;
238 #===  FUNCTION  ================================================================
239 #         NAME:  open_socket
240 #   PARAMETERS:  PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
241 #                [PeerPort] string necessary if port not appended by PeerAddr
242 #      RETURNS:  socket IO::Socket::INET
243 #  DESCRIPTION:  open a socket to PeerAddr
244 #===============================================================================
245 sub open_socket {
246     my ($PeerAddr, $PeerPort) = @_ ;
247     if(defined($PeerPort)){
248         $PeerAddr = $PeerAddr.":".$PeerPort;
249     }
250     my $socket;
251     $socket = new IO::Socket::INET(PeerAddr => $PeerAddr ,
252             Porto => "tcp" ,
253             Type => SOCK_STREAM,
254             Timeout => 5,
255             );
256     if(not defined $socket) {
257         return;
258     }
259     &main::daemon_log("open_socket to: $PeerAddr", 7);
260     return $socket;
264 #===  FUNCTION  ================================================================
265 #         NAME:  process_incoming_msg
266 #   PARAMETERS:  crypted_msg - string - incoming crypted message
267 #      RETURNS:  nothing
268 #  DESCRIPTION:  handels the proceeded distribution to the appropriated functions
269 #===============================================================================
270 sub process_incoming_msg {
271     my ($crypted_msg) = @_ ;
272         &main::daemon_log("Got message $crypted_msg", 8);
273         if( (not(defined($crypted_msg))) || (length($crypted_msg) <= 0)) {
274         &main::daemon_log("function 'process_incoming_msg': got no msg", 7);
275         return;
276     }
278     $crypted_msg =~ /^([\s\S]*?)\.(\d{1,3}?)\.(\d{1,3}?)\.(\d{1,3}?)\.(\d{1,3}?)$/;
279     $crypted_msg = $1;
280         my $host = sprintf("%s.%s.%s.%s", $2, $3, $4, $5);
281  
282     # collect addresses from possible incoming clients
283     # only gosa is allowd as incoming client
284     &main::daemon_log("GosaPackages: host_key: $host", 7);
285     &main::daemon_log("GosaPackages: key_passwd: $gosa_passwd", 7);
287     $gosa_cipher = &create_ciphering($gosa_passwd);
289     # determine the correct passwd for deciphering of the incoming msgs
290     my $msg = "";
291     my $msg_hash;
292     eval{
293         $msg = &decrypt_msg($crypted_msg, $gosa_cipher);
294         &main::daemon_log("GosaPackages: decrypted_msg: \n$msg", 8);
296         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
297     };
298     if($@) {
299         &main::daemon_log("WARNING: GosaPackages do not understand the message:", 5);
300         &main::daemon_log("$@", 7);
301         return;
302     }
304     my $header = @{$msg_hash->{header}}[0];
305     
306     &main::daemon_log("GosaPackages: receive '$header' from $host", 1);
307     
308     my $out_msg;
309     if ($header =~ /^job_/) {
310         $out_msg = &process_job_msg($msg, $msg_hash);
311     } elsif ($header =~ /^gosa_/) {
312         $out_msg = &process_gosa_msg($msg, $header);
313     } else {
314         &main::daemon_log("ERROR: $header is not a valid GosaPackage-header, need a 'job_' or a 'gosa_' prefix");
315     }
316         
317     if (not defined $out_msg) {
318         return;
319     }
321     if ($out_msg =~ /<jobdb_id>(\d*?)<\/jobdb_id>/) {
322         my $job_id = $1;
323         my $sql = "UPDATE '".$main::job_queue_table_name.
324             "' SET status='done', result='".$out_msg.
325             "' WHERE id='$job_id'";
326         my $res = $main::job_db->exec_statement($sql);
327         return;
329     } else {
331         my $out_cipher = &create_ciphering($gosa_passwd);
332         $out_msg = &encrypt_msg($out_msg, $out_cipher);
334         return $out_msg;
335         }
339 sub process_gosa_msg {
340     my ($msg, $header) = @_ ;
341     my $out_msg;
342     $header =~ s/gosa_//;
344     # decide wether msg is a core function or a event handler
345     if ( $header eq 'query_jobdb') { $out_msg = &query_jobdb }
346     elsif ($header eq 'delete_jobdb_entry') { $out_msg = &delete_jobdb_entry }
347     elsif ($header eq 'clear_jobdb') { $out_msg = &clear_jobdb }
348     elsif ($header eq 'update_status_jobdb_entry' ) { $out_msg = &update_status_jobdb_entry }
349     elsif ($header eq 'update_timestamp_jobdb_entry' ) { $out_msg = &update_timestamp_jobdb_entry }
350     else {
351         # msg could not be assigned to core function
352         # fetch all available eventhandler under $server_event_dir
353         opendir (DIR, $server_event_dir) or &main::daemon_log("ERROR cannot open $server_event_dir: $!\n", 1) and return;
354         while (defined (my $file = readdir (DIR))) {
355             if (not $file eq $header) {
356                 next;
357             }
358             # try to deliver incoming msg to eventhandler
359             my $cmd = File::Spec->join($server_event_dir, $header)." '$msg'";
360             &main::daemon_log("GosaPackages: execute event_handler $header", 3);
361             &main::daemon_log("GosaPackages: cmd: $cmd", 7);
363             $out_msg = "";
364             open(PIPE, "$cmd 2>&1 |");
365             while(<PIPE>) {
366                 $out_msg.=$_;
367             }
368             close(PIPE);
369             &main::daemon_log("GosaPackages: answer of cmd: $out_msg", 5);
370             last;
371         }
372     }
374     # if delivery not possible raise error and return 
375     if (not defined $out_msg) {
376         &main::daemon_log("ERROR: GosaPackages: no event handler or core function defined for $header", 1);
377     } elsif ($out_msg eq "") {
378         &main::daemon_log("ERROR: GosaPackages got not answer from event_handler $header", 1);
379     }
380     return $out_msg;
381     
385 sub process_job_msg {
386     my ($msg, $msg_hash)= @_ ;    
388     my $header = @{$msg_hash->{header}}[0];
389     $header =~ s/job_//;
390     
391     # check wether mac address is already known in known_daemons or known_clients
392     my $target = 'none';
394     # add job to job queue
395     my $func_dic = {table=>$main::job_queue_table_name, 
396                     primkey=>'id',
397                     timestamp=>@{$msg_hash->{timestamp}}[0],
398                     status=>'waiting', 
399                     result=>'none',
400                     headertag=>$header, 
401                     targettag=>$target,
402                     xmlmessage=>$msg,
403                     macaddress=>@{$msg_hash->{mac}}[0],
404                     };
405     my $res = $main::job_db->add_dbentry($func_dic);
406     if (not $res == 0) {
407         &main::daemon_log("ERROR: GosaPackages: process_job_msg: $res", 1);
408     }
409     
410     &main::daemon_log("GosaPackages: $header job successfully added to job queue", 3);
411     return "<xml><answer1>$res</answer1></xml>";
416 sub db_res_2_xml {
417     my ($db_res) = @_ ;
419     my $xml = "<xml>";
421     while ( my ($hit, $hash) = each %{ $db_res } ) {
422         $xml .= "\n<answer$hit>";
424         while ( my ($column_name, $column_value) = each %{$hash} ) {
425             $xml .= "<$column_name>";
426             my $xml_content;
427             if( $column_name eq "xmlmessage" ) {
428                 $xml_content = &encode_base64($column_value);
429             } else {
430                 $xml_content = $column_value;
431             }
432             $xml .= $xml_content;
433             $xml .= "</$column_name>"; 
434         }
436         $xml .= "</answer$hit>";
437     }
439     $xml .= "</xml>";
440     return $xml;
444 ## CORE FUNCTIONS ############################################################
446 sub get_where_statement {
447     my ($msg, $msg_hash)= @_;
448     my $error= 0;
449     
450     my $clause_str= "";
451     if( not exists @{$msg_hash->{'where'}}[0]->{'clause'} ) { $error++; };
452     if( $error == 0 ) {
453         my @clause_l;
454         my @where = @{@{$msg_hash->{'where'}}[0]->{'clause'}};
455         foreach my $clause (@where) {
456             my $connector = $clause->{'connector'}[0];
457             if( not defined $connector ) { $connector = "AND"; }
458             $connector = uc($connector);
459             delete($clause->{'connector'});
461             my @phrase_l ;
462             foreach my $phrase (@{$clause->{'phrase'}}) {
463                 my $operator = "=";
464                 if( exists $phrase->{'operator'} ) {
465                     my $op = $op_hash->{$phrase->{'operator'}[0]};
466                     if( not defined $op ) {
467                         &main::daemon_log("Can not translate operator '$operator' in where ".
468                                 "statement to sql valid syntax. Please use 'eq', ".
469                                 "'ne', 'ge', 'gt', 'le', 'lt' in xml message\n", 1);
470                         &main::daemon_log($msg, 8);
471                         $op = "=";
472                     }
473                     $operator = $op;
474                     delete($phrase->{'operator'});
475                 }
477                 my @xml_tags = keys %{$phrase};
478                 my $tag = $xml_tags[0];
479                 my $val = $phrase->{$tag}[0];
480                 push(@phrase_l, "$tag$operator'$val'");
481             }
482             my $clause_str .= join(" $connector ", @phrase_l);
483             push(@clause_l, $clause_str);
484         }
486         if( not 0 == @clause_l ) {
487             $clause_str = join(" AND ", @clause_l);
488             $clause_str = "WHERE $clause_str ";
489         }
490     }
492     return $clause_str;
495 sub get_select_statement {
496     my ($msg, $msg_hash)= @_;
497     my $select = "*";
498     if( exists $msg_hash->{'select'} ) {
499         my $select_l = \@{$msg_hash->{'select'}};
500         $select = join(' AND ', @{$select_l});
501     }
502     return $select;
506 sub get_update_statement {
507     my ($msg, $msg_hash) = @_;
508     my $error= 0;
509     my $update_str= "";
510     my @update_l; 
512     if( not exists $msg_hash->{'update'} ) { $error++; };
514     if( $error == 0 ) {
515         my $update= @{$msg_hash->{'update'}}[0];
516         while( my ($tag, $val) = each %{$update} ) {
517             my $val= @{$update->{$tag}}[0];
518             push(@update_l, "$tag='$val'");
519         }
520         if( 0 == @update_l ) { $error++; };   
521     }
523     if( $error == 0 ) { 
524         $update_str= join(', ', @update_l);
525         $update_str= "SET $update_str ";
526     }
528     return $update_str;
531 sub query_jobdb {
532     my ($msg) = @_;
533     my $msg_hash = &transform_msg2hash($msg);
535     # prepare query sql statement
536     my $select= &get_select_statement($msg, $msg_hash);
537     my $table= $main::job_queue_table_name;
538     my $where= &get_where_statement($msg, $msg_hash);
539     my $sql_statement= "SELECT $select FROM $table $where";
541     # execute db query   
542     my $res_hash = $main::job_db->select_dbentry($sql_statement);
543     my $out_xml = &db_res_2_xml($res_hash);
544     
545     return $out_xml;
548 sub delete_jobdb_entry {
549     my ($msg) = @_ ;
550     my $msg_hash = &transform_msg2hash($msg);
551     
552     # prepare query sql statement
553     my $table= $main::job_queue_table_name;
554     my $where= &get_where_statement($msg, $msg_hash);
555     my $sql_statement = "DELETE FROM $table $where";
556     
557     # execute db query
558     my $db_res = $main::job_db->del_dbentry($sql_statement);
560     my $res;
561     if( $db_res > 0 ) { 
562         $res = 0 ;
563     } else {
564         $res = 1;
565     }
567     # prepare xml answer
568     my $out_xml = "<xml><answer1>$res</answer1></xml>";
569     return $out_xml;
574 sub clear_jobdb {
575     my ($msg) = @_ ;
576     my $msg_hash = &transform_msg2hash($msg);
577     my $error= 0;
578     my $out_xml= "<xml><answer1>1</answer1></xml>";
579  
580     my $table= $main::job_queue_table_name;
581     
582     my $sql_statement = "DELETE FROM $table";
583     my $db_res = $main::job_db->del_dbentry($sql_statement);
584     if( not $db_res > 0 ) { $error++; };
585     
586     if( $error == 0 ) {
587         $out_xml = "<xml><answer1>0</answer1></xml>";
588     }
589    
590     return $out_xml;
594 sub update_status_jobdb_entry {
595     my ($msg) = @_ ;
596     my $msg_hash = &transform_msg2hash($msg);
597     my $error= 0;
598     my $out_xml= "<xml><answer1>1</answer1></xml>";
600     my @len_hash = keys %{$msg_hash};
601     if( 0 == @len_hash) {  $error++; };
602     
603     # prepare query sql statement
604     if( $error == 0) {
605         my $table= $main::job_queue_table_name;
606         my $where= &get_where_statement($msg, $msg_hash);
607         my $update= &get_update_statement($msg, $msg_hash);
609         my $sql_statement = "UPDATE $table $update $where";
611         # execute db query
612         my $db_res = $main::job_db->update_dbentry($sql_statement);
614         # check success of db update
615         if( not $db_res > 0 ) { $error++; };
616     }
618     if( $error == 0) {
619         $out_xml = "<xml><answer1>0</answer1></xml>";
620     }
622     return $out_xml;
625 #sub update_timestamp_jobdb_entry {
626 #    my ($msg) = @_ ;
627 #    my $msg_hash = &transform_msg2hash($msg);
628 #    
629 #    # prepare query sql statement
630 #    my $update_hash = {table=>$main::job_queue_table_name };
631 #    if( exists $msg_hash->{where} ) {
632 #        $update_hash->{where} = $msg_hash->{where};
633 #    } else {
634 #        $update_hash->{where} = [];
635 #    }
637 #    if( not exists $msg_hash->{update}[0]->{timestamp} ) {
638 #        return "<xml><answer1>1</answer1></xml>";
639 #    }
641 #    $update_hash->{update} = [ { timestamp=>$msg_hash->{update}[0]->{timestamp} } ];
643 #    # execute db query
644 #    my $db_res = $main::job_db->update_dbentry($update_hash);
646 #    # transform db answer to error returnment
647 #    my $res;
648 #    if( $db_res > 0 ) { 
649 #        $res = 0 ;
650 #    } else {
651 #        $res = 1;
652 #    }
654 #    # prepare xml answer
655 #    my $out_xml = "<xml><answer1>$res</answer1></xml>";
656 #    return $out_xml;
658 #}
661 1;