Code

Namensänderungen
[gosa.git] / contrib / daemon / gosa-sd-bus
1 #!/usr/bin/perl
2 #===============================================================================
3 #
4 #         FILE:  gosa-server
5 #
6 #        USAGE:  ./gosa-server
7 #
8 #  DESCRIPTION:
9 #
10 #      OPTIONS:  ---
11 # REQUIREMENTS:  ---
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 );
28 use IO::Socket::INET;
29 use Crypt::Rijndael;
30 use XML::Simple;
31 use Data::Dumper;
32 use Sys::Syslog qw( :DEFAULT setlogsock);
34 my ($cfg_file, %cfg_defaults, $foreground);
35 my ($bus_passwd, $bus_ip, $bus_port, $bus);
36 my ($pid_file, $procid, $pid, $log_file);
37 my (%free_child, %busy_child, $child_max, $child_min, %child_alive_time, $child_timeout);
39 $foreground = 0 ;
40 %cfg_defaults =
41 ("general" =>
42     {"log_file" => [\$log_file, "/var/run/gosa-server-bus.log"],
43     "pid_file" => [\$pid_file, "/var/run/gosa-server-bus.pid"],
45     },
46 "bus" =>
47     {"bus_passwd" => [\$bus_passwd, "tester78901234567890123456789012"],
48     "bus_ip" => [\$bus_ip, "10.89.1.155"],
49     "bus_port" => [\$bus_port, "10001"],
50     "child_max" => [\$child_max, 10],
51     "child_min" => [\$child_min, 3],
52     "child_timeout" => [\$child_timeout, 180],
53     }
54     );
56 #===  FUNCTION  ================================================================
57 #         NAME:  read_configfile
58 #   PARAMETERS:  cfg_file - string - 
59 #      RETURNS:  
60 #  DESCRIPTION: 
61 #===============================================================================
62 sub read_configfile {
63     my $cfg;
64     if( defined( $cfg_file) && ( length($cfg_file) > 0 )) {
65         if( -r $cfg_file ) {
66             $cfg = Config::IniFiles->new( -file => $cfg_file );
67         } else {
68             print STDERR "Couldn't read config file!";
69         }
70     } else {
71         $cfg = Config::IniFiles->new() ;
72     }
73     foreach my $section (keys %cfg_defaults) {
74         foreach my $param (keys %{$cfg_defaults{ $section }}) {
75             my $pinfo = $cfg_defaults{ $section }{ $param };
76             ${@$pinfo[ 0 ]} = $cfg->val( $section, $param, @$pinfo[ 1 ] );
77         }
78     }
79 }
81 #===  FUNCTION  ================================================================
82 #         NAME:  logging
83 #   PARAMETERS:  level - string - default 'info' 
84 #                msg - string - 
85 #                facility - string - default 'LOG_DAEMON' 
86 #      RETURNS:  
87 #  DESCRIPTION: 
88 #===============================================================================
89 sub daemon_log {
90     my( $msg, $level ) = @_;
91     if(not defined $msg) { return }
92     if(not defined $level) { $level = 1 }
93     if(defined $log_file){
94         open(LOG_HANDLE, ">>$log_file");
95         if(not defined open( LOG_HANDLE, ">>$log_file" )) { 
96             print STDERR "cannot open $log_file: $!";
97             return }
98         chomp($msg);
99         print LOG_HANDLE $msg."\n";
100         if(defined $foreground) { print $msg."\n" }
101     }
102     close( LOG_HANDLE );
103 #    my ($msg, $level, $facility) = @_;
104 #    if(not defined $msg) {return}
105 #    if(not defined $level) {$level = "info"}
106 #    if(not defined $facility) {$facility = "LOG_DAEMON"}
107 #    openlog($0, "pid,cons,", $facility);
108 #    syslog($level, $msg);
109 #    closelog;
110 #    return;
113 #===  FUNCTION  ================================================================
114 #         NAME: check_cmdline_param
115 #   PARAMETERS: 
116 #      RETURNS:  
117 #  DESCRIPTION: 
118 #===============================================================================
119 sub check_cmdline_param () {
120     my $err_config;
121     my $err_counter = 0;
122     if( not defined( $cfg_file)) {
123         $err_config = "please specify a config file";
124         $err_counter += 1;
125     }
126     if( $err_counter > 0 ) {
127         &usage( "", 1 );
128         if( defined( $err_config)) { print STDERR "$err_config\n"}
129         print STDERR "\n";
130         exit( -1 );
131     }
134 #===  FUNCTION  ================================================================
135 #         NAME: check_pid
136 #   PARAMETERS:
137 #      RETURNS:
138 #  DESCRIPTION:
139 #===============================================================================
140 sub check_pid {
141     $pid = -1;
142     # Check, if we are already running
143     if( open(LOCK_FILE, "<$pid_file") ) {
144         $pid = <LOCK_FILE>;
145         if( defined $pid ) {
146             chomp( $pid );
147             if( -f "/proc/$pid/stat" ) {
148                 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
149                 if( "faimond" eq $stat ) {
150                     close( LOCK_FILE );
151                     exit -1;
152                 }
153             }
154         }
155         close( LOCK_FILE );
156         unlink( $pid_file );
157     }
158     # Try to open PID file
159     if (!sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
160         my($msg) = "Couldn't obtain lockfile '$pid_file' ";
162         if (open(LOCK_FILE, '<', $pid_file)
163                 && ($pid = <LOCK_FILE>))
164         {
165             chomp($pid);
166             $msg .= "(PID $pid)\n";
167         } else {
168             $msg .= "(unable to read PID)\n";
169         }
170         if( ! ($foreground) ) {
171             openlog( "gosa-server-bus", "cons,pid", "daemon" );
172             syslog( "warning", $msg );
173             closelog();
174         }
175         else {
176             print( STDERR " $msg " );
177         }
178         exit( -1 );
179     }
182 #===  FUNCTION  ================================================================
183 #         NAME:  usage
184 #   PARAMETERS: 
185 #      RETURNS:  
186 #  DESCRIPTION: 
187 #===============================================================================
188 sub usage {
189         my( $text, $help ) = @_;
190         $text = undef if( "h" eq $text );
191         (defined $text) && print STDERR "\n$text\n";
192         if( (defined $help && $help) || (!defined $help && !defined $text) ) {
193                 print STDERR << "EOF" ;
194 usage: $0 [-hvf] [-c config]
196     -h        : this (help) message
197     -c <file> : config file
198     -v        : be verbose (multiple to increase verbosity)
199 EOF
200         }
201         print "\n" ;
204 sub sig_int_handler {
205     my ($signal) = @_;
206     if($bus){
207         close($bus);
208         print "$bus closed\n";
209     }
210     print "$signal\n";
211     exit(1);
213 $SIG{INT} = \&sig_int_handler;
215 #===  FUNCTION  ================================================================
216 #         NAME:  activating_child
217 #   PARAMETERS:
218 #      RETURNS:
219 #  DESCRIPTION:
220 #===============================================================================
221 sub activating_child {
222     my ($msg) = @_;
223     my $child = &get_processing_child();
224     my $pipe_wr = $$child{'pipe_wr'};
225     print "activating: childpid:$$child{'pid'}\n";
226     #print "activating: pipe_wr:$pipe_wr\n";
227     print $pipe_wr $msg."\n";
228     #print "activating: done\n";
229     return;
234 #===  FUNCTION  ================================================================
235 #         NAME:  get_processing_child
236 #   PARAMETERS:
237 #      RETURNS:
238 #  DESCRIPTION:
239 #===============================================================================
240 sub get_processing_child {
241     my $child;
242     # schaue durch alle %busy_child{pipe_wr} ob irgendwo 'done' drinsteht, wenn ja, dann setze das kind von busy auf free um
243     while(my ($key, $val) = each(%busy_child)) {
244         # test ob prozess noch existiert
245         my $exitus_pid = waitpid($key, WNOHANG);
246         if($exitus_pid != 0) {
247             delete $busy_child{$key};
248             print "prozess:$key wurde aus busy_child entfernt\n";
249             next;
250         }
252         # test ob prozess noch arbeitet
253         my $fh = $$val{'pipe_rd'};
254         $fh->blocking(0); 
255         my $child_answer;
256         if(not $child_answer = <$fh>) { next }
257         chomp($child_answer);
258         if($child_answer eq "done") {
259             delete $busy_child{$key};
260             $free_child{$key} = $val;
261         }
262     }
264     while(my ($key, $val) = each(%free_child)) {
265         my $exitus_pid = waitpid($key, WNOHANG);
266         if($exitus_pid != 0) {
267             delete $free_child{$key};
268             print "prozess:$key wurde aus free_child entfernt\n";
269         }
270         print "free child:$key\n";
271     }
274     # teste @free_child und @busy_child
275     my $free_len = scalar(keys(%free_child));
276     my $busy_len = scalar(keys(%busy_child));
277     print "free children $free_len, busy children $busy_len\n";
278     
279     # gibt es bereits ein freies kind, dann lass es arbeiten
280     if($free_len > 0){
281         my @keys = keys(%free_child);
282         $child = $free_child{$keys[0]};
283         if(defined $child) {
284             $busy_child{$$child{'pid'}} = $child ; 
285             delete $free_child{$$child{'pid'}};          
286         }
287         return $child;
288     }
289     
290     # no free child, try to fork another one 
291     if($free_len + $busy_len < $child_max) {
292                 
293         print "not enough children, create a new one\n";
295         # New pipes for communication
296         my( $PARENT_wr, $PARENT_rd );
297         my( $CHILD_wr, $CHILD_rd );
298         pipe( $CHILD_rd,  $PARENT_wr );
299         pipe( $PARENT_rd, $CHILD_wr  );
300         $PARENT_wr->autoflush(1);
301         $CHILD_wr->autoflush(1);
303         ############
304         # fork child
305         ############
306         my $child_pid = fork();
307         
308         #CHILD
309         if($child_pid == 0) {
310             # Close unused pipes
311             close( $CHILD_rd );
312             close( $CHILD_wr );
313             while( 1 ) {
314                 my $rbits = "";
315                 vec( $rbits, fileno $PARENT_rd , 1 ) = 1;
316                 my $nf = select($rbits, undef, undef, $child_timeout);
317                 if($nf < 0 ) {
318                     # wenn $nf < 1, error handling
319                     die "select(): $!\n";
320                 } elsif (! $nf) {
321                     # seit timeout ist nichts mehr passiert,
322                     print "timeout!!\n";
324                     # ist dieses kind nicht eines der letzenen $child_min, dann springe aus while schleife raus
325                     $free_len = scalar(keys(%free_child));
326                     $busy_len = scalar(keys(%busy_child));
327                     if($free_len + $busy_len >= $child_min) {
328                         last;
329                     } else {
330                         redo;
331                     }
332                 } 
334                 # ansonsten
335                 if ( vec $rbits, fileno $PARENT_rd, 1 ) {
336                     my $msg = <$PARENT_rd>;
337                     &process_incoming_msg($msg);
338                     
339                     # schreibe an den parent_wr zurück 'done' 
340                     print $PARENT_wr "done"."\n";
341                     
342                     redo;
343                 }
344             }
345             # child die die while-schleife verlassen haben, "stirbt!"
346             exit(0);
348         #PARENT
349         } else {
350             # Close unused pipes
351             close( $PARENT_rd );
352             close( $PARENT_wr );
353             
354             # add child to child alive hash
355             my %child_hash = (
356                     'pid' => $child_pid,
357                     'pipe_wr' => $CHILD_wr,
358                     'pipe_rd' => $CHILD_rd,
359                     );
361             $child = \%child_hash;
362             $busy_child{$$child{'pid'}} = $child;
363             return $child;
364         }
365     }
369 #===  FUNCTION  ================================================================
370 #         NAME:  process_incoming_msg
371 #   PARAMETERS:  
372 #      RETURNS:
373 #  DESCRIPTION:
374 #===============================================================================
375 sub process_incoming_msg {
376     my ($msg) = @_;
377     print "msg to process:$msg\n";
378     sleep(10);
379     return;
382 #==== MAIN = main ==============================================================
384 #  parse commandline options
385 Getopt::Long::Configure( "bundling" );
386 GetOptions("h|help" => \&usage,
387            "c|config=s" => \$cfg_file,
388            "f|foreground" => \$foreground,
389            );
391 #  read and set config parameters
392 &read_configfile;
393 &check_cmdline_param ;
394 &check_pid;
396 $SIG{CHLD} = 'IGNORE';
398 # restart daemon log file
399 if(-e $log_file ) { unlink $log_file }
400 daemon_log("started!");
402 # Just fork, if we"re not in foreground mode
403 if( ! $foreground ) { $pid = fork(); }
404 else { $pid = $$; }
406 # Do something useful - put our PID into the pid_file
407 if( 0 != $pid ) {
408     open( LOCK_FILE, ">$pid_file" );
409     print LOCK_FILE "$pid\n";
410     close( LOCK_FILE );
411     if( !$foreground ) { exit( 0 ) };
414 # open the bus socket
415 $bus = IO::Socket::INET->new(LocalPort => $bus_port,
416                             Type => SOCK_STREAM,
417                             Reuse => 1,
418                             Listen => 20,
419         ) or die "kann kein TCP-Server an Port $bus_port sein: $@\n";
421 # waiting for incoming msg
422 my $client;
423 while($client = $bus->accept()){ 
424     my $other_end = getpeername($client)
425         or die "Gegenstelle konnte nicht identifiziert werden: $!\n";
426     my ($port, $iaddr) = unpack_sockaddr_in($other_end);
427     my $actual_ip = inet_ntoa($iaddr);
428     my $claimed_hostname = gethostbyaddr($iaddr, AF_INET);
429     daemon_log("accept client from $actual_ip\n");
430     
431     my $in_msg;
432     chomp( $in_msg = <$client> );
433     
434     &activating_child($in_msg);
435     close($client);
440 #Struktur Parent:
441 # syslog(Parent gestartet)
443 # forken von min_child kindern
444 #   liste mit child refs
445 #   
446 # aufbau des sockets
447 # warten auf eingang
448 # kinder die älter als child_timeout sind werden beendet, aber nicht mehr als child_min
449 # wenn eingang da, abfrage, ist ein kind frei?
450 #   kind frei: kind nimmt eingang entgegen und arbeitet ihn ab
451 #   kind nicht frei: existieren bereits max_child kinder?
452 #       max ereicht: tue nichts, warte bis ein kind fertig ist
453 #       max nicht erreicht: forke neues kind