Subversion Repositories DevTools

Rev

Rev 7396 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1040 dpurdie 1
#! /usr/bin/perl
2
########################################################################
3
# Copyright (C) 2011 Vix-ERG Limited, All rights reserved
4
#
5
# Module name   : pl
6
# Compiler(s)   : Perl
7
#
8
# Description   : Start up the package blat system
9
#
10
# Usage         : blat 'tag' opr args
11
#
12
#......................................................................#
13
 
14
require 5.008_002;
15
use strict;
16
use warnings;
17
 
18
use Getopt::Long;                               # Option processing
19
use Pod::Usage;                                 # Required for help support
20
 
21
use POSIX qw(setsid strftime);
22
use POSIX ":sys_wait_h";
23
use File::Basename;
24
use File::Spec::Functions qw(rel2abs);
25
use Cwd;
26
 
27
use FindBin;                                    # Determine the current directory
28
use lib "$FindBin::Bin/lib";                    # Allow local libraries
29
 
30
use Utils;
31
use StdLogger;                                  # Log to sdtout
32
use Logger;                                     # Log to file
33
 
34
#
35
#   Globals
36
#
37
my $rootDir = $FindBin::Bin;
38
my $configFile = "/blat.cfg";
39
my $configPath = "${rootDir}/config${configFile}";
40
my $logger = StdLogger->new();
41
my %children;
42
my %dead;
43
my $conf;
44
my $mtimeConfig = 0;
45
 
46
#
47
#   Options
48
#
49
my $opt_help = 0;
50
my $opt_verbose = 0;
51
my $opt_nodaemonise = 0;
52
my $opt_config;
53
my $opt_basedir = $rootDir;
54
my $opt_pid;
55
 
56
#
57
#   Describe configuration parameters
58
#
59
my %cdata = (
60
    '.ignore'         => {'env\.(.+)'   => 'envVars', 'path\.(.+)'   => 'envPath'},
61
    'pidfile'         => {'mandatory' => 0    , 'fmt' => 'vfile'},
62
    'logfile'         => {'mandatory' => 1    , 'fmt' => 'vfile'},
63
    'logfile.size'    => {'default'   => '1M' , 'fmt' => 'size'},
64
    'logfile.count'   => {'default'   => 9    , 'fmt' => 'int'},
65
    'config'          => {'mandatory' => 1    , 'fmt' => 'dir'},
66
    'verbose'         => {'default'   => 0    , 'fmt' => 'int'},
67
    'configpoll'      => {'default'   => 10   , 'fmt' => 'period'},
68
);
69
 
70
#-------------------------------------------------------------------------------
71
# Function        : Mainline Entry Point
72
#
73
# Description     :
74
#
75
# Inputs          :
76
#
77
my $result = GetOptions (
78
                "help|h:+"      => \$opt_help,
79
                "manual:3"      => \$opt_help,
80
                "verbose:+"     => \$opt_verbose,
81
                "D"             => \$opt_nodaemonise,
82
                "config=s"      => \$opt_config,
83
                "dir=s"         => \$opt_basedir,
84
                "pid=s"         => \$opt_pid,
85
                );
86
 
87
                #
88
                #   UPDATE THE DOCUMENTATION AT THE END OF THIS FILE !!!
89
                #
90
 
