2d9ce606325c9941e5036da1fd3fda7b7c8906cf
1 #!/usr/bin/perl
2 #===============================================================================
3 #
4 # FILE: gosa-server
5 #
6 # USAGE: gosa-si-client
7 #
8 # DESCRIPTION:
9 #
10 # OPTIONS: ---
11 # REQUIREMENTS: libnetaddr-ip-perl
12 # BUGS: ---
13 # NOTES:
14 # AUTHOR: (Andreas Rettenberger), <rettenberger@gonicus.de>
15 # COMPANY:
16 # VERSION: 1.0
17 # CREATED: 12.09.2007 08:54:41 CEST
18 # REVISION: ---
19 #===============================================================================
21 use strict;
22 use warnings;
23 use Getopt::Long;
24 use Config::IniFiles;
25 use POSIX;
26 use Time::HiRes qw( gettimeofday );
27 use File::Basename;
29 use Fcntl;
30 use IO::Socket::INET;
31 use Crypt::Rijndael;
32 use MIME::Base64;
33 use Digest::MD5 qw(md5 md5_hex md5_base64);
34 use XML::Simple;
35 use Data::Dumper;
36 use Sys::Syslog qw( :DEFAULT setlogsock);
37 use File::Spec;
38 use Cwd;
39 use NetAddr::IP;
40 use GOSA::GosaSupportDaemon;
43 my ($cfg_file, %cfg_defaults, $foreground, $verbose, $pid_file, $procid, $pid, $log_file);
44 my ($server_address, $server_ip, $server_port, $server_domain, $server_passwd, $server_cipher, $server_timeout);
45 my ($client_address, $client_ip, $client_port, $client_mac_address, $network_interface, $ldap_config, $pam_config, $nss_config, $gotoHardwareChecksum);
46 my ($input_socket, $rbits, $wbits, $ebits, $xml, $known_hosts, $ldap_enabled);
47 my (@events);
49 # default variables
50 my $event_dir = "/usr/lib/gosa-si/client/events";
51 $known_hosts = {};
52 $foreground = 0 ;
53 %cfg_defaults =
54 ("general" =>
55 {"log_file" => [\$log_file, "/var/run/".$0.".log"],
56 "pid_file" => [\$pid_file, "/var/run/".$0.".pid"],
57 },
58 "client" =>
59 {"client_port" => [\$client_port, "20083"],
60 "client_ip" => [\$client_ip, "0.0.0.0"],
61 "client_mac_address" => [\$client_mac_address, "00:00:00:00:00:00"],
62 "ldap" => [\$ldap_enabled, 1],
63 "ldap_config" => [\$ldap_config, "/etc/ldap/ldap.conf"],
64 "pam_config" => [\$pam_config, "/etc/pam_ldap.conf"],
65 "nss_config" => [\$nss_config, "/etc/libnss_ldap.conf"],
66 },
67 "server" =>
68 {"server_ip" => [\$server_ip, ""],
69 "server_port" => [\$server_port, "20081"],
70 "server_passwd" => [\$server_passwd, ""],
71 "server_timeout" => [\$server_timeout, 10],
72 "server_domain" => [\$server_domain, ""],
73 },
74 );
77 #=== FUNCTION ================================================================
78 # NAME: read_configfile
79 # PARAMETERS: cfg_file - string -
80 # RETURNS:
81 # DESCRIPTION:
82 #===============================================================================
83 sub read_configfile {
84 my $cfg;
85 if( defined( $cfg_file) && ( length($cfg_file) > 0 )) {
86 if( -r $cfg_file ) {
87 $cfg = Config::IniFiles->new( -file => $cfg_file );
88 } else {
89 print STDERR "Couldn't read config file!";
90 }
91 } else {
92 $cfg = Config::IniFiles->new() ;
93 }
94 foreach my $section (keys %cfg_defaults) {
95 foreach my $param (keys %{$cfg_defaults{ $section }}) {
96 my $pinfo = $cfg_defaults{ $section }{ $param };
97 ${@$pinfo[ 0 ]} = $cfg->val( $section, $param, @$pinfo[ 1 ] );
98 }
99 }
100 }
103 #=== FUNCTION ================================================================
104 # NAME: logging
105 # PARAMETERS: level - string - default 'info'
106 # msg - string -
107 # facility - string - default 'LOG_DAEMON'
108 # RETURNS:
109 # DESCRIPTION:
110 #===============================================================================
111 sub daemon_log {
112 my( $msg, $level ) = @_;
113 if(not defined $msg) { return }
114 if(not defined $level) { $level = 1 }
115 if(defined $log_file){
116 open(LOG_HANDLE, ">>$log_file");
117 if(not defined open( LOG_HANDLE, ">>$log_file" )) {
118 print STDERR "cannot open $log_file: $!";
119 return }
120 chomp($msg);
121 if($level <= $verbose){
122 print LOG_HANDLE $msg."\n";
123 if(defined $foreground) { print $msg."\n" }
124 }
125 }
126 close( LOG_HANDLE );
127 # my ($msg, $level, $facility) = @_;
128 # if(not defined $msg) {return}
129 # if(not defined $level) {$level = "info"}
130 # if(not defined $facility) {$facility = "LOG_DAEMON"}
131 # openlog($0, "pid,cons,", $facility);
132 # syslog($level, $msg);
133 # closelog;
134 # return;
135 }
138 #=== FUNCTION ================================================================
139 # NAME: check_cmdline_param
140 # PARAMETERS:
141 # RETURNS:
142 # DESCRIPTION:
143 #===============================================================================
144 sub check_cmdline_param () {
145 my $err_config;
146 my $err_counter = 0;
147 if(not defined($cfg_file)) {
148 $cfg_file = "/etc/gosa-si/client.conf";
149 if(! -r $cfg_file) {
150 $err_config = "please specify a config file";
151 $err_counter += 1;
152 }
153 }
154 if( $err_counter > 0 ) {
155 &usage( "", 1 );
156 if( defined( $err_config)) { print STDERR "$err_config\n"}
157 print STDERR "\n";
158 exit( -1 );
159 }
160 }
163 #=== FUNCTION ================================================================
164 # NAME: check_pid
165 # PARAMETERS:
166 # RETURNS:
167 # DESCRIPTION:
168 #===============================================================================
169 sub check_pid {
170 $pid = -1;
171 # Check, if we are already running
172 if( open(LOCK_FILE, "<$pid_file") ) {
173 $pid = <LOCK_FILE>;
174 if( defined $pid ) {
175 chomp( $pid );
176 if( -f "/proc/$pid/stat" ) {
177 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
178 if( $0 eq $stat ) {
179 close( LOCK_FILE );
180 exit -1;
181 }
182 }
183 }
184 close( LOCK_FILE );
185 unlink( $pid_file );
186 }
188 # create a syslog msg if it is not to possible to open PID file
189 if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
190 my($msg) = "Couldn't obtain lockfile '$pid_file' ";
191 if (open(LOCK_FILE, '<', $pid_file)
192 && ($pid = <LOCK_FILE>))
193 {
194 chomp($pid);
195 $msg .= "(PID $pid)\n";
196 } else {
197 $msg .= "(unable to read PID)\n";
198 }
199 if( ! ($foreground) ) {
200 openlog( $0, "cons,pid", "daemon" );
201 syslog( "warning", $msg );
202 closelog();
203 }
204 else {
205 print( STDERR " $msg " );
206 }
207 exit( -1 );
208 }
209 }
211 #=== FUNCTION ================================================================
212 # NAME: get_interface_for_ip
213 # PARAMETERS: ip address (i.e. 192.168.0.1)
214 # RETURNS: array: list of interfaces if ip=0.0.0.0, matching interface if found, undef else
215 # DESCRIPTION: Uses proc fs (/proc/net/dev) to get list of interfaces.
216 #===============================================================================
217 sub get_interface_for_ip {
218 my $result;
219 my $ip= shift;
220 if ($ip && length($ip) > 0) {
221 my @ifs= &get_interfaces();
222 if($ip eq "0.0.0.0") {
223 $result = "all";
224 } else {
225 foreach (@ifs) {
226 my $if=$_;
227 if(get_ip($if) eq $ip) {
228 $result = $if;
229 last;
230 }
231 }
232 }
233 }
234 return $result;
235 }
237 #=== FUNCTION ================================================================
238 # NAME: get_interfaces
239 # PARAMETERS: none
240 # RETURNS: (list of interfaces)
241 # DESCRIPTION: Uses proc fs (/proc/net/dev) to get list of interfaces.
242 #===============================================================================
243 sub get_interfaces {
244 my @result;
245 my $PROC_NET_DEV= ('/proc/net/dev');
247 open(PROC_NET_DEV, "<$PROC_NET_DEV")
248 or die "Could not open $PROC_NET_DEV";
250 my @ifs = <PROC_NET_DEV>;
252 close(PROC_NET_DEV);
254 # Eat first two line
255 shift @ifs;
256 shift @ifs;
258 chomp @ifs;
259 foreach my $line(@ifs) {
260 my $if= (split /:/, $line)[0];
261 $if =~ s/^\s+//;
262 push @result, $if;
263 }
265 return @result;
266 }
268 #=== FUNCTION ================================================================
269 # NAME: get_mac
270 # PARAMETERS: interface name (i.e. eth0)
271 # RETURNS: (mac address)
272 # DESCRIPTION: Uses ioctl to get mac address directly from system.
273 #===============================================================================
274 sub get_mac {
275 my $ifreq= shift;
276 my $result;
278 if ($ifreq && length($ifreq) > 0) {
279 if($ifreq eq "all") {
280 if(defined($server_ip)) {
281 $result = &get_local_mac_for_remote_ip($server_ip);
282 } elsif ($client_mac_address && length($client_mac_address) > 0) {
283 $result = $client_mac_address;
284 } else {
285 $result = "00:00:00:00:00:00";
286 }
287 } else {
288 my $SIOCGIFHWADDR= 0x8927; # man 2 ioctl_list
290 socket SOCKET, PF_INET, SOCK_DGRAM, getprotobyname('ip')
291 or die "socket: $!";
293 # A configured MAC Address should always override a guessed value
294 if ($client_mac_address and length($client_mac_address) > 0) {
295 $result = $client_mac_address;
296 } elsif(ioctl SOCKET, $SIOCGIFHWADDR, $ifreq) {
297 my ($if, $mac)= unpack 'h36 H12', $ifreq;
299 if (length($mac) > 0) {
300 $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])$/;
301 $mac= sprintf("%s:%s:%s:%s:%s:%s", $1, $2, $3, $4, $5, $6);
302 $result = $mac;
303 }
304 }
305 }
306 }
307 return $result;
308 }
310 #=== FUNCTION ================================================================
311 # NAME: get_ip
312 # PARAMETERS: interface name (i.e. eth0)
313 # RETURNS: (ip address)
314 # DESCRIPTION: Uses ioctl to get ip address directly from system.
315 #===============================================================================
316 sub get_ip {
317 my $ifreq= shift;
318 my $result= "";
319 my $SIOCGIFADDR= 0x8915; # man 2 ioctl_list
320 my $proto= getprotobyname('ip');
322 socket SOCKET, PF_INET, SOCK_DGRAM, $proto
323 or die "socket: $!";
325 if(ioctl SOCKET, $SIOCGIFADDR, $ifreq) {
326 my ($if, $sin) = unpack 'a16 a16', $ifreq;
327 my ($port, $addr) = sockaddr_in $sin;
328 my $ip = inet_ntoa $addr;
330 if ($ip && length($ip) > 0) {
331 $result = $ip;
332 }
333 }
335 return $result;
336 }
338 #=== FUNCTION ================================================================
339 # NAME: get_local_mac_for_remote_ip
340 # PARAMETERS: none (takes server_ip from global variable)
341 # RETURNS: (ip address from interface that is used for communication)
342 # DESCRIPTION: Uses ioctl to get routing table from system, checks which entry
343 # matches (defaultroute last).
344 #===============================================================================
345 sub get_local_mac_for_remote_ip {
346 my $ifreq= shift;
347 my $result= "00:00:00:00:00:00";
348 my $PROC_NET_ROUTE= ('/proc/net/route');
350 open(PROC_NET_ROUTE, "<$PROC_NET_ROUTE")
351 or die "Could not open $PROC_NET_ROUTE";
353 my @ifs = <PROC_NET_ROUTE>;
355 close(PROC_NET_ROUTE);
357 # Eat header line
358 shift @ifs;
359 chomp @ifs;
360 foreach my $line(@ifs) {
361 my ($Iface,$Destination,$Gateway,$Flags,$RefCnt,$Use,$Metric,$Mask,$MTU,$Window,$IRTT)=split(/\s/, $line);
362 my $destination;
363 my $mask;
364 my ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Destination);
365 $destination= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
366 ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Mask);
367 $mask= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
368 if(new NetAddr::IP($server_ip)->within(new NetAddr::IP($destination, $mask))) {
369 # destination matches route, save mac and exit
370 $result= &get_mac($Iface);
371 last;
372 }
373 }
376 return $result;
377 }
379 #=== FUNCTION ================================================================
380 # NAME: usage
381 # PARAMETERS:
382 # RETURNS:
383 # DESCRIPTION:
384 #===============================================================================
385 sub usage {
386 my( $text, $help ) = @_;
387 $text = undef if( "h" eq $text );
388 (defined $text) && print STDERR "\n$text\n";
389 if( (defined $help && $help) || (!defined $help && !defined $text) ) {
390 print STDERR << "EOF" ;
391 usage: $0 [-hvf] [-c config]
393 -h : this (help) message
394 -c <file> : config file
395 -f : foreground, process will not be forked to background
396 -v : be verbose (multiple to increase verbosity)
397 EOF
398 }
399 print "\n" ;
400 }
402 #=== FUNCTION ================================================================
403 # NAME: get_server_addresses
404 # PARAMETERS:
405 # RETURNS:
406 # DESCRIPTION:
407 #===============================================================================
408 sub get_server_addresses {
409 my $domain= shift;
410 my @result;
411 my $dig_cmd= 'dig +nocomments srv _gosad._tcp.'.$domain;
413 my $output= `$dig_cmd 2>&1`;
414 open (PIPE, "$dig_cmd 2>&1 |");
415 while(<PIPE>) {
416 chomp $_;
417 # If it's not a comment
418 if($_ =~ m/^[^;]/) {
419 my @matches= split /\s+/;
421 # Push hostname with port
422 if($matches[3] eq 'SRV') {
423 push @result, $matches[7].':'.$matches[6];
424 } elsif ($matches[3] eq 'A') {
425 my $i=0;
427 # Substitute the hostname with the ip address of the matching A record
428 foreach my $host (@result) {
429 if ((split /\:/, $host)[0] eq $matches[0]) {
430 $result[$i]= $matches[4].':'.(split /\:/, $host)[1];
431 }
432 $i++;
433 }
434 }
435 }
436 }
437 close(PIPE);
438 return @result;
439 }
442 #=== FUNCTION ================================================================
443 # NAME: register_at_server
444 # PARAMETERS:
445 # RETURNS:
446 # DESCRIPTION:
447 #===============================================================================
448 sub register_at_server {
449 my ($tmp) = @_;
451 # create new passwd and ciphering object for client-server communication
452 my $new_server_passwd = &create_passwd();
453 my $new_server_cipher;
455 # detect all client accepted events
456 opendir(DIR, $event_dir)
457 or daemon_log("cannot find directory $event_dir!\ngosa-si-client starts without any accepting events!", 1);
458 my $file_name;
459 my @events_list = ();
460 while(defined($file_name = readdir(DIR))){
461 if ($file_name eq "." || $file_name eq "..") {
462 next;
463 }
464 push(@events_list, $file_name);
465 }
466 my $events = join(",", @events_list);
467 daemon_log("found events: $events", 1);
469 # fill in all possible servers
470 my @servers;
471 if (defined $server_domain) {
472 my @tmp_servers = &get_server_addresses($server_domain);
473 foreach my $server (@tmp_servers) { unshift(@servers, $server); }
474 }
475 # add server address from config file at first position of server list
476 if (defined $server_address) {
477 unshift(@servers, $server_address);
478 }
479 daemon_log("found servers in configuration file and via DNS:", 5);
480 foreach my $server (@servers) {
481 daemon_log("\t$server", 5);
482 }
484 my ($rout, $wout, $reg_server);
485 foreach my $server (@servers) {
487 # create msg hash
488 my $register_hash = &create_xml_hash("here_i_am", $client_address, $server);
489 &add_content2xml_hash($register_hash, "new_passwd", $new_server_passwd);
490 &add_content2xml_hash($register_hash, "mac_address", $client_mac_address);
491 &add_content2xml_hash($register_hash, "events", $events);
492 &add_content2xml_hash($register_hash, "gotoHardwareChecksum", $gotoHardwareChecksum);
494 # send xml hash to server with general server passwd
495 my $answer = &send_msg_hash2address($register_hash, $server, $server_passwd);
497 if ($answer != 0) { next; }
499 # waiting for response
500 daemon_log("waiting for response...\n", 5);
501 my $nf = select($rout=$rbits, $wout=$wbits, undef, $server_timeout);
503 # something is coming in
504 if(vec $rout, fileno $input_socket, 1) {
505 my $crypted_msg;
506 my $client = $input_socket->accept();
507 my $other_end = getpeername($client);
508 if(not defined $other_end) {
509 daemon_log("client cannot be identified: $!\n");
510 } else {
511 my ($port, $iaddr) = unpack_sockaddr_in($other_end);
512 my $actual_ip = inet_ntoa($iaddr);
513 daemon_log("\naccept client from $actual_ip\n", 5);
514 my $in_msg = &read_from_socket($client);
515 if(defined $in_msg){
516 chomp($in_msg);
517 $crypted_msg = $in_msg;
518 } else {
519 daemon_log("cannot read from $actual_ip\n", 5);
520 }
521 }
522 close($client);
524 # validate acknowledge msg from server
525 $new_server_cipher = &create_ciphering($new_server_passwd);
526 my $msg_hash;
527 eval {
528 my $decrypted_msg = &decrypt_msg($crypted_msg, $new_server_cipher);
529 daemon_log("decrypted register msg: $decrypted_msg", 5);
530 $msg_hash = $xml->XMLin($decrypted_msg, ForceArray=>1);
531 };
532 if($@) {
533 daemon_log("ERROR: do not understand the incoming message:" , 5);
534 daemon_log("$@", 7);
535 } else {
536 my $header = @{$msg_hash->{header}}[0];
537 if($header eq "registered") {
538 $reg_server = $server;
539 last;
540 } elsif($header eq "denied") {
541 my $reason = (&get_content_from_xml_hash($msg_hash, "denied"))[0];
542 daemon_log("registration at $server denied: $reason", 1);
543 } else {
544 daemon_log("cannot register at $server", 1);
545 }
546 }
547 }
548 # if no answer arrive, try next server in list
550 }
552 if(defined $reg_server) {
553 daemon_log("registered at $reg_server", 1);
554 } else {
555 daemon_log("cannot register at any server", 1);
556 daemon_log("exiting!!!", 1);
557 exit(1);
558 }
560 # update the global available variables
561 $server_address = $reg_server;
562 $server_passwd = $new_server_passwd;
563 $server_cipher = $new_server_cipher;
564 return;
565 }
568 #=== FUNCTION ================================================================
569 # NAME: create_xml_hash
570 # PARAMETERS:
571 # RETURNS:
572 # DESCRIPTION:
573 #===============================================================================
574 #sub create_xml_hash {
575 # my ($header, $source, $target, $header_value) = @_;
576 # my $hash = {
577 # header => [$header],
578 # source => [$source],
579 # target => [$target],
580 # $header => [$header_value],
581 # };
582 # daemon_log("create_xml_hash:", 7),
583 # chomp(my $tmp = Dumper $hash);
584 # daemon_log("\t$tmp\n", 7);
585 # return $hash
586 #}
589 #=== FUNCTION ================================================================
590 # NAME: create_xml_string
591 # PARAMETERS:
592 # RETURNS:
593 # DESCRIPTION:
594 #===============================================================================
595 #sub create_xml_string {
596 # my ($xml_hash) = @_ ;
597 # my $xml_string = $xml->XMLout($xml_hash, RootName => 'xml');
598 # $xml_string =~ s/[\n]+//g;
599 # daemon_log("create_xml_string:\n\t$xml_string\n", 7);
600 # return $xml_string;
601 #}
604 #=== FUNCTION ================================================================
605 # NAME: add_content2xml_hash
606 # PARAMETERS:
607 # RETURNS:
608 # DESCRIPTION:
609 #===============================================================================
610 #sub add_content2xml_hash {
611 # my ($xml_ref, $element, $content) = @_;
612 # if(not exists $$xml_ref{$element} ) {
613 # $$xml_ref{$element} = [];
614 # }
615 # my $tmp = $$xml_ref{$element};
616 # push(@$tmp, $content);
617 # return;
618 #}
621 #=== FUNCTION ================================================================
622 # NAME: get_content_from_xml_hash
623 # PARAMETERS: ref : reference to the xml hash
624 # string: key of the value you want
625 # RETURNS: STRING AND ARRAY
626 # DESCRIPTION: if key of the hash is either 'header', 'target' or 'source' the
627 # function returns a string cause it is expected that these keys
628 # do just have one value, all other keys returns an array!!!
629 #===============================================================================
630 #sub get_content_from_xml_hash {
631 # my ($xml_ref, $element) = @_;
632 # my $result = $xml_ref->{$element};
633 # if( $element eq "header" || $element eq "target" || $element eq "source") {
634 # return @$result[0];
635 # }
636 # return @$result;
637 #}
639 # my ($xml_ref, $element) = @_;
640 # if (exists $xml_ref->{$element}) {
641 # my $result = $xml_ref->{$element};
642 # if( $element eq "header" || $element eq "target" || $element eq "source") {
643 # return @$result[0];
644 # } else {
645 # return @$result;
646 # }
647 #
648 # } else {
649 # my $result = ();
650 # return @$result;
651 # }
652 #}
655 #=== FUNCTION ================================================================
656 # NAME: encrypt_msg
657 # PARAMETERS:
658 # RETURNS:
659 # DESCRIPTION:
660 #===============================================================================
661 #sub encrypt_msg {
662 # my ($msg, $my_cipher) = @_;
663 # if(not defined $my_cipher) { print "no cipher object\n"; }
664 # $msg = "\0"x(16-length($msg)%16).$msg;
665 # my $crypted_msg = $my_cipher->encrypt($msg);
666 # chomp($crypted_msg = &encode_base64($crypted_msg));
667 # return $crypted_msg;
668 #}
671 #=== FUNCTION ================================================================
672 # NAME: decrypt_msg
673 # PARAMETERS:
674 # RETURNS:
675 # DESCRIPTION:
676 #===============================================================================
677 #sub decrypt_msg {
678 # my ($crypted_msg, $my_cipher) = @_ ;
679 # $crypted_msg = &decode_base64($crypted_msg);
680 # my $msg = $my_cipher->decrypt($crypted_msg);
681 # $msg =~ s/\0*//g;
682 # return $msg;
683 #}
686 #=== FUNCTION ================================================================
687 # NAME: create_ciphering
688 # PARAMETERS:
689 # RETURNS: cipher object
690 # DESCRIPTION:
691 #===============================================================================
692 #sub create_ciphering {
693 # my ($passwd) = @_;
694 # $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
695 # my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
696 #
697 # #daemon_log("iv: $iv", 7);
698 # #daemon_log("key: $passwd", 7);
699 # my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
700 # $my_cipher->set_iv($iv);
701 # return $my_cipher;
702 #}
705 #=== FUNCTION ================================================================
706 # NAME: create_passwd
707 # PARAMETERS:
708 # RETURNS: cipher object
709 # DESCRIPTION:
710 #===============================================================================
711 sub create_passwd {
712 my $new_passwd = "";
713 for(my $i=0; $i<31; $i++) {
714 $new_passwd .= ("a".."z","A".."Z",0..9)[int(rand(62))]
715 }
717 return $new_passwd;
718 }
721 #=== FUNCTION ================================================================
722 # NAME: send_msg_hash2address
723 # PARAMETERS: msg string - xml message
724 # PeerAddr string - socket address to send msg
725 # PeerPort string - socket port, if not included in socket address
726 # RETURNS: nothing
727 # DESCRIPTION: ????
728 #===============================================================================
729 #sub send_msg_hash2address {
730 # my ($msg_hash, $address, $passwd) = @_ ;
731 #
732 # # fetch header for logging
733 # my $header = @{$msg_hash->{header}}[0];
734 #
735 # # generiere xml string
736 # my $msg_xml = &create_xml_string($msg_hash);
737 #
738 # # hole das entsprechende passwd aus dem hash
739 # if(not defined $passwd) {
740 # if(exists $known_hosts->{$address}) {
741 # $passwd = $known_hosts->{$address}->{passwd};
742 # } elsif ($address eq $server_address) {
743 # $passwd = $server_passwd;
744 # } else {
745 # daemon_log("$address not known, neither as server nor as client", 1);
746 # return "failed";
747 # }
748 # }
749 #
750 # # erzeuge ein ciphering object
751 # my $act_cipher = &create_ciphering($passwd);
752 #
753 # # encrypt xml msg
754 # my $crypted_msg = &encrypt_msg($msg_xml, $act_cipher);
755 #
756 # # öffne socket
757 # my $socket = &open_socket($address);
758 # if(not defined $socket){
759 # daemon_log("cannot open socket to $address, server not reachable", 1);
760 # daemon_log("cannot send '$header'-msg", 1);
761 # return "failed";
762 # }
763 #
764 # # versende xml msg
765 # print $socket $crypted_msg."\n";
766 #
767 # # schlieĂźe socket
768 # close $socket;
769 #
770 # daemon_log("send '$header'-msg to $address", 5);
771 # daemon_log("crypted_msg:\n\t$crypted_msg", 7);
772 #
773 # return "done";
774 #}
777 #=== FUNCTION ================================================================
778 # NAME: open_socket
779 # PARAMETERS: PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
780 # [PeerPort] string necessary if port not appended by PeerAddr
781 # RETURNS: socket IO::Socket::INET
782 # DESCRIPTION:
783 #===============================================================================
784 sub open_socket {
785 my ($PeerAddr, $PeerPort) = @_ ;
786 if(defined($PeerPort)){
787 $PeerAddr = $PeerAddr.":".$PeerPort;
788 }
789 my $socket;
790 $socket = new IO::Socket::INET(PeerAddr => $PeerAddr ,
791 Porto => "tcp" ,
792 Type => SOCK_STREAM,
793 Timeout => 5,
794 );
795 if(not defined $socket) {
796 #daemon_log("cannot connect to socket at $PeerAddr, $@\n");
797 return;
798 }
799 daemon_log("open_socket:\n\t$PeerAddr", 7);
800 return $socket;
801 }
804 #=== FUNCTION ================================================================
805 # NAME: read_from_socket
806 # PARAMETERS: socket fh -
807 # RETURNS: result string - readed characters from socket
808 # DESCRIPTION: reads data from socket in 16 byte steps
809 #===============================================================================
810 sub read_from_socket {
811 my ($socket) = @_;
812 my $result = "";
814 $socket->blocking(1);
815 $result = <$socket>;
817 $socket->blocking(0);
818 while ( my $char = <$socket> ) {
819 if (not defined $char) { last }
820 $result .= $char;
821 }
822 return $result;
826 # my ($socket) = @_;
827 # my $result = "";
828 # my $len = 16;
829 # while($len == 16){
830 # my $char;
831 # $len = sysread($socket, $char, 16);
832 # if($len != 16) { last }
833 # if($len != 16) { last }
834 # $result .= $char;
835 # }
836 # return $result;
837 }
840 #=== FUNCTION ================================================================
841 # NAME: print_known_hosts_hash
842 # PARAMETERS:
843 # RETURNS:
844 # DESCRIPTION:
845 #===============================================================================
846 sub print_known_hosts_hash {
847 my ($tmp) = @_;
848 print "####################################\n";
849 print "# status of known_hosts\n";
850 my $hosts;
851 my $host_hash;
852 my @hosts = keys %$known_hosts;
853 foreach my $host (@hosts) {
854 #my @elements = keys %$known_hosts->{$host};
855 my $status = $known_hosts->{$host}->{status} ;
856 my $passwd = $known_hosts->{$host}->{passwd};
857 my $timestamp = $known_hosts->{$host}->{timestamp};
858 print "$host\n";
859 print "\t$status\n";
860 print "\t$passwd\n";
861 print "\t$timestamp\n";
862 }
863 print "####################################\n";
864 return;
865 }
867 #=== FUNCTION ================================================================
868 # NAME:
869 # PARAMETERS:
870 # RETURNS:
871 # DESCRIPTION:
872 #===============================================================================
873 sub create_known_hosts_entry {
874 my ($hostname) = @_;
875 $known_hosts->{$hostname} = {};
876 $known_hosts->{$hostname}->{status} = "none";
877 $known_hosts->{$hostname}->{passwd} = "none";
878 $known_hosts->{$hostname}->{timestamp} = "none";
879 return;
880 }
883 #=== FUNCTION ================================================================
884 # NAME:
885 # PARAMETERS:
886 # RETURNS:
887 # DESCRIPTION:
888 #===============================================================================
889 sub update_known_hosts_entry {
890 my ($hostname, $status, $passwd, $timestamp) = @_;
891 my ($seconds, $minutes, $hours, $monthday, $month,
892 $year, $weekday, $yearday, $sommertime) = localtime(time);
893 $hours = $hours < 10 ? $hours = "0".$hours : $hours;
894 $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
895 $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
896 $month+=1;
897 $month = $month < 10 ? $month = "0".$month : $month;
898 $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
899 $year+=1900;
900 my $t = "$year$month$monthday$hours$minutes$seconds";
902 if($status) {
903 $known_hosts->{$hostname}->{status} = $status;
904 }
905 if($passwd) {
906 $known_hosts->{$hostname}->{passwd} = $passwd;
907 }
908 if($timestamp) {
909 $t = $timestamp;
910 }
911 $known_hosts->{$hostname}->{timestamp} = $t;
912 return;
913 }
916 #=== FUNCTION ================================================================
917 # NAME:
918 # PARAMETERS:
919 # RETURNS:
920 # DESCRIPTION:
921 #===============================================================================
922 sub add_content2known_hosts {
923 my ($hostname, $element, $content) = @_;
924 my ($seconds, $minutes, $hours, $monthday, $month,
925 $year, $weekday, $yearday, $sommertime) = localtime(time);
926 $hours = $hours < 10 ? $hours = "0".$hours : $hours;
927 $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
928 $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
929 $month+=1;
930 $month = $month < 10 ? $month = "0".$month : $month;
931 $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
932 $year+=1900;
933 my $t = "$year$month$monthday$hours$minutes$seconds";
935 $known_hosts->{$hostname}->{$element} = $content;
936 $known_hosts->{$hostname}->{timestamp} = $t;
937 return;
938 }
941 #=== FUNCTION ================================================================
942 # NAME:
943 # PARAMETERS:
944 # RETURNS:
945 # DESCRIPTION:
946 #===============================================================================
947 sub process_incoming_msg {
948 my ($crypted_msg) = @_;
949 if(not defined $crypted_msg) {
950 daemon_log("function 'process_incoming_msg': got no msg", 7);
951 }
952 $crypted_msg =~ /^([\s\S]*?)\.(\d{1,3}?)\.(\d{1,3}?)\.(\d{1,3}?)\.(\d{1,3}?)$/;
953 $crypted_msg = $1;
954 my $host = sprintf("%s.%s.%s.%s", $2, $3, $4, $5);
955 daemon_log("msg from host:", 1);
956 daemon_log("\t$host", 1);
957 daemon_log("crypted msg:", 7);
958 daemon_log("\t$crypted_msg", 7);
960 my $act_cipher = &create_ciphering($server_passwd);
962 # try to decrypt incoming msg
963 my ($msg, $msg_hash);
964 eval{
965 $msg = &decrypt_msg($crypted_msg, $act_cipher);
966 $msg_hash = $xml->XMLin($msg, ForceArray=>1);
967 };
968 if($@) {
969 daemon_log("ERROR: incoming msg cannot be decrypted with server passwd", 1);
970 return;
971 }
973 my $header = @{$msg_hash->{header}}[0];
975 daemon_log("receive '$header' from $host", 1);
977 #check whether msg to process is a event
978 opendir(DIR, $event_dir)
979 or daemon_log("cannot find directory $event_dir, no events specified", 5);
980 my $file_name;
981 while(defined($file_name = readdir(DIR))){
982 if ($file_name eq "." || $file_name eq "..") {
983 next;
984 }
985 if ($file_name eq $header) {
986 my $cmd = "$event_dir/$file_name '$msg'";
987 my $result_xml = "";
988 open(PIPE, "$cmd 2>&1 |");
989 while(<PIPE>) {
990 $result_xml.=$_;
991 last;
992 }
993 close(PIPE);
994 my $res_hash = &transform_msg2hash($result_xml);
995 my $res_target = @{$res_hash->{target}}[0];
996 &send_msg_hash2address($res_hash, $server_address);
998 return;
999 }
1000 }
1001 close(DIR);
1002 daemon_log("could not assign the msg $header to an event", 5);
1004 if ($header eq 'new_ldap_config') { if ($ldap_enabled == 1) {&new_ldap_config($msg_hash)}}
1005 elsif ($header eq 'ping') { &got_ping($msg_hash) }
1006 elsif ($header eq 'wake_up') { &execute_event($msg_hash)}
1007 elsif ($header eq 'new_passwd') { &new_passwd()}
1008 elsif ($header eq 'detect_hardware') { &detect_hardware()}
1009 else { daemon_log("ERROR: no function assigned to msg $header", 5) }
1011 return;
1012 }
1015 #=== FUNCTION ================================================================
1016 # NAME:
1017 # PARAMETERS:
1018 # RETURNS:
1019 # DESCRIPTION:
1020 #===============================================================================
1021 sub update_status {
1022 my ($new_status) = @_ ;
1023 my $out_hash = &create_xml_hash("update_status", $client_address, $server_address);
1024 &add_content2xml_hash($out_hash, "update_status", $new_status);
1025 &send_msg_hash2address($out_hash, $server_address);
1026 return;
1027 }
1030 #=== FUNCTION ================================================================
1031 # NAME:
1032 # PARAMETERS:
1033 # RETURNS:
1034 # DESCRIPTION:
1035 #===============================================================================
1036 sub server_leaving {
1037 my ($msg_hash) = @_ ;
1038 my $source = &get_content_from_xml_hash("source");
1039 my $header = &get_content_from_xml_hash("header");
1041 daemon_log("gosa daemon $source is going down, cause registration procedure", 1);
1042 my $server_address = "none";
1043 my $server_passwd = "none";
1044 my $server_cipher = "none";
1046 # reinitialization of default values in config file
1047 &read_configfile;
1049 # registrated at new daemon
1050 ®ister_at_server();
1052 return;
1053 }
1056 sub got_ping {
1057 my ($msg_hash) = @_ ;
1059 my $source = &get_content_from_xml_hash($msg_hash, 'source');
1060 my $target = &get_content_from_xml_hash($msg_hash, 'target');
1061 my $header = &get_content_from_xml_hash($msg_hash, 'header');
1063 &add_content2known_hosts(hostname=>$target, status=>$header);
1065 my $out_hash = &create_xml_hash("got_ping", $target, $source);
1066 &send_msg_hash2address($out_hash, $source, $server_passwd);
1068 return;
1069 }
1072 sub new_ldap_config {
1073 my ($msg_hash) = @_ ;
1074 my $element;
1075 my @ldap_uris;
1076 my $ldap_base;
1077 my @ldap_options;
1078 my @pam_options;
1079 my @nss_options;
1080 my $goto_admin;
1081 my $goto_secret;
1082 my $admin_base= "";
1083 my $department= "";
1084 my $unit_tag;
1086 # Transform input into array
1087 while ( my ($key, $value) = each(%$msg_hash) ) {
1088 if ($key =~ /^(source|target|header)$/) {
1089 next;
1090 }
1092 foreach $element (@$value) {
1093 if ($key =~ /^ldap_uri$/) {
1094 push (@ldap_uris, $element);
1095 next;
1096 }
1097 if ($key =~ /^ldap_base$/) {
1098 $ldap_base= $element;
1099 next;
1100 }
1101 if ($key =~ /^goto_admin$/) {
1102 $goto_admin= $element;
1103 next;
1104 }
1105 if ($key =~ /^goto_secret$/) {
1106 $goto_secret= $element;
1107 next;
1108 }
1109 if ($key =~ /^ldap_cfg$/) {
1110 push (@ldap_options, "$element");
1111 next;
1112 }
1113 if ($key =~ /^pam_cfg$/) {
1114 push (@pam_options, "$element");
1115 next;
1116 }
1117 if ($key =~ /^nss_cfg$/) {
1118 push (@nss_options, "$element");
1119 next;
1120 }
1121 if ($key =~ /^admin_base$/) {
1122 $admin_base= $element;
1123 next;
1124 }
1125 if ($key =~ /^department$/) {
1126 $department= $element;
1127 next;
1128 }
1129 if ($key =~ /^unit_tag$/) {
1130 $unit_tag= $element;
1131 next;
1132 }
1133 }
1134 }
1136 # Unit tagging enabled?
1137 if (defined $unit_tag){
1138 push (@pam_options, "pam_filter gosaUnitTag=$unit_tag");
1139 push (@nss_options, "nss_base_passwd $admin_base?sub?gosaUnitTag=$unit_tag");
1140 push (@nss_options, "nss_base_group $admin_base?sub?gosaUnitTag=$unit_tag");
1141 }
1143 # Setup ldap.conf
1144 my $file1;
1145 my $file2;
1146 open(file1, "> $ldap_config");
1147 print file1 "# This file was automatically generated by gosa-si-client. Do not change.\n";
1148 print file1 "URI";
1149 foreach $element (@ldap_uris) {
1150 print file1 " $element";
1151 }
1152 print file1 "\nBASE $ldap_base\n";
1153 foreach $element (@ldap_options) {
1154 print file1 "$element\n";
1155 }
1156 close (file1);
1157 daemon_log("wrote $ldap_config", 5);
1159 # Setup pam_ldap.conf / libnss_ldap.conf
1160 open(file1, "> $pam_config");
1161 open(file2, "> $nss_config");
1162 print file1 "# This file was automatically generated by gosa-si-client. Do not change.\n";
1163 print file2 "# This file was automatically generated by gosa-si-client. Do not change.\n";
1164 print file1 "uri";
1165 print file2 "uri";
1166 foreach $element (@ldap_uris) {
1167 print file1 " $element";
1168 print file2 " $element";
1169 }
1170 print file1 "\nbase $ldap_base\n";
1171 print file2 "\nbase $ldap_base\n";
1172 foreach $element (@pam_options) {
1173 print file1 "$element\n";
1174 }
1175 foreach $element (@nss_options) {
1176 print file2 "$element\n";
1177 }
1178 close (file2);
1179 daemon_log("wrote $nss_config", 5);
1180 close (file1);
1181 daemon_log("wrote $pam_config", 5);
1183 # Create goto.secrets if told so - for compatibility reasons
1184 if (defined $goto_admin){
1185 open(file1, "> /etc/goto/secret");
1186 close(file1);
1187 chown(0,0, "/etc/goto/secret");
1188 chmod(0600, "/etc/goto/secret");
1189 open(file1, "> /etc/goto/secret");
1190 print file1 "GOTOADMIN=\"$goto_admin\"\nGOTOSECRET=\"$goto_secret\"\n";
1191 close(file1);
1192 daemon_log("wrote /etc/goto/secret", 5);
1193 }
1197 # Write shell based config
1198 my $cfg_name= dirname($ldap_config)."/ldap-shell.conf";
1199 open(file1, "> $cfg_name");
1200 print file1 "LDAP_BASE=\"$ldap_base\"\n";
1201 print file1 "ADMIN_BASE=\"$admin_base\"\n";
1202 print file1 "DEPARTMENT=\"$department\"\n";
1203 print file1 "UNIT_TAG=\"".(defined $unit_tag ? "$unit_tag" : "")."\"\n";
1204 print file1 "UNIT_TAG_FILTER=\"".(defined $unit_tag ? "(gosaUnitTag=$unit_tag)" : "")."\"\n";
1205 close(file1);
1206 daemon_log("wrote $cfg_name", 5);
1208 return;
1210 }
1213 sub execute_event {
1214 my ($msg_hash)= @_;
1215 my $configdir= '/etc/gosa-si/client/events/';
1216 my $result;
1218 my $header = &get_content_from_xml_hash($msg_hash, 'header');
1219 my $source = &get_content_from_xml_hash($msg_hash, 'source');
1220 my $target = &get_content_from_xml_hash($msg_hash, 'target');
1223 if((not defined $source)
1224 && (not defined $target)
1225 && (not defined $header)) {
1226 daemon_log("ERROR: Entries missing in XML msg for gosa events under $configdir");
1227 } else {
1228 my $parameters="";
1229 my @params = &get_content_from_xml_hash($msg_hash, $header);
1230 my $params = join(", ", @params);
1231 daemon_log("execute_event: got parameters: $params", 5);
1233 if (@params) {
1234 foreach my $param (@params) {
1235 my $param_value = (&get_content_from_xml_hash($msg_hash, $param))[0];
1236 daemon_log("execute_event: parameter -> value: $param -> $param_value", 7);
1237 $parameters.= " ".$param_value;
1238 }
1239 }
1241 my $cmd= $configdir.$header."$parameters";
1242 daemon_log("execute_event: executing cmd: $cmd", 7);
1243 $result= "";
1244 open(PIPE, "$cmd 2>&1 |");
1245 while(<PIPE>) {
1246 $result.=$_;
1247 }
1248 close(PIPE);
1249 }
1251 # process the event result
1254 return;
1255 }
1258 sub new_passwd {
1259 # my ($msg_hash) = @_ ;
1260 my $new_server_passwd = &create_passwd();
1261 my $new_server_cipher = &create_ciphering($new_server_passwd);
1263 my $out_hash = &create_xml_hash("new_passwd", $client_address, $server_address, $new_server_passwd);
1265 &send_msg_hash2address($out_hash, $server_address, $server_passwd);
1267 $server_passwd = $new_server_passwd;
1268 $server_cipher = $new_server_cipher;
1269 return;
1270 }
1272 sub generate_hw_digest {
1273 my $hw_data;
1274 foreach my $line (split /\n/, `cat /proc/bus/pci/devices`) {
1275 $hw_data.= sprintf "%s", $line =~ /[^\s]+\s([^\s]+)\s.*/;
1276 }
1277 return(md5_base64($hw_data));
1278 }
1280 sub detect_hardware {
1281 my $hwinfo= `which hwinfo`;
1282 chomp $hwinfo;
1284 if (!(defined($hwinfo) && length($hwinfo) > 0)) {
1285 &main::daemon_log("ERROR: hwinfo was not found in \$PATH! Hardware detection will not work!", 1);
1286 return;
1287 }
1289 my $result= {
1290 macAddress => $client_mac_address,
1291 gotoXMonitor => "",
1292 gotoXDriver => "",
1293 gotoXMouseType => "",
1294 gotoXMouseport => "",
1295 gotoXkbModel => "",
1296 gotoXHsync => "",
1297 gotoXVsync => "",
1298 gotoXResolution => "",
1299 ghUsbSupport => "",
1300 gotoSndModule => "",
1301 ghGfxAdapter => "",
1302 ghNetNic => "",
1303 ghSoundAdapter => "",
1304 ghMemSize => "",
1305 ghCpuType => "",
1306 gotoModules => [],
1307 ghIdeDev => [],
1308 ghScsiDev => [],
1309 };
1311 &main::daemon_log("Starting hardware detection", 4);
1312 my $gfxcard= `$hwinfo --gfxcard`;
1313 my $primary_adapter= $1 if $gfxcard =~ /^Primary display adapter:\s#(\d+)\n/m;
1314 if(defined($primary_adapter)) {
1315 ($result->{ghGfxAdapter}, $result->{gotoXDriver}) = ($1,$2) if
1316 $gfxcard =~ /$primary_adapter:.*?Model:\s\"([^\"]*)\".*?Server Module:\s(\w*).*?\n\n/s;
1317 }
1318 my $monitor= `$hwinfo --monitor`;
1319 my $primary_monitor= $1 if $monitor =~ /^(\d*):.*/m;
1320 if(defined($primary_monitor)) {
1321 ($result->{gotoXMonitor}, $result->{gotoXResolution}, $result->{gotoXVsync}, $result->{gotoXHsync})= ($1,$2,$3,$4) if
1322 $monitor =~ /$primary_monitor:\s.*?Model:\s\"(.*?)\".*?Max\.\sResolution:\s([0-9x]*).*?Vert\.\sSync\sRange:\s([\d\-]*)\sHz.*?Hor\.\sSync\sRange:\s([\d\-]*)\skHz.*/s;
1323 }
1325 if(length($result->{gotoXHsync}) == 0) {
1326 # set default values
1327 $result->{gotoXHsync} = "30+50";
1328 $result->{gotoXVsync} = "30+90";
1329 }
1331 my $mouse= `$hwinfo --mouse`;
1332 my $primary_mouse= $1 if $mouse =~ /^(\d*):.*/m;
1333 if(defined($primary_mouse)) {
1334 ($result->{gotoXMouseport}, $result->{gotoXMouseType}) = ($1,$2) if
1335 $mouse =~ /$primary_mouse:\s.*?Device\sFile:\s(.*?)\s.*?XFree86\sProtocol:\s(.*?)\n.*?/s;
1336 }
1338 my $sound= `$hwinfo --sound`;
1339 my $primary_sound= $1 if $sound =~ /^(\d*):.*/m;
1340 if(defined($primary_sound)) {
1341 ($result->{ghSoundAdapter}, $result->{gotoSndModule})= ($1,$2) if
1342 $sound =~ /$primary_sound:\s.*?Model:\s\"(.*?)\".*?Driver\sModules:\s\"(.*?)\".*/s;
1343 }
1345 my $netcard= `hwinfo --netcard`;
1346 my $primary_netcard= $1 if $netcard =~ /^(\d*):.*/m;
1347 if(defined($primary_netcard)) {
1348 $result->{ghNetNic}= $1 if $netcard =~ /$primary_netcard:\s.*?Model:\s\"(.*?)\".*/s;
1349 }
1351 my $keyboard= `hwinfo --keyboard`;
1352 my $primary_keyboard= $1 if $keyboard =~ /^(\d*):.*/m;
1353 if(defined($primary_keyboard)) {
1354 $result->{gotoXkbModel}= $1 if $keyboard =~ /$primary_keyboard:\s.*?XkbModel:\s(.*?)\n.*/s;
1355 }
1357 $result->{ghCpuType}= sprintf "%s / %s - %s",
1358 `cat /proc/cpuinfo` =~ /.*?vendor_id\s+:\s(.*?)\n.*?model\sname\s+:\s(.*?)\n.*?cpu\sMHz\s+:\s(.*?)\n.*/s;
1359 $result->{ghMemSize}= $1 if `cat /proc/meminfo` =~ /^MemTotal:\s+(.*?)\skB.*/s;
1361 my @gotoModules=();
1362 for my $line(`lsmod`) {
1363 if (($line =~ /^Module.*$/) or ($line =~ /^snd.*$/)) {
1364 next;
1365 } else {
1366 push @gotoModules, $1 if $line =~ /^(\w*).*$/
1367 }
1368 }
1369 my %seen = ();
1371 # Remove duplicates and save
1372 push @{$result->{gotoModules}}, grep { ! $seen{$_} ++ } @gotoModules;
1374 $result->{ghUsbSupport} = (-d "/proc/bus/usb")?"true":"false";
1376 foreach my $device(`hwinfo --ide` =~ /^.*?Model:\s\"(.*?)\".*$/mg) {
1377 push @{$result->{ghIdeDev}}, $device;
1378 }
1380 foreach my $device(`hwinfo --scsi` =~ /^.*?Model:\s\"(.*?)\".*$/mg) {
1381 push @{$result->{ghScsiDev}}, $device;
1382 }
1384 &main::daemon_log("Hardware detection done!", 4);
1386 return &send_msg_hash2address(
1387 &create_xml_hash("detected_hardware", $client_address, $server_address, $result),
1388 $server_address,
1389 $server_passwd
1390 );
1391 }
1393 #==== MAIN = main ==============================================================
1395 # parse commandline options
1396 Getopt::Long::Configure( "bundling" );
1397 GetOptions("h|help" => \&usage,
1398 "c|config=s" => \$cfg_file,
1399 "f|foreground" => \$foreground,
1400 "v|verbose+" => \$verbose,
1401 );
1403 # read and set config parameters
1404 &check_cmdline_param ;
1405 &read_configfile;
1406 &check_pid;
1408 if ( ! $foreground ) {
1409 open STDIN, '/dev/null' or die "Can’t read /dev/null: $!";
1410 open STDOUT, '>>/dev/null' or die "Can't write to /dev/null: $!";
1411 open STDERR, '>>/dev/null' or die "Can't write to /dev/null: $!";
1412 }
1415 # restart daemon log file
1416 if(-e $log_file ) { unlink $log_file }
1417 daemon_log(" ", 1);
1418 daemon_log("$0 started!", 1);
1420 # Just fork, if we"re not in foreground mode
1421 if( ! $foreground ) { $pid = fork(); }
1422 else { $pid = $$; }
1424 # Do something useful - put our PID into the pid_file
1425 if( 0 != $pid ) {
1426 open( LOCK_FILE, ">$pid_file" );
1427 print LOCK_FILE "$pid\n";
1428 close( LOCK_FILE );
1429 if( !$foreground ) { exit( 0 ) };
1430 }
1432 # detect own ip and mac address
1433 $network_interface= &get_interface_for_ip($client_ip);
1434 $client_mac_address= &get_mac($network_interface);
1436 # ($client_ip, $client_mac_address) = &get_ip_and_mac();
1437 #if (not defined $client_ip) {
1438 # die "EXIT: ip address of $0 could not be detected";
1439 #}
1440 daemon_log("client ip address detected: $client_ip", 1);
1441 daemon_log("client mac address detected: $client_mac_address", 1);
1443 # prepare variables
1444 if (defined $server_ip && defined $server_port) {
1445 $server_address = $server_ip.":".$server_port;
1446 }
1448 # this is necessary that gosa-si-server knowns to which ip-address he can send msgs
1449 if( $client_ip eq "0.0.0.0" ) {
1450 $client_ip = "127.0.0.1";
1451 }
1452 $client_address = $client_ip.":".$client_port;
1454 # setup xml parser
1455 $xml = new XML::Simple();
1457 # compute hardware checksum
1458 $gotoHardwareChecksum= &generate_hw_digest();
1460 # create input socket
1461 daemon_log(" ", 1);
1462 $rbits = $wbits = $ebits = "";
1463 $input_socket = IO::Socket::INET->new(LocalPort => $client_port,
1464 Type => SOCK_STREAM,
1465 Reuse => 1,
1466 Listen => 20,
1467 );
1468 if(not defined $input_socket){
1469 daemon_log("cannot be a tcp server at $client_port : $@\n");
1470 } else {
1471 daemon_log("start client at $client_address",1) ;
1472 vec($rbits, fileno $input_socket, 1) = 1;
1473 vec($wbits, fileno $input_socket, 1) = 1;
1474 }
1476 # register at server
1477 daemon_log(" ", 1);
1478 ®ister_at_server();
1481 ##############
1482 # Debugging
1483 #############
1484 #sleep(2);
1485 #&update_status("ich_bin_ein_neuer_status");
1487 ###################################
1488 #everything ready, okay, lets start
1489 ###################################
1490 while(1) {
1491 my ($rout, $wout);
1492 my $nf = select($rout=$rbits, $wout=$wbits, undef, undef);
1494 # error handling
1495 if($nf < 0 ) {
1496 }
1498 # something is coming in
1499 if(vec $rout, fileno $input_socket, 1) {
1500 my $client = $input_socket->accept();
1501 my $other_end = getpeername($client);
1503 if(not defined $other_end) {
1504 daemon_log("client cannot be identified: $!");
1505 } else {
1506 my ($port, $iaddr) = unpack_sockaddr_in($other_end);
1507 my $actual_ip = inet_ntoa($iaddr);
1508 daemon_log("accept client from $actual_ip", 5);
1509 my $in_msg = &read_from_socket($client);
1510 if(defined $in_msg){
1511 chomp($in_msg);
1512 $in_msg = $in_msg.".".$actual_ip;
1513 &process_incoming_msg($in_msg);
1515 }
1516 }
1517 }
1518 }