Code

- Added library Net::Arp and corrected function to make it work
[gosa.git] / gosa-si / modules / GosaSupportDaemon.pm
1 package GOSA::GosaSupportDaemon;
3 use Exporter;
4 @ISA = qw(Exporter);
5 my @functions = (
6     "create_passwd",
7     "create_xml_hash",
8           "createXmlHash",
9           "myXmlHashToString",
10     "get_content_from_xml_hash",
11     "add_content2xml_hash",
12     "create_xml_string",
13     "transform_msg2hash",
14     "get_time",
15     "get_utc_time",
16     "build_msg",
17     "db_res2xml",
18     "db_res2si_msg",
19     "get_where_statement",
20     "get_select_statement",
21     "get_update_statement",
22     "get_limit_statement",
23     "get_orderby_statement",
24     "get_dns_domains",
25     "get_server_addresses",
26     "get_logged_in_users",
27     "import_events",
28     "del_doubles",
29     "get_ip",
30     "get_interface_for_ip",
31     "get_interfaces",
32     "get_mac_for_interface",
33     "get_local_ip_for_remote_ip",
34     "is_local",
35     "run_as",
36     "inform_all_other_si_server",
37     "read_configfile",
38     "check_opsi_res",
39     "calc_timestamp",
40     "opsi_callobj2string",
41     ); 
42 @EXPORT = @functions;
43 use strict;
44 use warnings;
45 use IO::Socket::INET;
46 use Crypt::Rijndael;
47 use Digest::MD5  qw(md5 md5_hex md5_base64);
48 use MIME::Base64;
49 use XML::Quote qw(:all);
50 use XML::Simple;
51 use Data::Dumper;
52 use Net::DNS;
53 use Net::ARP;
54 use DateTime;
56 my $op_hash = {
57     'eq' => '=',
58     'ne' => '!=',
59     'ge' => '>=',
60     'gt' => '>',
61     'le' => '<=',
62     'lt' => '<',
63     'like' => ' LIKE ',
64 };
67 BEGIN {}
69 END {}
71 ### Start ######################################################################
73 our $xml = new XML::Simple();
75 sub daemon_log {
76     my ($msg, $level) = @_ ;
77     &main::daemon_log($msg, $level);
78     return;
79 }
82 sub create_passwd {
83     my $new_passwd = "";
84     for(my $i=0; $i<31; $i++) {
85         $new_passwd .= ("a".."z","A".."Z",0..9)[int(rand(62))]
86     }
88     return $new_passwd;
89 }
92 sub del_doubles { 
93     my %all; 
94     $all{$_}=0 for @_; 
95     return (keys %all); 
96 }
99 #===  FUNCTION  ================================================================
100 #         NAME:  create_xml_hash
101 #   PARAMETERS:  header - string - message header (required)
102 #                source - string - where the message come from (required)
103 #                target - string - where the message should go to (required)
104 #                [header_value] - string - something usefull (optional)
105 #      RETURNS:  hash - hash - nomen est omen
106 #  DESCRIPTION:  creates a key-value hash, all values are stored in a array
107 #===============================================================================
108 sub create_xml_hash {
109     my ($header, $source, $target, $header_value) = @_;
110     my $hash = {
111             header => [$header],
112             source => [$source],
113             target => [$target],
114             $header => [$header_value],
115     };
116     return $hash
119 sub createXmlHash {
120         my ($header, $source, $target) = @_;
121         return { header=>$header, source=>$source, target=>$target};
124 sub _transformHashToString {
125         my ($hash) = @_;
126         my $s = "";
128         while (my ($tag, $content) = each(%$hash)) {
130                 if (ref $content eq "HASH") {
131                         $s .= "<$tag>".&_transformHashToString($content)."</$tag>";
132                 } elsif ( ref $content eq "ARRAY") {
133                         $s .= &_transformArrayToString($tag, $content);
134                 } else {
135                         $content = defined $content ? $content : "";
136                         $s .= "<$tag>".&xml_quote($content)."</$tag>";
137                 }
138         }
139         return $s;
142 sub _transformArrayToString {
143         my ($tag, $contentArray) = @_;
144         my $s = "";
145         foreach my $content (@$contentArray) {
146                 if (ref $content eq "HASH") {
147                         $s .= "<$tag>".&_transformHashToString($content)."</$tag>";
148                 } else {
149                         $content = defined $content ? $content : "";
150                         $s .= "<$tag>".&xml_quote($content)."</$tag>";
151                 }
152         }
153         return $s;
157 #===  FUNCTION  ================================================================
158 #         NAME:  myXmlHashToString
159 #   PARAMETERS:  xml_hash - hash - hash from function createXmlHash
160 #      RETURNS:  xml_string - string - xml string representation of the hash
161 #  DESCRIPTION:  Transforms the given hash to a xml wellformed string. I.e.:
162 #                    {
163 #                   'header' => 'a'
164 #                   'source' => 'c',
165 #                   'target' => 'b',
166 #                   'hit' => [ '1',
167 #                              '2',
168 #                              { 
169 #                                'hit31' => 'ABC',
170 #                                'hit32' => 'XYZ'
171 #                              }
172 #                            ],
173 #                   'res0' => {
174 #                      'res1' => {
175 #                         'res2' => 'result'
176 #                      }
177 #                   },
178 #                };
179 #           
180 #                                will be transformed to 
181 #                                <xml>
182 #                                       <header>a</header>
183 #                                       <source>c</source>
184 #                                       <target>b</target>
185 #                                       <hit>1</hit>
186 #                                       <hit>2</hit>
187 #                                       <hit>
188 #                                               <hit31>ABC</hit31>
189 #                                               <hit32>XYZ</hit32>
190 #                                       </hit>
191 #                                       <res0>
192 #                                               <res1>
193 #                                                       <res2>result</res2>
194 #                                               </res1>
195 #                                       </res0>
196 #                               </xml>
198 #===============================================================================
199 sub myXmlHashToString {
200         my ($hash) = @_;
201         return "<xml>".&_transformHashToString($hash)."</xml>";
205 #===  FUNCTION  ================================================================
206 #         NAME:  create_xml_string
207 #   PARAMETERS:  xml_hash - hash - hash from function create_xml_hash
208 #      RETURNS:  xml_string - string - xml string representation of the hash
209 #  DESCRIPTION:  transform the hash to a string using XML::Simple module
210 #===============================================================================
211 sub create_xml_string {
212     my ($xml_hash) = @_ ;
213     my $xml_string = $xml->XMLout($xml_hash, RootName => 'xml');
214     #$xml_string =~ s/[\n]+//g;
215     #daemon_log("create_xml_string:",7);
216     #daemon_log("$xml_string\n", 7);
217     return $xml_string;
221 sub transform_msg2hash {
222     my ($msg) = @_ ;
223     my $hash = $xml->XMLin($msg, ForceArray=>1);
224     
225     # xml tags without a content are created as an empty hash
226     # substitute it with an empty list
227     eval {
228         while( my ($xml_tag, $xml_content) = each %{ $hash } ) {
229             if( 1 == @{ $xml_content } ) {
230                 # there is only one element in xml_content list ...
231                 my $element = @{ $xml_content }[0];
232                 if( ref($element) eq "HASH" ) {
233                     # and this element is an hash ...
234                     my $len_element = keys %{ $element };
235                     if( $len_element == 0 ) {
236                         # and this hash is empty, then substitute the xml_content
237                         # with an empty string in list
238                         $hash->{$xml_tag} = [ "none" ];
239                     }
240                 }
241             }
242         }
243     };
244     if( $@ ) {  
245         $hash = undef;
246     }
248     return $hash;
252 #===  FUNCTION  ================================================================
253 #         NAME:  add_content2xml_hash
254 #   PARAMETERS:  xml_ref - ref - reference to a hash from function create_xml_hash
255 #                element - string - key for the hash
256 #                content - string - value for the hash
257 #      RETURNS:  nothing
258 #  DESCRIPTION:  add key-value pair to xml_ref, if key alread exists, 
259 #                then append value to list
260 #===============================================================================
261 sub add_content2xml_hash {
262     my ($xml_ref, $element, $content) = @_;
263     if(not exists $$xml_ref{$element} ) {
264         $$xml_ref{$element} = [];
265     }
266     my $tmp = $$xml_ref{$element};
267     push(@$tmp, $content);
268     return;
272 sub get_time {
273         my ($seconds, $minutes, $hours, $monthday, $month,
274                 $year, $weekday, $yearday, $sommertime) = localtime;
275         $hours = $hours < 10 ? $hours = "0".$hours : $hours;
276         $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
277         $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
278         $month+=1;
279         $month = $month < 10 ? $month = "0".$month : $month;
280         $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
281         $year+=1900;
282         return "$year$month$monthday$hours$minutes$seconds";
286 sub get_utc_time {
287     my $utc_time = qx(date --utc +%Y%m%d%H%M%S);
288     $utc_time =~ s/\s$//;
289     return $utc_time;
293 #===  FUNCTION  ================================================================
294 #         NAME: build_msg
295 #  DESCRIPTION: Send a message to a destination
296 #   PARAMETERS: [header] Name of the header
297 #               [from]   sender ip
298 #               [to]     recipient ip
299 #               [data]   Hash containing additional attributes for the xml
300 #                        package
301 #      RETURNS:  nothing
302 #===============================================================================
303 sub build_msg ($$$$) {
304         my ($header, $from, $to, $data) = @_;
306     # data is of form, i.e.
307     # %data= ('ip' => $address, 'mac' => $mac);
309         my $out_hash = &create_xml_hash($header, $from, $to);
311         while ( my ($key, $value) = each(%$data) ) {
312                 if(ref($value) eq 'ARRAY'){
313                         map(&add_content2xml_hash($out_hash, $key, $_), @$value);
314                 } else {
315                         &add_content2xml_hash($out_hash, $key, $value);
316                 }
317         }
318     my $out_msg = &create_xml_string($out_hash);
319     return $out_msg;
323 sub db_res2xml {
324     my ($db_res) = @_ ;
325     my $xml = "";
327     my $len_db_res= keys %{$db_res};
328     for( my $i= 1; $i<= $len_db_res; $i++ ) {
329         $xml .= "\n<answer$i>";
330         my $hash= $db_res->{$i};
331         while ( my ($column_name, $column_value) = each %{$hash} ) {
332             $xml .= "<$column_name>";
333             my $xml_content;
334             if( $column_name eq "xmlmessage" ) {
335                 $xml_content = &encode_base64($column_value);
336             } else {
337                 $xml_content = defined $column_value ? $column_value : "";
338             }
339             $xml .= $xml_content;
340             $xml .= "</$column_name>"; 
341         }
342         $xml .= "</answer$i>";
344     }
346     return $xml;
350 sub db_res2si_msg {
351     my ($db_res, $header, $target, $source) = @_;
353     my $si_msg = "<xml>";
354     $si_msg .= "<header>$header</header>";
355     $si_msg .= "<source>$source</source>";
356     $si_msg .= "<target>$target</target>";
357     $si_msg .= &db_res2xml;
358     $si_msg .= "</xml>";
362 sub get_where_statement {
363     my ($msg, $msg_hash) = @_;
364     my $error= 0;
365     
366     my $clause_str= "";
367     if( (not exists $msg_hash->{'where'}) || (not exists @{$msg_hash->{'where'}}[0]->{'clause'}) ) { 
368         $error++; 
369     }
371     if( $error == 0 ) {
372         my @clause_l;
373         my @where = @{@{$msg_hash->{'where'}}[0]->{'clause'}};
374         foreach my $clause (@where) {
375             my $connector = $clause->{'connector'}[0];
376             if( not defined $connector ) { $connector = "AND"; }
377             $connector = uc($connector);
378             delete($clause->{'connector'});
380             my @phrase_l ;
381             foreach my $phrase (@{$clause->{'phrase'}}) {
382                 my $operator = "=";
383                 if( exists $phrase->{'operator'} ) {
384                     my $op = $op_hash->{$phrase->{'operator'}[0]};
385                     if( not defined $op ) {
386                         &main::daemon_log("ERROR: Can not translate operator '$operator' in where-".
387                                 "statement to sql valid syntax. Please use 'eq', ".
388                                 "'ne', 'ge', 'gt', 'le', 'lt' in xml message\n", 1);
389                         &main::daemon_log($msg, 8);
390                         $op = "=";
391                     }
392                     $operator = $op;
393                     delete($phrase->{'operator'});
394                 }
396                 my @xml_tags = keys %{$phrase};
397                 my $tag = $xml_tags[0];
398                 my $val = $phrase->{$tag}[0];
399                 if( ref($val) eq "HASH" ) { next; }  # empty xml-tags should not appear in where statement
401                                 # integer columns do not have to have single quotes besides the value
402                                 if ($tag eq "id") {
403                                                 push(@phrase_l, "$tag$operator$val");
404                                 } else {
405                                                 push(@phrase_l, "$tag$operator'$val'");
406                                 }
407             }
409             if (not 0 == @phrase_l) {
410                 my $clause_str .= join(" $connector ", @phrase_l);
411                 push(@clause_l, "($clause_str)");
412             }
413         }
415         if( not 0 == @clause_l ) {
416             $clause_str = join(" AND ", @clause_l);
417             $clause_str = "WHERE ($clause_str) ";
418         }
419     }
421     return $clause_str;
424 sub get_select_statement {
425     my ($msg, $msg_hash)= @_;
426     my $select = "*";
427     if( exists $msg_hash->{'select'} ) {
428         my $select_l = \@{$msg_hash->{'select'}};
429         $select = join(', ', @{$select_l});
430     }
431     return $select;
435 sub get_update_statement {
436     my ($msg, $msg_hash) = @_;
437     my $error= 0;
438     my $update_str= "";
439     my @update_l; 
441     if( not exists $msg_hash->{'update'} ) { $error++; };
443     if( $error == 0 ) {
444         my $update= @{$msg_hash->{'update'}}[0];
445         while( my ($tag, $val) = each %{$update} ) {
446             my $val= @{$update->{$tag}}[0];
447             push(@update_l, "$tag='$val'");
448         }
449         if( 0 == @update_l ) { $error++; };   
450     }
452     if( $error == 0 ) { 
453         $update_str= join(', ', @update_l);
454         $update_str= "SET $update_str ";
455     }
457     return $update_str;
460 sub get_limit_statement {
461     my ($msg, $msg_hash)= @_; 
462     my $error= 0;
463     my $limit_str = "";
464     my ($from, $to);
466     if( not exists $msg_hash->{'limit'} ) { $error++; };
468     if( $error == 0 ) {
469         eval {
470             my $limit= @{$msg_hash->{'limit'}}[0];
471             $from= @{$limit->{'from'}}[0];
472             $to= @{$limit->{'to'}}[0];
473         };
474         if( $@ ) {
475             $error++;
476         }
477     }
479     if( $error == 0 ) {
480         $limit_str= "LIMIT $from, $to";
481     }   
482     
483     return $limit_str;
486 sub get_orderby_statement {
487     my ($msg, $msg_hash)= @_;
488     my $error= 0;
489     my $order_str= "";
490     my $order;
491     
492     if( not exists $msg_hash->{'orderby'} ) { $error++; };
494     if( $error == 0) {
495         eval {
496             $order= @{$msg_hash->{'orderby'}}[0];
497         };
498         if( $@ ) {
499             $error++;
500         }
501     }
503     if( $error == 0 ) {
504         $order_str= "ORDER BY $order";   
505     }
506     
507     return $order_str;
510 sub get_dns_domains() {
511         my $line;
512         my @searches;
513         open(RESOLV, "</etc/resolv.conf") or return @searches;
514         while(<RESOLV>){
515                 $line= $_;
516                 chomp $line;
517                 $line =~ s/^\s+//;
518                 $line =~ s/\s+$//;
519                 $line =~ s/\s+/ /;
520                 if ($line =~ /^domain (.*)$/ ){
521                         push(@searches, $1);
522                 } elsif ($line =~ /^search (.*)$/ ){
523                         push(@searches, split(/ /, $1));
524                 }
525         }
526         close(RESOLV);
528         my %tmp = map { $_ => 1 } @searches;
529         @searches = sort keys %tmp;
531         return @searches;
535 sub get_server_addresses {
536     my $domain= shift;
537     my @result;
538     my $error_string;
540     my $error = 0;
541     my $res   = Net::DNS::Resolver->new;
542     my $query = $res->send("_gosa-si._tcp.".$domain, "SRV");
543     my @hits;
545     if ($query) {
546         foreach my $rr ($query->answer) {
547             push(@hits, $rr->target.":".$rr->port);
548         }
549     }
550     else {
551         $error_string = "determination of '_gosa-si._tcp' server in domain '$domain' failed: ".$res->errorstring;
552         $error++;
553     }
555     if( $error == 0 ) {
556         foreach my $hit (@hits) {
557             my ($hit_name, $hit_port) = split(/:/, $hit);
558             chomp($hit_name);
559             chomp($hit_port);
561             my $address_query = $res->send($hit_name);
562             if( 1 == length($address_query->answer) ) {
563                 foreach my $rr ($address_query->answer) {
564                     push(@result, $rr->address.":".$hit_port);
565                 }
566             }
567         }
568     }
570     return \@result, $error_string;
574 sub get_logged_in_users {
575     my $result = qx(/usr/bin/w -hs);
576     my @res_lines;
578     if( defined $result ) { 
579         chomp($result);
580         @res_lines = split("\n", $result);
581     }
583     my @logged_in_user_list;
584     foreach my $line (@res_lines) {
585         chomp($line);
586         my @line_parts = split(/\s+/, $line); 
587         push(@logged_in_user_list, $line_parts[0]);
588     }
590     return @logged_in_user_list;
595 sub import_events {
596     my ($event_dir) = @_;
597     my $event_hash;
598     my $error = 0;
599     my @result = ();
600     if (not -e $event_dir) {
601         $error++;
602         push(@result, "cannot find directory or directory is not readable: $event_dir");   
603     }
605     my $DIR;
606     if ($error == 0) {
607         opendir ($DIR, $event_dir) or do { 
608             $error++;
609             push(@result, "cannot open directory '$event_dir' for reading: $!\n");
610         }
611     }
613     if ($error == 0) {
614         while (defined (my $event = readdir ($DIR))) {
615             if( $event eq "." || $event eq ".." || ($event =~ /^\.pm$/)) { next; }  
617                         # Check config file to exclude disabled event plugins (i.e. Opsi)
618                         if ($event eq "opsi_com.pm" &&  $main::opsi_enabled ne "true")  { 
619                                 &main::daemon_log("0 WARNING: opsi-module is installed but not enabled in config file, please set under section '[OPSI]': 'enabled=true'", 3);  
620                                 next; 
621                         }
623             # try to import event module
624             eval{ require $event; };
625             if( $@ ) {
626                 $error++;
627                 #push(@result, "import of event module '$event' failed: $@");
628                 #next;
629                 
630                 &main::daemon_log("ERROR: Import of event module '$event' failed: $@",1);
631                 exit(1);
632             }
634             # fetch all single events
635             $event =~ /(\S*?).pm$/;
636             my $event_module = $1;
637             my $events_l = eval( $1."::get_events()") ;
638             foreach my $event_name (@{$events_l}) {
639                 $event_hash->{$event_module}->{$event_name} = "";
640             }
641             my $events_string = join( ", ", @{$events_l});
642             push(@result, "import of event module '$event' succeed: $events_string");
643         }
644         
645         close $DIR;
646     }
648     return ($error, \@result, $event_hash);
653 #===  FUNCTION  ================================================================
654 #         NAME:  get_ip 
655 #   PARAMETERS:  interface name (i.e. eth0)
656 #      RETURNS:  (ip address) 
657 #  DESCRIPTION:  Uses ioctl to get ip address directly from system.
658 #===============================================================================
659 sub get_ip {
660         my $ifreq= shift;
661         my $result= "";
662         my $SIOCGIFADDR= 0x8915;       # man 2 ioctl_list
663         my $proto= getprotobyname('ip');
665         socket SOCKET, PF_INET, SOCK_DGRAM, $proto
666                 or die "socket: $!";
668         if(ioctl SOCKET, $SIOCGIFADDR, $ifreq) {
669                 my ($if, $sin)    = unpack 'a16 a16', $ifreq;
670                 my ($port, $addr) = sockaddr_in $sin;
671                 my $ip            = inet_ntoa $addr;
673                 if ($ip && length($ip) > 0) {
674                         $result = $ip;
675                 }
676         }
678         return $result;
682 #===  FUNCTION  ================================================================
683 #         NAME:  get_interface_for_ip
684 #   PARAMETERS:  ip address (i.e. 192.168.0.1)
685 #      RETURNS:  array: list of interfaces if ip=0.0.0.0, matching interface if found, undef else
686 #  DESCRIPTION:  Uses proc fs (/proc/net/dev) to get list of interfaces.
687 #===============================================================================
688 sub get_interface_for_ip {
689         my $result;
690         my $ip= shift;
692         if($ip =~ /^[a-z]/i) {
693                 my $ip_address = inet_ntoa(scalar gethostbyname($ip));
694                 if(defined($ip_address) && $ip_address =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/) {
695                         # Write ip address to $source variable
696                         $ip = $ip_address;
697                 }
698         }
700         if ($ip && length($ip) > 0) {
701                 my @ifs= &get_interfaces();
702                 if($ip eq "0.0.0.0") {
703                         $result = "all";
704                 } else {
705                         foreach (@ifs) {
706                                 my $if=$_;
707                                 if(&get_ip($if) eq $ip) {
708                                         $result = $if;
709                                 }
710                         }       
711                 }
712         }       
713         return $result;
716 #===  FUNCTION  ================================================================
717 #         NAME:  get_interfaces 
718 #   PARAMETERS:  none
719 #      RETURNS:  (list of interfaces) 
720 #  DESCRIPTION:  Uses proc fs (/proc/net/dev) to get list of interfaces.
721 #===============================================================================
722 sub get_interfaces {
723         my @result;
724         my $PROC_NET_DEV= ('/proc/net/dev');
726         open(PROC_NET_DEV, "<$PROC_NET_DEV")
727                 or die "Could not open $PROC_NET_DEV";
729         my @ifs = <PROC_NET_DEV>;
731         close(PROC_NET_DEV);
733         # Eat first two line
734         shift @ifs;
735         shift @ifs;
737         chomp @ifs;
738         foreach my $line(@ifs) {
739                 my $if= (split /:/, $line)[0];
740                 $if =~ s/^\s+//;
741                 push @result, $if;
742         }
744         return @result;
747 sub get_local_ip_for_remote_ip {
748         my $remote_ip= shift;
749         my $result="0.0.0.0";
751     if($remote_ip =~ /^(\d\d?\d?\.){3}\d\d?\d?$/) {
752         my $PROC_NET_ROUTE= ('/proc/net/route');
754         open(PROC_NET_ROUTE, "<$PROC_NET_ROUTE")
755             or die "Could not open $PROC_NET_ROUTE";
757         my @ifs = <PROC_NET_ROUTE>;
759         close(PROC_NET_ROUTE);
761         # Eat header line
762         shift @ifs;
763         chomp @ifs;
764         my $iffallback = ''; 
766         # linux-vserver might have * as Iface due to hidden interfaces, set a default 
767         foreach my $line(@ifs) { 
768             my ($Iface,$Destination,$Gateway,$Flags,$RefCnt,$Use,$Metric,$Mask,$MTU,$Window,$IRTT)=split(/\s/, $line); 
769             if ($Iface =~ m/^[^\*]+$/) { 
770                  $iffallback = $Iface; 
771             } 
772         }
773  
774         foreach my $line(@ifs) {
775             my ($Iface,$Destination,$Gateway,$Flags,$RefCnt,$Use,$Metric,$Mask,$MTU,$Window,$IRTT)=split(/\s/, $line);
776             my $destination;
777             my $mask;
778             my ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Destination);
779             if ($Iface =~ m/^[^\*]+$/) { 
780                  $iffallback = $Iface;
781             } 
782             $destination= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
783             ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Mask);
784             $mask= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
785             if(new NetAddr::IP($remote_ip)->within(new NetAddr::IP($destination, $mask))) {
786                 # destination matches route, save mac and exit
787                 #$result= &get_ip($Iface);
789                 if ($Iface =~ m/^\*$/ ) { 
790                     $result= &get_ip($iffallback);    
791                 } else { 
792                     $result= &get_ip($Iface); 
793                 } 
794                 last;
795             }
796         }
797     } 
799         return $result;
803 sub get_mac_for_interface {
804         my $ifreq= shift;
805         my $result;
806         if ($ifreq && length($ifreq) > 0) {
807                 if($ifreq eq "all") {
808                         $result = "00:00:00:00:00:00";
809                 } else {
810         $result = Net::ARP::get_mac($ifreq);
811                 }
812         }
813         return $result;
817 #===  FUNCTION  ================================================================
818 #         NAME:  is_local
819 #   PARAMETERS:  Server Address
820 #      RETURNS:  true if Server Address is on this host, false otherwise
821 #  DESCRIPTION:  Checks all interface addresses, stops on first match
822 #===============================================================================
823 sub is_local {
824     my $server_address = shift || "";
825     my $result = 0;
827     my $server_ip = $1 if $server_address =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):\d{1,6}$/;
829     if(defined($server_ip) && length($server_ip) > 0) {
830         foreach my $interface(&get_interfaces()) {
831             my $ip_address= &get_ip($interface);
832             if($ip_address eq $server_ip) {
833                 $result = 1;
834                 last;
835             }
836         }
837     }
839     return $result;
843 #===  FUNCTION  ================================================================
844 #         NAME:  run_as
845 #   PARAMETERS:  uid, command
846 #      RETURNS:  hash with keys 'resultCode' = resultCode of command and 
847 #                'output' = program output
848 #  DESCRIPTION:  Runs command as uid using the sudo utility.
849 #===============================================================================
850 sub run_as {
851         my ($uid, $command) = @_;
852         my $sudo_cmd = `which sudo`;
853         chomp($sudo_cmd);
854         if(! -x $sudo_cmd) {
855                 &main::daemon_log("ERROR: The sudo utility is not available! Please fix this!");
856         }
857         my $cmd_line= "$sudo_cmd su - $uid -c '$command'";
858         open(PIPE, "$cmd_line |");
859         my $result = {'command' => $cmd_line};
860         push @{$result->{'output'}}, <PIPE>;
861         close(PIPE);
862         my $exit_value = $? >> 8;
863         $result->{'resultCode'} = $exit_value;
864         return $result;
868 #===  FUNCTION  ================================================================
869 #         NAME:  inform_other_si_server
870 #   PARAMETERS:  message
871 #      RETURNS:  nothing
872 #  DESCRIPTION:  Sends message to all other SI-server found in known_server_db. 
873 #===============================================================================
874 sub inform_all_other_si_server {
875     my ($msg) = @_;
877     # determine all other si-server from known_server_db
878     my $sql_statement= "SELECT * FROM $main::known_server_tn";
879     my $res = $main::known_server_db->select_dbentry( $sql_statement ); 
881     while( my ($hit_num, $hit) = each %$res ) {    
882         my $act_target_address = $hit->{hostname};
883         my $act_target_key = $hit->{hostkey};
885         # determine the source address corresponding to the actual target address
886         my ($act_target_ip, $act_target_port) = split(/:/, $act_target_address);
887         my $act_source_address = &main::get_local_ip_for_remote_ip($act_target_ip).":$act_target_port";
889         # fill into message the correct target and source addresses
890         my $act_msg = $msg;
891         $act_msg =~ s/<target>\w*<\/target>/<target>$act_target_address<\/target>/g;
892         $act_msg =~ s/<source>\w*<\/source>/<source>$act_source_address<\/source>/g;
894         # send message to the target
895         &main::send_msg_to_target($act_msg, $act_target_address, $act_target_key, "foreign_job_updates" , "J");
896     }
898     return;
902 sub read_configfile {
903     my ($cfg_file, %cfg_defaults) = @_ ;
904     my $cfg;
905     if( defined( $cfg_file) && ( (-s $cfg_file) > 0 )) {
906         if( -r $cfg_file ) {
907             $cfg = Config::IniFiles->new( -file => $cfg_file, -nocase => 1 );
908         } else {
909             print STDERR "Couldn't read config file!";
910         }
911     } else {
912         $cfg = Config::IniFiles->new() ;
913     }
914     foreach my $section (keys %cfg_defaults) {
915         foreach my $param (keys %{$cfg_defaults{ $section }}) {
916             my $pinfo = $cfg_defaults{ $section }{ $param };
917            ${@$pinfo[ 0 ]} = $cfg->val( $section, $param, @$pinfo[ 1 ] );
918         }
919     }
923 sub check_opsi_res {
924     my $res= shift;
926     if($res) {
927         if ($res->is_error) {
928             my $error_string;
929             if (ref $res->error_message eq "HASH") { 
930                                 # for different versions
931                 $error_string = $res->error_message->{'message'}; 
932                                 $_ = $res->error_message->{'message'};
933             } else { 
934                                 # for different versions
935                 $error_string = $res->error_message; 
936                                 $_ = $res->error_message;
937             }
938             return 1, $error_string;
939         }
940     } else {
941                 # for different versions
942                 $_ = $main::opsi_client->status_line;
943         return 1, $main::opsi_client->status_line;
944     }
945     return 0;
948 sub calc_timestamp {
949     my ($timestamp, $operation, $value, $entity) = @_ ;
950         $entity = defined $entity ? $entity : "seconds";
951     my $res_timestamp = 0;
952     
953     $value = int($value);
954     $timestamp = int($timestamp);
955     $timestamp =~ /(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/;
956     my $dt = DateTime->new( year   => $1,
957             month  => $2,
958             day    => $3,
959             hour   => $4,
960             minute => $5,
961             second => $6,
962             );
964     if ($operation eq "plus" || $operation eq "+") {
965         $dt->add($entity => $value);
966         $res_timestamp = $dt->ymd('').$dt->hms('');
967     }
969     if ($operation eq "minus" || $operation eq "-") {
970         $dt->subtract($entity => $value);
971         $res_timestamp = $dt->ymd('').$dt->hms('');
972     }
974     return $res_timestamp;
977 sub opsi_callobj2string {
978     my ($callobj) = @_;
979     my @callobj_string;
980     while(my ($key, $value) = each(%$callobj)) {
981         my $value_string = "";
982         if (ref($value) eq "ARRAY") {
983             $value_string = join(",", @$value);
984         } else {
985             $value_string = $value;
986         }
987         push(@callobj_string, "$key=$value_string")
988     }
989     return join(", ", @callobj_string);
992 1;