91
#
92
#   Process help and manual options
93
#
94
pod2usage(-verbose => 0, -message => "dpkg_blat") if ($opt_help == 1 || ! $result || $#ARGV >= 0);
95
pod2usage(-verbose => 1) if ($opt_help == 2 );
96
pod2usage(-verbose => 2) if ($opt_help > 2);
97
 
98
#
99
#   Process options
100
#
101
chdir ($opt_basedir) || die ("Cannot 'cd' to $opt_basedir: $!\n");
102
print "Current Directory: ", getcwd() , "\n";
103
 
104
if ( $opt_config )
105
{
106
    $configPath = rel2abs( $opt_config );
107
    my($filename, $directories, $suffix) = fileparse($configPath);
108
    $configFile = '/' . $filename;
109
}
110
 
111
#-------------------------------------------------------------------------------
112
#   Read in the basic configuration
113
readConfig();
114
 
115
#
116
#   Only one instance
117
#
118
die "No PID file provided or configured" unless ( $conf->{pidfile} );
119
Utils::checkpid($conf);
120
 
121
#
122
#   Daemonise myself
123
#
124
unless ( $opt_nodaemonise )
125
{
126
    $logger->logmsg('Daemonizing...');
127
 
128
    my $msg = 'Can\'t read /dev/null:';
129
    open STDIN, '>/dev/null' or $logger->err("$msg $!");
130
    open STDOUT, '>/dev/null' or $logger->err("$msg $!");
131
    open STDERR, '>/dev/null' or $logger->err("$msg $!");
132
    defined (my $pid = fork) or $logger->err("Can't fork: $!");
133
    exit if $pid;
5398 dpurdie 134
 
1040 dpurdie 135
    setsid or $logger->err("Can't start a new session: $!");
136
    $logger->logmsg('Writing pid');
137
}
138
 
139
#
140
#   Locate and start up new transfer targets
141
#
142
Utils::writepid($conf);
7423 dpurdie 143
sighandlers();
1040 dpurdie 144
while ( 1 )
145
{
146
    readConfig();
147
 
148
    #
149
    #   Process all the configured transfer targets
150
    #
151
    for my $target ( glob($conf->{'config'} . '/*.conf') )
152
    {
153
        #
154
        #   Known entries are tested
155
        #   If the child is dead, then it will be restarted
156
        #
157
        if ( exists $children{$target} )
158
        {
159
            my $pid = $children{$target}{pid};
160
            next unless ( exists $dead{$pid} );
161
            delete $dead{$pid};
162
            delete $children{$target};
163
        }
164
 
165
        #
166
        #   Fork then exec
167
        #
168
        $logger->logmsg("Starting $target");
5398 dpurdie 169
        defined (my $pid = fork) or $logger->err("Can't fork: $!");
1040 dpurdie 170
        if ( $pid )
171
        {
172
            #
173
            #   Parent process. Save PID for dead child testing
174
            #
175
            $children{$target}{pid} = $pid;
5398 dpurdie 176
            next;
1040 dpurdie 177
        }
178
 
179
        #
180
        #   Child process at this point
181
        #
182
#        setsid or $logger->err("Can't start a new session: $!");
183
        $logger->logmsg("Started $target");
7387 dpurdie 184
 
7396 dpurdie 185
        #
186
        #   Determine the daemon to run
7423 dpurdie 187
        #   Based on the name of the config file
188
        #       Use the basename of the config file
189
        #       Uppercase the first letter
190
        #       If there is a perl file of this name in the rootDir then use it
191
        #       Otherwise use the default of blatDaemon
192
        #   
193
        $target =~ m~.*/(.*)\.conf$~;
194
        my $daemon = $rootDir . '/blat' . ucfirst($1) . '.pl';
195
        unless (-f $daemon) {
196
            $daemon = "$rootDir/blatDaemon.pl";
7396 dpurdie 197
        }
7423 dpurdie 198
        $logger->verbose("Start: $daemon $target");
7387 dpurdie 199
        exec 'perl',$daemon, $target or $logger->err ("Can't exec $daemon");
1040 dpurdie 200
    }
201
 
202
    #
203
    #   What a bit and try again
204
    #
205
    sleep $conf->{'configpoll'};
206
 
207
    #
208
    #   Reap child processes
209
    #   Remember the dead ones - they will be restarted
210
    #
211
    #...
212
    my $kid;
213
    do {
214
        if ( ($kid = waitpid(-1, WNOHANG)) > 0 )
215
        {
216
            $dead{$kid} = 1;
217
            $logger->logmsg("Child Process terminated: $kid");
218
        }
219
    } while ( $kid > 0 );
220
}
221
 
222
#-------------------------------------------------------------------------------
223
# Function        : readConfig
224
#
225
# Description     : Re read the config file if it modification time has changed
3847 dpurdie 226
#                   Not much happens if the config is re-read
1040 dpurdie 227
#                   Only a few of the attributes are used
228
#
229
# Inputs          : Nothing
230
#
231
# Returns         : Nothing
232
#
233
sub readConfig
234
{
235
    my ($mtime) = Utils::mtime($configPath);
236
    if ( $mtimeConfig != $mtime )
237
    {
238
        $logger->logmsg("Reading config file: $configPath");
239
        $mtimeConfig = $mtime;
240
        my $errors;
241
        ($conf, $errors) = Utils::readconf ( $configPath, \%cdata );
242
        if ( scalar @{$errors} > 0 )
243
        {
244
            warn "$_\n" foreach (@{$errors});
245
            die ("Config contained errors\n");
246
        }
247
 
248
        #
249
        #   Reset some information
250
        #   Create a new logger
251
        #
252
        $logger = Logger->new($conf);
253
        $conf->{logger} = $logger;
254
        $logger->verbose("Log Levl: $conf->{verbose}");
255
 
256
        #
257
        #   Some command line options will override config
258
        #
259
        $conf->{pidfile} = $opt_pid if ( defined $opt_pid );
260
        $conf->{verbose} = $opt_verbose if ( $opt_verbose > 0 );
261
 
262
        #
263
        #   Scan the configuration and export specified environment values
264
        #   Simplfies the process of passing data to children
265
        #
266
        while (my($key, $data) = each ( %{$conf->{envVars}} ))
267
        {
268
            $ENV{$key} = $data;
269
            $logger->verbose("Env: $key -> $data");
270
 
271
        }
272
 
273
        #   Extend the path
274
        my @extraPath;
275
        while (my($key, $data) = each ( %{$conf->{envPath}} ))
276
        {
277
            push @extraPath, $data;
278
            $logger->verbose("Path: $key -> $data");
279
 
280
        }
281
        if ( @extraPath )
282
        {
283
            $ENV{PATH} = join( ':', $ENV{PATH} , @extraPath );
284
        }
285
        $logger->verbose("PATH: $ENV{PATH}");
286
    }
287
}
288
 
289
 
290
 
291
#-------------------------------------------------------------------------------
292
# Function        : sighandlers
293
#
294
# Description     : Install signal handlers
295
#
7423 dpurdie 296
# Inputs          : None
1040 dpurdie 297
#
298
# Returns         : Nothing
299
#
300
sub sighandlers
301
{
5398 dpurdie 302
    $SIG{TERM} = sub {
303
        # On shutdown
304
        $logger->logmsg('Received SIGTERM. Shutting down....' );
1040 dpurdie 305
        foreach my $entry ( keys %children )
306
        {
307
            my $pid = $children{$entry}{pid};
5398 dpurdie 308
            $logger->logmsg('Terminate child: ' . $pid);
1040 dpurdie 309
            kill 15, $pid;
310
        }
311
 
5398 dpurdie 312
        unlink $conf->{'pidfile'} if -f $conf->{'pidfile'};
313
        exit 0;
314
    };
1040 dpurdie 315
 
5398 dpurdie 316
    $SIG{HUP} = sub {
317
        # On logrotate
318
        $logger->logmsg('Received SIGHUP.');
319
        $logger->rotatelog();
1040 dpurdie 320
        foreach my $entry ( keys %children )
321
        {
322
            my $pid = $children{$entry}{pid};
5398 dpurdie 323
            $logger->logmsg('Signal HUP to child: ' . $pid);
1040 dpurdie 324
            kill 1, $pid;
325
        }
5398 dpurdie 326
    };
3847 dpurdie 327
 
5398 dpurdie 328
    $SIG{USR1} = sub {
329
        # On Force Archive Sync
330
        $logger->logmsg('Received SIGUSR1.');
3847 dpurdie 331
        foreach my $entry ( keys %children )
332
        {
333
            my $pid = $children{$entry}{pid};
5398 dpurdie 334
            $logger->logmsg('Signal USR1 to child: ' . $pid);
3847 dpurdie 335
            kill 'USR1', $pid;
336
        }
5398 dpurdie 337
    };
1040 dpurdie 338
}
339