source: A2P/a2p/a2p-status.pl @ 10

Last change on this file since 10 was 3, checked in by guillaume, 17 years ago
  • AUTHORS: Ajout des différents contributeurs
  • COPYING: Ajout de la licence GPL v3
  • a2p: Préparation des sources pour leur publication sous GPL
  • Property svn:executable set to *
  • Property svn:keywords set to Id
File size: 13.8 KB
RevLine 
[3]1#!/usr/bin/perl
2#
3# Copyright (c) 2004-2007 - Consultas, PKG.fr
4#
5# This file is part of A2P.
6#
7# A2P is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation; either version 2 of the License, or
10# (at your option) any later version.
11#
12# A2P is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with A2P; if not, write to the Free Software
19# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20#
21# $Id: a2p-status.pl 3 2007-10-18 16:20:19Z guillaume $
22#
23# Simple service to synchronize Job STATUS with a DB
24#
25
26BEGIN {
27    use strict ;
28    use warnings ;
29    use POSIX 'setsid' ;
30    use A2P::Globals ;
31
32    our $VERSION = sprintf "%s", q$Rev: 1185 $ =~ /(\d[0-9.]+)\s+/ ;
33
34    if (defined($ENV{JAVA_HOME}) and $ENV{JAVA_HOME}) {
35        # Don't fork if launch by Eclipse, script launcher unset JAVA_HOME
36        $Progname = "a2p-eclipse" ;
37        $MUSTQUIT = 1 ;
38
39    } else {
40        # Daemonize early
41        my $forkpid = 0 ;
42        our $argument = $ARGV[0] || "" ;
43        chdir '/'                      or die "Can't chdir to /: $!";
44        open *STDIN , '<', '/dev/null' or die "Can't read from /dev/null: $!";
45        open *STDOUT, '>', $StdoutFile or die "Can't write to $StdoutFile: $!";
46           defined($forkpid = fork)       or die "Can't fork: $!";
47
48        # Eventually save son PID in system file provided as argument and exit
49        if ( $forkpid > 0 ) {
50            if ( length($argument) > 0 ) {
51                open *PID, '>', $argument
52                    or die "Can't open PID file '$argument': $!";
53                print PID $forkpid ;
54                close(PID);
55            }
56            exit(0);
57        }
58    }
59}
60our $VERSION  ;
61our $argument ;
62our $Progname ;
63our $MUSTQUIT ;
64
65#-------------------------------------------------------------------------------
66# Here we import any needed modules only after we are in background
67#-------------------------------------------------------------------------------
68
69use strict   ;
70use warnings ;
71use integer  ;
72use POSIX qw( :signal_h :sys_wait_h setsid );
73use Time::HiRes qw( usleep gettimeofday tv_interval );
74use A2P::Init   ;
75use A2P::Globals;
76use A2P::Syslog ;
77use A2P::Signal ;
78use A2P::Thread ;
79use A2P::JobStatus qw( sync_dbm_to_db get_status );
80use A2P::Signal 'LogSigMessage' ;
81use A2P::DB ;
82use A2P::DB qw( CheckJobsStatusTables CheckJobsQueueTable get_serviceid_info );
83use A2P::Infos ;
84use A2P::AfpJob ;
85
86&Debug(" Module load error: $! $? $@") if ( $! or $? or $@ );
87&Debug(" =============================START==============================");
88
89&setsid                     or die "Can't set a new session: $!";
90open *STDERR, '>&', *STDOUT or die "Can't redirect STDERR to STDOUT: $!";
91
92#-------------------------------------
93# Here we are in a daemonized process
94#-------------------------------------
95
96&Debug("Current progname = $0, version $VERSION, started");
97&Debug("Setting service name to $Progname");
98# Update system name of this daemon to be pretty with ps command
99$0 = $Progname ;
100
101# Reset error code
102$! = 0 ;
103
104################################################################################
105sub InitCheck {
106
107    # Read conf
108    &Init ;
109
110    # Check needed folders
111    my %Folders = (
112        SERVICE_TMP  =>  $SERVICE_TMP,
113        SHMDIR       =>  $SHMDIR
114        );
115
116    while ( my ($var, $Path) = each(%Folders) ) {
117        unless ( -d $Path ) {
118            &Info("Creating $Path folder as $var");
119            my $Folder = "" ;
120            foreach my $tmp_path ( split(m{[/]+}, $Path) ) {
121                $Folder .= "/$tmp_path" ;
122                mkdir $Folder , oct(775) unless ( -d $Folder );
123                &Error("Can't create '$Path' folder as $var: $!"), last
124                    unless ( -d $Folder );
125            }
126        }
127    }
128
129    # Initialize SERVICE_TMP folder
130    if ( -d $SERVICE_TMP ) {
131        unless ( system("touch $SERVICE_TMP/init_$Progname") == 0 ) {
132            &Error("Can't initialize $SERVICE_TMP folder: $!");
133            $MUSTQUIT = 1 ;
134        }
135
136    } else {
137        &Error("Can't initialize unavailable $SERVICE_TMP folder: $!");
138        $MUSTQUIT = 1 ;
139    }
140
141    # Test opening logfile in case of file logging
142    if ( $LOGFILENAME and ( $LOGFILE_VS_SYSLOG or $DEBUG_IN_FILE )) {
143        $! = 0 ;
144        unless (open(*LOG, '>', $LOGFILENAME)) {
145            $LOGFILE_VS_SYSLOG = 0 ;
146            $DEBUG_IN_FILE     = 0 ;
147            return &Error("Can't open '$LOGFILENAME': $!");
148        }
149
150        if ( $LOGFILE_PURGE ) {
151            &UPSTAT('PURGE-LOG');
152            truncate LOG , 0 ;
153            $LOGFILE_PURGE = 0 ;
154        }
155
156        close(LOG);
157    }
158
159    # Reset Debug comportement
160    &ResetDebug() ;
161
162    &Info("Configuration reloaded") if ($do_init);
163    $do_init = 0 ;
164}
165
166################################################################################
167
168&InitCheck( $do_init = 0 );
169
170################################################################################
171### Local variables
172################################################################################
173my @MarkTimer = &gettimeofday() ;
174
175# Status objects hash ref
176my $STATUS = {} ;
177my $factor = 0 ;
178my $mode   = 0 ;
179my $afpjob = undef ;
180my $try_connect = 1 ;
181my $connected = 0 ;
182my $not_connected_timer = undef ;
183my $delay = 60 ;
184my %lockid_list = () ;
185my $event_timer = undef ;
186
187# Initialization from DB
188my $services_infos ;
189$services_infos = &get_serviceid_info()
190    if (&a2p_db_connect() and &CheckJobsStatusTables());
191
192&Info("$Progname service v" . A2P_RPM_VERSION . " started");
193$STATS{'A2P-STATUS-VERSION'} = A2P_RPM_VERSION ;
194
195# Main loop
196while ( ! $MUSTQUIT ) {
197    &TIMESTAT('MainLoop');
198
199    &LogSigMessage();
200
201    A2P::Thread::self_TEST() if $do_test ;
202
203    @MarkTimer = &gettimeofday() , &Info("---MARK---")
204        unless ( &tv_interval(\@MarkTimer) < 1800 );
205
206    ##################################################################
207    # 1. Get a DB Access and check db tables
208    ##################################################################
209    $connected = 1
210        if (( ! $connected or $try_connect-- > 0 ) and &a2p_db_connect());
211
212    if ( $connected > 0 and &CheckJobsStatusTables and &CheckJobsQueueTable ) {
213        #############################
214        # 2. Read all status objects
215        #############################
216        &sync_dbm_to_db ;
217
218        ###########################################################
219        # 3. Check for events in jobs_queue table for current mode
220        ###########################################################
221        my @list = keys(%lockid_list) ;
222        if ( defined($services_infos) and @list ) {
223            my $count = 5 ;
224            my $lockid = shift @list ;
225            while ( $count-- ) {
226                my ( $serviceid, $name ) = @{$lockid_list{$lockid}} ;
227                #&Debug("Getting events for $name service")
228                #    if ($ADVANCED_DEBUGGING) ;
229
230                if (defined($afpjob) and $afpjob) {
231                    $afpjob = $afpjob->get_next_from_db($serviceid) ;
232                    if (ref($afpjob) =~ /^A2P::AfpJob/) {
233                        my $job = $afpjob->jobname ;
234                        # Got an event
235                        &UPSTAT('A2P-STATUS-GOT-EVENT-'.$lockid);
236                        &Debug("Got event for $name service on $job")
237                            if ($ADVANCED_DEBUGGING) ;
238
239                        # Update the Status from our memory
240                        my $status = &get_status($job);
241                        $afpjob->set_status_object($status) if ($status);
242
243                        # Update afpspool/service name
244                        my @service = ( $lockid, $name ) ;
245                        my $spool = $services_infos->get_afpspool_of(@service);
246                        my @spools = $services_infos->get_spools_of(@service) ;
247                        &Debug("Setting afpspool to $spool on $job")
248                            if ($ADVANCED_DEBUGGING) ;
249                        $afpjob->afpspool($spool);
250                        $afpjob->otherspools(@spools);
251                        $afpjob->service($name);
252
253                        # Handle event getting if deleting (chained events case)
254                        my $neg_is_deleted = $afpjob->handle_event
255                            or &Warn("Bad event handling on " . $job .
256                                " A2P::AfpJob object");
257
258                        # Synchronize us in jobs_queue table unless deleted
259                        &Warn("Bad event synchro on $job A2P::AfpJob")
260                            unless ( $neg_is_deleted < 0
261                            or $afpjob->sync_with_jobs_queue );
262
263                    } else {
264                        # Select next mode and/or next lockid to check
265                        if ($mode) {
266                            &UPSTAT('A2P-STATUS-ADV-EVENT-CHECKED-'.$lockid);
267                            $mode = 0 ;
268                            delete $lockid_list{$lockid} ;
269                            last unless @list ; # All possible event checked
270                            $lockid = shift @list ;
271
272                        } else {
273                            &UPSTAT('A2P-STATUS-USER-EVENT-CHECKED-'.$lockid);
274                            $mode = 1 ;
275                        }
276
277                        $afpjob = new A2P::AfpJob($mode) ;
278                        &Debug("Getting events for $name service with mode " .
279                            $mode ) if ($ADVANCED_DEBUGGING) ;
280
281                        $count ++ ;
282                        next ;
283                    }
284
285                } else {
286                    # Initialize with a fake afpjob
287                    $afpjob = new A2P::AfpJob($mode) ;
288                    $count ++ ;
289                    next ;
290                }
291           }
292
293        } elsif ( ref($services_infos) !~ /^A2P::Infos/i
294        and ( ! defined($event_timer) or &tv_interval($event_timer) > 2 )) {
295            # Reset event check timer
296            $event_timer = [ &gettimeofday() ];
297
298            $services_infos = &get_serviceid_info ;
299
300        } elsif ( ! defined($event_timer) or &tv_interval($event_timer) > 2 ) {
301            # Reset lockid list to check for events
302            foreach my $lockid ($services_infos->get_lockid_list()) {
303                my $sname = $services_infos->get_servicename_of($lockid) || '' ;
304                next unless $sname ;
305                $lockid_list{$lockid} = [
306                    $services_infos->get_serviceid_of($lockid), $sname ];
307            }
308
309            # Control list
310            @list = keys(%lockid_list) ;
311            &Debug("Checking events for services: " .
312                join( ', ', map { "$_ (@{$lockid_list{$_}})" } @list ))
313                if ($ADVANCED_DEBUGGING);
314
315            # Reset event check timer
316            $event_timer = [ &gettimeofday() ];
317        }
318
319        # TODO Check SHMDIR for not finished work
320
321        # Check if still connected to DB, this reconnect also to DB if a DB
322        # error is detected or if a reconnection DB timer is set
323        unless (&a2p_db_connected) {
324            &Info("Just disconnected from DB", "Retrying DB connection");
325            $connected = 0 ;
326        }
327
328    } elsif ( $connected > 0 ) {
329        # Log any pending warning from DBI API
330        &LogSigMessage();
331        # Not a connection error but a checktable error just warn and retry
332        # connection in 10 minutes
333        &Warn("Disconnecting from DB for 10 minutes");
334        &a2p_db_disconnect ;
335        $not_connected_timer = [ &gettimeofday() ];
336        $connected = $try_connect = -10 ;
337
338    } elsif ( ! $connected ) {
339        &Info("Disconnected from DB");
340        $not_connected_timer = [ &gettimeofday() ];
341        $connected-- ;
342        # Anyway reset delay to one minute
343        $delay = 60 ;
344
345    } elsif ( $connected <= 10 ) {
346        # Retry only after 10 min when more than 10 connections have failed
347        $delay = 600 ;
348    }
349
350    # Check disconnection timing when not connected
351    if ( $connected < 0 and &tv_interval($not_connected_timer) >= $delay ) {
352        my $delta = abs($connected) ;
353        $delta += 9 * ($connected - 10) if ( $delta >= 10 );
354        &Info("Retrying DB connection after $delta minutes disconnection");
355        $try_connect = 1 ;
356        $not_connected_timer = [ &gettimeofday() ];
357        $connected-- ;
358    }
359
360    #######################
361    # Check flags
362    #######################
363
364    # true after QUIT/TERM signal has been received
365    &TIMESTAT('MainLoop'), last if ( $do_quit );
366
367    # true after HUP signal has been received
368    &InitCheck() if ( $do_init );
369
370    # Check to do last commits
371    &check_commit() if ( $connected > 0 );
372
373    # Adapt USLEEP factor to be 1 when JobStatus has been found
374    # and grow up to 10 otherwise
375    if (keys(%{$STATUS})) {
376        $factor = 1 ;
377
378    } elsif ( $factor < 10 ) {
379        $factor ++ ;
380    }
381
382    # Avoid zombies sub-processes
383    waitpid(-1,&WNOHANG);
384
385    &TIMESTAT('MainLoop');
386    usleep $factor * $USLEEP ;
387    &UPSTAT('SLEEP-IN-MAINLOOP');
388}
389
390# We can safely activate debugging when quitting is requested
391$NO_SYSLOG_DEBUG = 0 if $SERVICE_DEBUG ;
392
393&a2p_db_disconnect();
394&LogSigMessage ;
395
396&Info("$Progname service is stopping");
397
398# Wait until every child has quit
399while ( my $pid = wait ) {
400    last if ( $pid < 0 );
401    &Debug("T$pid has terminated");
402}
403
404&Debug("PID file = '$argument'");
405
406if (defined($argument)) {
407    # Delete our PID in PID file
408    open *RET , '>', $argument or die "Can't open PID file '$argument': $!";
409    print RET "0" ;
410    close(RET);
411}
412
413&LogSigMessage ;
414&Info("$Progname service stopped");
415
416exit(0);
417
418END {
419    # Output statistics when quitting from a diagnostic service
420    A2P::Thread::self_TEST() if ($Progname =~ /^diagnostics/);
421
422    &Debug("=============================QUIT===============================");
423    &LogSigMessage ;
424}
Note: See TracBrowser for help on using the repository browser.