Subversion Repositories DevTools

Rev

Rev 1040 | Go to most recent revision | Details | Last modification | View Log | RSS feed

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