2005-12-16 18:05:29 +08:00
|
|
|
#!/usr/bin/perl -wT
|
|
|
|
#
|
|
|
|
# ==========================================================================
|
|
|
|
#
|
|
|
|
# ZoneMinder Daemon Control Script, $Date$, $Revision$
|
2008-07-25 17:48:16 +08:00
|
|
|
# Copyright (C) 2001-2008 Philip Coombes
|
2005-12-16 18:05:29 +08:00
|
|
|
#
|
|
|
|
# This program is free software; you can redistribute it and/or
|
|
|
|
# modify it under the terms of the GNU General Public License
|
|
|
|
# as published by the Free Software Foundation; either version 2
|
|
|
|
# of the License, or (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
# along with this program; if not, write to the Free Software
|
2016-12-26 23:23:16 +08:00
|
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
2005-12-16 18:05:29 +08:00
|
|
|
#
|
|
|
|
# ==========================================================================
|
2015-04-09 01:41:20 +08:00
|
|
|
|
|
|
|
=head1 NAME
|
|
|
|
|
|
|
|
zmdc.pl - ZoneMinder Daemon Control script
|
|
|
|
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
|
2017-04-28 22:32:05 +08:00
|
|
|
zmdc.pl {command} [daemon [options]]
|
2015-04-09 01:41:20 +08:00
|
|
|
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
|
|
|
|
This script is the gateway for controlling the various ZoneMinder
|
|
|
|
daemons. All starting, stopping and restarting goes through here.
|
|
|
|
On the first invocation it starts up a server which subsequently
|
|
|
|
records what's running and what's not. Other invocations just
|
|
|
|
connect to the server and pass instructions to it.
|
|
|
|
|
|
|
|
=head1 OPTIONS
|
|
|
|
|
2017-04-28 22:32:05 +08:00
|
|
|
{command} - One of 'startup|shutdown|status|check|logrot' or
|
|
|
|
'start|stop|restart|reload|version'.
|
|
|
|
[daemon [options]] - Daemon name and options, required for second group of commands
|
2015-04-09 01:41:20 +08:00
|
|
|
|
|
|
|
=cut
|
2005-12-16 18:05:29 +08:00
|
|
|
use strict;
|
2017-04-28 22:32:05 +08:00
|
|
|
use warnings;
|
2005-12-16 18:05:29 +08:00
|
|
|
use bytes;
|
|
|
|
|
|
|
|
# ==========================================================================
|
|
|
|
#
|
|
|
|
# User config
|
|
|
|
#
|
|
|
|
# ==========================================================================
|
|
|
|
|
2017-04-28 22:32:05 +08:00
|
|
|
# in useconds, not seconds.
|
2017-10-25 03:31:23 +08:00
|
|
|
use constant MAX_CONNECT_DELAY => 40;
|
2005-12-16 21:16:37 +08:00
|
|
|
|
2005-12-16 18:05:29 +08:00
|
|
|
# ==========================================================================
|
|
|
|
#
|
|
|
|
# Don't change anything from here on down
|
|
|
|
#
|
|
|
|
# ==========================================================================
|
|
|
|
|
2009-06-08 17:11:56 +08:00
|
|
|
@EXTRA_PERL_LIB@
|
2005-12-16 18:05:29 +08:00
|
|
|
use ZoneMinder;
|
|
|
|
use POSIX;
|
|
|
|
use Socket;
|
|
|
|
use IO::Handle;
|
2017-04-28 22:32:05 +08:00
|
|
|
use Time::HiRes qw(usleep);
|
2017-10-25 03:31:23 +08:00
|
|
|
|
2015-04-09 01:41:20 +08:00
|
|
|
use autouse 'Pod::Usage'=>qw(pod2usage);
|
2014-11-26 03:44:08 +08:00
|
|
|
#use Data::Dumper;
|
2005-12-16 18:05:29 +08:00
|
|
|
|
2016-08-17 21:36:00 +08:00
|
|
|
use constant SOCK_FILE => $Config{ZM_PATH_SOCKS}.'/zmdc'.($Config{ZM_SERVER_ID}?$Config{ZM_SERVER_ID}:'').'.sock';
|
2005-12-16 18:36:22 +08:00
|
|
|
|
2005-12-16 18:05:29 +08:00
|
|
|
$| = 1;
|
|
|
|
|
2016-03-12 05:28:16 +08:00
|
|
|
$ENV{PATH} = '/bin:/usr/bin:/usr/local/bin';
|
2005-12-16 18:05:29 +08:00
|
|
|
$ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL};
|
2016-05-10 00:46:26 +08:00
|
|
|
if ( $Config{ZM_LD_PRELOAD} ) {
|
|
|
|
Debug("Adding ENV{LD_PRELOAD} = $Config{ZM_LD_PRELOAD}");
|
|
|
|
$ENV{LD_PRELOAD} = $Config{ZM_LD_PRELOAD};
|
2016-09-28 00:51:04 +08:00
|
|
|
foreach my $lib ( split(/\s+/, $ENV{LD_PRELOAD} ) ) {
|
|
|
|
if ( ! -e $lib ) {
|
|
|
|
Warning("LD_PRELOAD lib $lib does not exist from LD_PRELOAD $ENV{LD_PRELOAD}.");
|
|
|
|
}
|
|
|
|
}
|
2016-05-10 00:46:26 +08:00
|
|
|
}
|
2005-12-16 18:05:29 +08:00
|
|
|
delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
|
|
|
|
|
2006-01-12 07:55:22 +08:00
|
|
|
my @daemons = (
|
2011-06-21 17:19:10 +08:00
|
|
|
'zmc',
|
|
|
|
'zma',
|
|
|
|
'zmfilter.pl',
|
|
|
|
'zmaudit.pl',
|
|
|
|
'zmtrigger.pl',
|
|
|
|
'zmx10.pl',
|
|
|
|
'zmwatch.pl',
|
|
|
|
'zmupdate.pl',
|
2018-01-11 01:57:37 +08:00
|
|
|
'zmstats.pl',
|
2016-02-07 04:08:28 +08:00
|
|
|
'zmtrack.pl',
|
2016-05-19 05:58:04 +08:00
|
|
|
'zmtelemetry.pl'
|
2017-04-28 22:32:05 +08:00
|
|
|
);
|
2005-12-16 18:05:29 +08:00
|
|
|
|
2018-04-25 22:48:50 +08:00
|
|
|
if ( $Config{ZM_OPT_USE_EVENTNOTIFICATION} ) {
|
|
|
|
push @daemons,'zmeventnotification.pl';
|
2018-03-29 20:47:55 +08:00
|
|
|
}
|
|
|
|
|
2005-12-16 18:05:29 +08:00
|
|
|
my $command = shift @ARGV;
|
2018-04-25 22:48:50 +08:00
|
|
|
if ( !$command ) {
|
|
|
|
print(STDERR "No command given\n");
|
2017-04-28 22:32:05 +08:00
|
|
|
pod2usage(-exitstatus => -1);
|
2006-10-20 17:31:40 +08:00
|
|
|
}
|
2015-01-07 21:47:32 +08:00
|
|
|
if ( $command eq 'version' ) {
|
2017-04-28 22:32:05 +08:00
|
|
|
print ZoneMinder::Base::ZM_VERSION."\n";
|
2018-04-25 22:48:50 +08:00
|
|
|
exit(0);
|
2015-01-07 21:47:32 +08:00
|
|
|
}
|
|
|
|
my $needs_daemon = $command !~ /(?:startup|shutdown|status|check|logrot|version)/;
|
2018-04-25 22:48:50 +08:00
|
|
|
my $daemon = shift @ARGV;
|
2018-05-31 22:25:16 +08:00
|
|
|
if ( $needs_daemon && !$daemon ) {
|
2018-04-25 22:48:50 +08:00
|
|
|
print(STDERR "No daemon given\n");
|
2017-04-28 22:32:05 +08:00
|
|
|
pod2usage(-exitstatus => -1);
|
2006-10-20 17:31:40 +08:00
|
|
|
}
|
2005-12-16 18:05:29 +08:00
|
|
|
my @args;
|
|
|
|
|
2018-05-31 22:25:16 +08:00
|
|
|
my $daemon_patt = '('.join('|', @daemons).')';
|
2016-08-23 00:08:49 +08:00
|
|
|
if ( $needs_daemon ) {
|
2017-04-28 22:32:05 +08:00
|
|
|
if ( $daemon =~ /^${daemon_patt}$/ ) {
|
|
|
|
$daemon = $1;
|
|
|
|
} else {
|
2018-04-25 22:48:50 +08:00
|
|
|
print(STDERR "Invalid daemon '$daemon' specified");
|
2017-04-28 22:32:05 +08:00
|
|
|
pod2usage(-exitstatus => -1);
|
|
|
|
}
|
2005-12-16 18:05:29 +08:00
|
|
|
}
|
|
|
|
|
2016-08-23 00:08:49 +08:00
|
|
|
foreach my $arg ( @ARGV ) {
|
2017-04-28 22:32:05 +08:00
|
|
|
# Detaint arguments, if they look ok
|
|
|
|
#if ( $arg =~ /^(-{0,2}[\w]+)/ )
|
|
|
|
if ( $arg =~ /^(-{0,2}[\w\/?&=.-]+)$/ ) {
|
2018-05-31 22:25:16 +08:00
|
|
|
push @args, $1;
|
2017-04-28 22:32:05 +08:00
|
|
|
} else {
|
2018-04-25 22:48:50 +08:00
|
|
|
print(STDERR "Bogus argument '$arg' found");
|
|
|
|
exit(-1);
|
2017-04-28 22:32:05 +08:00
|
|
|
}
|
2005-12-16 18:05:29 +08:00
|
|
|
}
|
|
|
|
|
2017-10-25 03:31:23 +08:00
|
|
|
my $dbh = zmDbConnect();
|
|
|
|
|
2018-04-25 22:48:50 +08:00
|
|
|
socket(CLIENT, PF_UNIX, SOCK_STREAM, 0) or Fatal("Can't open socket: $!");
|
2005-12-16 18:05:29 +08:00
|
|
|
|
2018-04-25 22:48:50 +08:00
|
|
|
my $saddr = sockaddr_un(SOCK_FILE);
|
|
|
|
my $server_up = connect(CLIENT, $saddr);
|
2018-01-11 01:57:37 +08:00
|
|
|
|
2018-04-25 22:48:50 +08:00
|
|
|
if ( !$server_up ) {
|
2017-10-25 03:31:23 +08:00
|
|
|
if ( $Config{ZM_SERVER_ID} ) {
|
2018-04-19 09:29:35 +08:00
|
|
|
use Sys::MemInfo qw(totalmem freemem totalswap freeswap);
|
|
|
|
use ZoneMinder::Server qw(CpuLoad);
|
2017-10-25 03:31:23 +08:00
|
|
|
if ( ! defined $dbh->do(q{UPDATE Servers SET Status=?,TotalMem=?,FreeMem=?,TotalSwap=?,FreeSwap=? WHERE Id=?}, undef,
|
|
|
|
'NotRunning', &totalmem, &freemem, &totalswap, &freeswap, $Config{ZM_SERVER_ID} ) ) {
|
|
|
|
Error("Failed Updating status of Server record to Not RUnning for Id=$Config{ZM_SERVER_ID}" . $dbh->errstr());
|
2011-06-21 17:19:10 +08:00
|
|
|
}
|
2017-10-25 03:31:23 +08:00
|
|
|
}
|
|
|
|
# Server is not up. Some commands can still be handled
|
2017-04-28 22:32:05 +08:00
|
|
|
if ( $command eq 'logrot' ) {
|
|
|
|
# If server is not running, then logrotate doesn't need to do anything.
|
2011-06-21 17:19:10 +08:00
|
|
|
exit();
|
2017-04-28 22:32:05 +08:00
|
|
|
}
|
|
|
|
if ( $command eq 'check' ) {
|
2018-04-25 22:48:50 +08:00
|
|
|
print("stopped\n");
|
2017-04-28 22:32:05 +08:00
|
|
|
exit();
|
|
|
|
} elsif ( $command ne 'startup' ) {
|
2018-04-25 22:48:50 +08:00
|
|
|
print("Unable to connect to server using socket at " . SOCK_FILE . "\n");
|
2017-04-28 22:32:05 +08:00
|
|
|
exit( -1 );
|
|
|
|
}
|
|
|
|
|
|
|
|
# The server isn't there
|
2018-04-25 22:48:50 +08:00
|
|
|
print("Starting server\n");
|
|
|
|
close(CLIENT);
|
2017-04-28 22:32:05 +08:00
|
|
|
|
|
|
|
if ( my $cpid = fork() ) {
|
|
|
|
# Parent process just sleep and fall through
|
|
|
|
|
|
|
|
# I'm still not sure why we need to re-init the logs
|
|
|
|
logInit();
|
|
|
|
|
2018-04-25 22:48:50 +08:00
|
|
|
socket(CLIENT, PF_UNIX, SOCK_STREAM, 0) or Fatal("Can't open socket: $!");
|
2017-04-28 22:32:05 +08:00
|
|
|
my $attempts = 0;
|
2018-01-30 23:45:32 +08:00
|
|
|
while( !connect(CLIENT, $saddr) ) {
|
2017-04-28 22:32:05 +08:00
|
|
|
$attempts++;
|
2018-01-30 23:45:32 +08:00
|
|
|
Debug("Waiting for zmdc.pl server process at ".SOCK_FILE.", attempt $attempts");
|
|
|
|
Fatal("Can't connect: $!") if $attempts > MAX_CONNECT_DELAY;
|
2017-07-04 22:11:57 +08:00
|
|
|
usleep(200000);
|
2017-04-28 22:32:05 +08:00
|
|
|
} # end while
|
|
|
|
} elsif ( defined($cpid) ) {
|
|
|
|
ZMServer::run();
|
|
|
|
} else {
|
2018-01-30 23:45:32 +08:00
|
|
|
Fatal("Can't fork: $!");
|
2017-04-28 22:32:05 +08:00
|
|
|
}
|
|
|
|
} # end if ! server is up
|
|
|
|
|
2018-01-30 23:45:32 +08:00
|
|
|
if ( ($command eq 'check') && !$daemon ) {
|
|
|
|
print("running\n");
|
2017-04-28 22:32:05 +08:00
|
|
|
exit();
|
2016-08-23 01:34:02 +08:00
|
|
|
} elsif ( $command eq 'startup' ) {
|
2017-04-28 22:32:05 +08:00
|
|
|
# Our work here is done
|
2018-01-30 23:45:32 +08:00
|
|
|
exit() if !$server_up;
|
2005-12-16 18:05:29 +08:00
|
|
|
}
|
2017-04-28 22:32:05 +08:00
|
|
|
|
2005-12-16 18:05:29 +08:00
|
|
|
# The server is there, connect to it
|
|
|
|
CLIENT->autoflush();
|
2017-04-28 22:32:05 +08:00
|
|
|
my $message = join(';', $command, ( $daemon ? $daemon : () ), @args );
|
2018-01-30 23:45:32 +08:00
|
|
|
print(CLIENT $message);
|
|
|
|
shutdown(CLIENT, 1);
|
2017-04-28 22:32:05 +08:00
|
|
|
while( my $line = <CLIENT> ) {
|
2018-01-30 23:45:32 +08:00
|
|
|
chomp($line);
|
|
|
|
print("$line\n");
|
2005-12-16 18:05:29 +08:00
|
|
|
}
|
2018-01-30 23:45:32 +08:00
|
|
|
close(CLIENT);
|
2005-12-23 18:22:32 +08:00
|
|
|
|
|
|
|
exit;
|
|
|
|
|
2011-04-28 22:22:44 +08:00
|
|
|
package ZMServer;
|
|
|
|
|
|
|
|
use strict;
|
2017-04-28 22:32:05 +08:00
|
|
|
use warnings;
|
2011-04-28 22:22:44 +08:00
|
|
|
use bytes;
|
|
|
|
|
2015-06-23 01:56:27 +08:00
|
|
|
@EXTRA_PERL_LIB@
|
2011-04-28 22:22:44 +08:00
|
|
|
use ZoneMinder;
|
|
|
|
use POSIX;
|
|
|
|
use Socket;
|
|
|
|
use IO::Handle;
|
2017-04-06 04:08:47 +08:00
|
|
|
use Time::HiRes qw(usleep);
|
2017-10-25 03:31:23 +08:00
|
|
|
use Sys::MemInfo qw(totalmem freemem totalswap freeswap);
|
2018-04-19 09:46:55 +08:00
|
|
|
use ZoneMinder::Server qw(CpuLoad);
|
2014-11-26 03:44:08 +08:00
|
|
|
#use Data::Dumper;
|
2011-04-28 22:22:44 +08:00
|
|
|
|
2018-04-24 23:41:04 +08:00
|
|
|
use constant KILL_DELAY => 60; # seconds to wait between sending TERM and sending KILL
|
2017-04-28 23:43:36 +08:00
|
|
|
|
2011-04-28 22:22:44 +08:00
|
|
|
our %cmd_hash;
|
|
|
|
our %pid_hash;
|
2018-04-24 23:41:04 +08:00
|
|
|
our %terminating_processes;
|
2018-04-30 22:09:23 +08:00
|
|
|
our $zm_terminate = 0;
|
2011-04-28 22:22:44 +08:00
|
|
|
|
2016-08-23 00:08:49 +08:00
|
|
|
sub run {
|
2017-04-28 22:32:05 +08:00
|
|
|
my $fd = 0;
|
2018-04-25 22:48:50 +08:00
|
|
|
while( $fd < POSIX::sysconf(&POSIX::_SC_OPEN_MAX) ) {
|
|
|
|
POSIX::close($fd++);
|
2017-04-28 22:32:05 +08:00
|
|
|
}
|
2011-04-28 22:22:44 +08:00
|
|
|
|
2017-04-28 22:32:05 +08:00
|
|
|
setpgrp();
|
2011-04-28 22:22:44 +08:00
|
|
|
|
2017-04-28 22:32:05 +08:00
|
|
|
logInit();
|
2011-04-28 22:22:44 +08:00
|
|
|
|
2018-04-25 22:48:50 +08:00
|
|
|
dPrint(ZoneMinder::Logger::INFO, 'Server starting at '
|
|
|
|
.strftime('%y/%m/%d %H:%M:%S', localtime())
|
2017-04-28 22:32:05 +08:00
|
|
|
."\n"
|
|
|
|
);
|
2011-04-28 22:22:44 +08:00
|
|
|
|
2018-04-25 22:48:50 +08:00
|
|
|
if ( open(my $PID, '>', ZM_PID) ) {
|
|
|
|
print($PID $$);
|
|
|
|
close($PID);
|
2017-04-28 22:32:05 +08:00
|
|
|
} else {
|
2018-04-25 22:48:50 +08:00
|
|
|
Error("Can't open pid file at " . ZM_PID);
|
2017-04-28 22:32:05 +08:00
|
|
|
}
|
2011-04-28 22:22:44 +08:00
|
|
|
|
2018-04-24 23:41:04 +08:00
|
|
|
# Tell any existing processes to die, wait 1 second between TERM and KILL
|
2018-04-25 22:48:50 +08:00
|
|
|
killAll(1);
|
2017-04-28 22:32:05 +08:00
|
|
|
|
2018-04-25 22:48:50 +08:00
|
|
|
dPrint(ZoneMinder::Logger::INFO, 'Socket should be open at ' .main::SOCK_FILE);
|
2017-10-25 03:31:23 +08:00
|
|
|
my $dbh = zmDbConnect(1);
|
2018-04-25 22:48:50 +08:00
|
|
|
socket(SERVER, PF_UNIX, SOCK_STREAM, 0) or Fatal("Can't open socket: $!");
|
|
|
|
unlink(main::SOCK_FILE) or Error('Unable to unlink ' . main::SOCK_FILE .". Error message was: $!") if -e main::SOCK_FILE;
|
|
|
|
bind(SERVER, $saddr) or Fatal("Can't bind to " . main::SOCK_FILE . ": $!");
|
|
|
|
listen(SERVER, SOMAXCONN) or Fatal("Can't listen: $!");
|
2017-04-28 22:32:05 +08:00
|
|
|
|
|
|
|
$SIG{CHLD} = \&reaper;
|
2018-04-30 22:09:23 +08:00
|
|
|
$SIG{INT} = \&shutdown_sig_handler;
|
|
|
|
$SIG{TERM} = \&shutdown_sig_handler;
|
|
|
|
$SIG{ABRT} = \&shutdown_sig_handler;
|
2017-04-28 22:32:05 +08:00
|
|
|
$SIG{HUP} = \&logrot;
|
|
|
|
|
|
|
|
my $rin = '';
|
2018-04-25 22:48:50 +08:00
|
|
|
vec($rin, fileno(SERVER), 1) = 1;
|
2017-04-28 22:32:05 +08:00
|
|
|
my $win = $rin;
|
|
|
|
my $ein = $win;
|
2017-10-25 03:31:23 +08:00
|
|
|
my $timeout = 1;
|
2018-01-11 01:57:37 +08:00
|
|
|
my $Server = undef;
|
2018-01-11 02:20:09 +08:00
|
|
|
my $secs_count = 0;
|
2018-01-11 01:57:37 +08:00
|
|
|
|
|
|
|
if ( $Config{ZM_SERVER_ID} ) {
|
|
|
|
require ZoneMinder::Server;
|
2018-04-25 22:48:50 +08:00
|
|
|
$Server = new ZoneMinder::Server($Config{ZM_SERVER_ID});
|
|
|
|
dPrint(ZoneMinder::Logger::INFO, 'Loading Server record have ' . $$Server{Name});
|
2018-01-11 01:57:37 +08:00
|
|
|
}
|
|
|
|
|
2018-04-30 22:09:23 +08:00
|
|
|
while( !$zm_terminate ) {
|
2018-01-11 02:20:09 +08:00
|
|
|
|
2017-10-25 03:31:23 +08:00
|
|
|
if ( $Config{ZM_SERVER_ID} ) {
|
2018-01-16 08:23:59 +08:00
|
|
|
if ( ! ( $secs_count % 60 ) ) {
|
2018-04-30 22:09:23 +08:00
|
|
|
Debug("Connecting");
|
2018-05-01 03:09:13 +08:00
|
|
|
while ( (!$zm_terminate) and !($dbh and $dbh->ping()) ) {
|
2018-05-05 04:00:55 +08:00
|
|
|
Warning("Not connected to db ($dbh)".($dbh?" ping(".$dbh->ping().")":''). ($DBI::errstr?" errstr($DBI::errstr)":'').' Reconnecting');
|
2018-04-30 22:09:23 +08:00
|
|
|
$dbh = zmDbConnect();
|
|
|
|
}
|
2018-04-18 09:09:41 +08:00
|
|
|
my @cpuload = CpuLoad();
|
2018-05-31 22:25:16 +08:00
|
|
|
Debug("Updating Server record @cpuload");
|
2018-01-11 02:20:09 +08:00
|
|
|
if ( ! defined $dbh->do(q{UPDATE Servers SET Status=?,CpuLoad=?,TotalMem=?,FreeMem=?,TotalSwap=?,FreeSwap=? WHERE Id=?}, undef,
|
2017-10-25 06:13:48 +08:00
|
|
|
'Running', $cpuload[0], &totalmem, &freemem, &totalswap, &freeswap, $Config{ZM_SERVER_ID} ) ) {
|
2018-01-11 02:20:09 +08:00
|
|
|
Error("Failed Updating status of Server record for Id=$Config{ZM_SERVER_ID}".$dbh->errstr());
|
2011-04-28 22:22:44 +08:00
|
|
|
}
|
2017-10-25 03:31:23 +08:00
|
|
|
}
|
2018-01-11 02:20:09 +08:00
|
|
|
$secs_count += 1;
|
2017-10-25 03:31:23 +08:00
|
|
|
}
|
2018-04-25 22:48:50 +08:00
|
|
|
my $nfound = select(my $rout = $rin, undef, undef, $timeout);
|
2017-04-28 22:32:05 +08:00
|
|
|
if ( $nfound > 0 ) {
|
2018-04-25 22:48:50 +08:00
|
|
|
if ( vec($rout, fileno(SERVER), 1) ) {
|
|
|
|
my $paddr = accept(CLIENT, SERVER);
|
2017-04-28 22:32:05 +08:00
|
|
|
my $message = <CLIENT>;
|
|
|
|
|
2018-01-11 01:57:37 +08:00
|
|
|
next if !$message;
|
2017-04-28 22:32:05 +08:00
|
|
|
|
2018-04-25 22:48:50 +08:00
|
|
|
my ( $command, $daemon, @args ) = split(';', $message);
|
2017-04-28 22:32:05 +08:00
|
|
|
|
|
|
|
if ( $command eq 'start' ) {
|
2018-04-25 22:48:50 +08:00
|
|
|
start($daemon, @args);
|
2017-04-28 22:32:05 +08:00
|
|
|
} elsif ( $command eq 'stop' ) {
|
2018-04-25 22:48:50 +08:00
|
|
|
stop($daemon, @args);
|
2017-04-28 22:32:05 +08:00
|
|
|
} elsif ( $command eq 'restart' ) {
|
2018-04-25 22:48:50 +08:00
|
|
|
restart($daemon, @args);
|
2017-04-28 22:32:05 +08:00
|
|
|
} elsif ( $command eq 'reload' ) {
|
2018-04-25 22:48:50 +08:00
|
|
|
reload($daemon, @args);
|
2017-04-28 22:32:05 +08:00
|
|
|
} elsif ( $command eq 'startup' ) {
|
|
|
|
# Do nothing, this is all we're here for
|
2018-04-25 22:48:50 +08:00
|
|
|
dPrint(ZoneMinder::Logger::WARNING, "Already running, ignoring command '$command'\n");
|
2017-04-28 22:32:05 +08:00
|
|
|
} elsif ( $command eq 'shutdown' ) {
|
2018-04-30 22:09:23 +08:00
|
|
|
# Breka out of while loop
|
|
|
|
last;
|
2017-04-28 22:32:05 +08:00
|
|
|
} elsif ( $command eq 'check' ) {
|
2018-04-25 22:48:50 +08:00
|
|
|
check($daemon, @args);
|
2017-04-28 22:32:05 +08:00
|
|
|
} elsif ( $command eq 'status' ) {
|
|
|
|
if ( $daemon ) {
|
2018-04-25 22:48:50 +08:00
|
|
|
status($daemon, @args);
|
2017-04-28 22:32:05 +08:00
|
|
|
} else {
|
|
|
|
status();
|
|
|
|
}
|
|
|
|
} elsif ( $command eq 'logrot' ) {
|
|
|
|
logrot();
|
2016-08-23 00:08:49 +08:00
|
|
|
} else {
|
2018-04-25 22:48:50 +08:00
|
|
|
dPrint(ZoneMinder::Logger::ERROR, "Invalid command '$command'\n");
|
2011-04-28 22:22:44 +08:00
|
|
|
}
|
2018-01-30 23:45:32 +08:00
|
|
|
close(CLIENT);
|
2017-04-28 22:32:05 +08:00
|
|
|
} else {
|
2018-04-26 04:04:46 +08:00
|
|
|
Error('Bogus descriptor');
|
2017-04-28 22:32:05 +08:00
|
|
|
}
|
|
|
|
} elsif ( $nfound < 0 ) {
|
|
|
|
if ( $! == EINTR ) {
|
|
|
|
# Dead child, will be reaped
|
|
|
|
#print( "Probable dead child\n" );
|
|
|
|
# See if it needs to start up again
|
|
|
|
} elsif ( $! == EPIPE ) {
|
2018-01-30 23:45:32 +08:00
|
|
|
Error("Can't select: $!");
|
2017-04-28 22:32:05 +08:00
|
|
|
} else {
|
2018-01-30 23:45:32 +08:00
|
|
|
Fatal("Can't select: $!");
|
2017-04-28 22:32:05 +08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
#print( "Select timed out\n" );
|
2011-04-28 22:22:44 +08:00
|
|
|
}
|
2018-04-24 23:41:04 +08:00
|
|
|
|
2018-04-30 22:09:23 +08:00
|
|
|
Debug("restartPending");
|
2018-04-26 04:04:46 +08:00
|
|
|
restartPending();
|
2018-04-30 22:09:23 +08:00
|
|
|
Debug("check_for_processes_to_kill");
|
2018-04-24 23:41:04 +08:00
|
|
|
check_for_processes_to_kill();
|
|
|
|
|
|
|
|
} # end while
|
2018-04-26 04:04:46 +08:00
|
|
|
|
2018-04-25 22:48:50 +08:00
|
|
|
dPrint(ZoneMinder::Logger::INFO, 'Server exiting at '
|
2017-04-28 22:32:05 +08:00
|
|
|
.strftime( '%y/%m/%d %H:%M:%S', localtime() )
|
|
|
|
."\n"
|
|
|
|
);
|
2017-10-25 03:31:23 +08:00
|
|
|
if ( $Config{ZM_SERVER_ID} ) {
|
2018-05-31 22:25:16 +08:00
|
|
|
$dbh = zmDbConnect() if ! ($dbh and $dbh->ping());
|
2018-04-25 22:48:50 +08:00
|
|
|
if ( ! defined $dbh->do(q{UPDATE Servers SET Status='NotRunning' WHERE Id=?}, undef, $Config{ZM_SERVER_ID}) ) {
|
2017-10-25 03:31:23 +08:00
|
|
|
Error("Failed Updating status of Server record for Id=$Config{ZM_SERVER_ID}".$dbh->errstr());
|
|
|
|
}
|
|
|
|
}
|
2018-04-30 22:09:23 +08:00
|
|
|
shutdownAll();
|
2011-04-28 22:22:44 +08:00
|
|
|
}
|
|
|
|
|
2016-08-23 00:08:49 +08:00
|
|
|
sub cPrint {
|
2017-04-28 22:32:05 +08:00
|
|
|
if ( fileno(CLIENT) ) {
|
|
|
|
print CLIENT @_
|
|
|
|
}
|
2011-04-28 22:22:44 +08:00
|
|
|
}
|
|
|
|
|
2018-02-13 19:21:39 +08:00
|
|
|
# I think the purpose of this is to echo the logs to the client process so it can then display them.
|
2016-08-23 00:08:49 +08:00
|
|
|
sub dPrint {
|
2017-04-28 22:32:05 +08:00
|
|
|
my $logLevel = shift;
|
2018-05-31 22:25:16 +08:00
|
|
|
# One thought here, if no client exists to read these... does it block?
|
2017-04-28 22:32:05 +08:00
|
|
|
if ( fileno(CLIENT) ) {
|
2018-05-31 22:25:16 +08:00
|
|
|
Debug("Have fileno for CLIENT, printing ");
|
2017-04-28 22:32:05 +08:00
|
|
|
print CLIENT @_
|
|
|
|
}
|
|
|
|
if ( $logLevel == ZoneMinder::Logger::DEBUG ) {
|
2018-04-25 22:48:50 +08:00
|
|
|
Debug(@_);
|
2017-04-28 22:32:05 +08:00
|
|
|
} elsif ( $logLevel == ZoneMinder::Logger::INFO ) {
|
2018-04-25 22:48:50 +08:00
|
|
|
Info(@_);
|
2017-04-28 22:32:05 +08:00
|
|
|
} elsif ( $logLevel == ZoneMinder::Logger::WARNING ) {
|
2018-04-25 22:48:50 +08:00
|
|
|
Warning(@_);
|
2017-04-28 22:32:05 +08:00
|
|
|
} elsif ( $logLevel == ZoneMinder::Logger::ERROR ) {
|
2018-04-25 22:48:50 +08:00
|
|
|
Error(@_);
|
2017-04-28 22:32:05 +08:00
|
|
|
} elsif ( $logLevel == ZoneMinder::Logger::FATAL ) {
|
2018-04-25 22:48:50 +08:00
|
|
|
Fatal(@_);
|
2017-04-28 22:32:05 +08:00
|
|
|
}
|
2011-04-28 22:22:44 +08:00
|
|
|
}
|
|
|
|
|
2016-08-23 00:08:49 +08:00
|
|
|
sub start {
|
2017-04-28 22:32:05 +08:00
|
|
|
my $daemon = shift;
|
|
|
|
my @args = @_;
|
2011-04-28 22:22:44 +08:00
|
|
|
|
2018-04-25 22:48:50 +08:00
|
|
|
my $command = join(' ', $daemon, @args);
|
2017-04-28 22:32:05 +08:00
|
|
|
my $process = $cmd_hash{$command};
|
2011-04-28 22:22:44 +08:00
|
|
|
|
2017-04-28 22:32:05 +08:00
|
|
|
if ( !$process ) {
|
|
|
|
# It's not running, or at least it's not been started by us
|
|
|
|
$process = { daemon=>$daemon, args=>\@args, command=>$command, keepalive=>!undef };
|
|
|
|
} elsif ( $process->{pid} && $pid_hash{$process->{pid}} ) {
|
2018-04-25 22:48:50 +08:00
|
|
|
dPrint(ZoneMinder::Logger::INFO, "'$process->{command}' already running at "
|
|
|
|
.strftime('%y/%m/%d %H:%M:%S', localtime($process->{started}))
|
2017-04-28 22:32:05 +08:00
|
|
|
.", pid = $process->{pid}\n"
|
2015-04-09 01:29:38 +08:00
|
|
|
);
|
2018-04-25 22:48:50 +08:00
|
|
|
return;
|
2017-04-28 22:32:05 +08:00
|
|
|
}
|
2011-04-28 22:22:44 +08:00
|
|
|
|
2017-04-28 22:32:05 +08:00
|
|
|
my $sigset = POSIX::SigSet->new;
|
2018-04-30 22:09:23 +08:00
|
|
|
my $blockset = POSIX::SigSet->new(SIGCHLD);
|
|
|
|
Debug("Blocking SIGCHLD");
|
2018-04-25 22:48:50 +08:00
|
|
|
sigprocmask(SIG_BLOCK, $blockset, $sigset) or Fatal("Can't block SIGCHLD: $!");
|
2018-04-30 22:09:23 +08:00
|
|
|
Debug("forking");
|
2017-04-28 22:32:05 +08:00
|
|
|
if ( my $cpid = fork() ) {
|
2018-05-31 22:25:16 +08:00
|
|
|
Debug("before logReinit");
|
2018-05-09 00:52:40 +08:00
|
|
|
# This logReinit is required. Not sure why.
|
|
|
|
logReinit();
|
2018-05-31 22:25:16 +08:00
|
|
|
Debug("aftere logReinit");
|
2011-06-21 17:19:10 +08:00
|
|
|
|
2017-04-28 22:32:05 +08:00
|
|
|
$process->{pid} = $cpid;
|
|
|
|
$process->{started} = time();
|
2018-04-25 22:48:50 +08:00
|
|
|
delete $process->{pending};
|
2011-04-28 22:22:44 +08:00
|
|
|
|
2018-04-25 22:48:50 +08:00
|
|
|
dPrint(ZoneMinder::Logger::INFO, "'$command' starting at "
|
|
|
|
.strftime('%y/%m/%d %H:%M:%S', localtime($process->{started}))
|
2017-04-28 22:32:05 +08:00
|
|
|
.", pid = $process->{pid}\n"
|
2015-04-09 01:29:38 +08:00
|
|
|
);
|
2011-04-28 22:22:44 +08:00
|
|
|
|
2017-04-28 22:32:05 +08:00
|
|
|
$cmd_hash{$process->{command}} = $pid_hash{$cpid} = $process;
|
2018-04-25 22:48:50 +08:00
|
|
|
sigprocmask(SIG_SETMASK, $sigset) or Fatal("Can't restore SIGCHLD: $!");
|
2018-05-13 05:28:03 +08:00
|
|
|
Debug("unblocking child");
|
2018-04-25 22:48:50 +08:00
|
|
|
} elsif ( defined($cpid) ) {
|
2017-04-28 22:32:05 +08:00
|
|
|
# Force reconnection to the db.
|
2017-10-25 03:31:23 +08:00
|
|
|
$dbh = zmDbConnect(1);
|
2017-04-28 22:32:05 +08:00
|
|
|
logReinit();
|
2011-04-28 22:22:44 +08:00
|
|
|
|
2018-04-25 22:48:50 +08:00
|
|
|
dPrint(ZoneMinder::Logger::INFO, "'$command' started at "
|
|
|
|
.strftime('%y/%m/%d %H:%M:%S', localtime())
|
2017-04-28 22:32:05 +08:00
|
|
|
."\n"
|
2015-04-09 01:29:38 +08:00
|
|
|
);
|
2011-04-28 22:22:44 +08:00
|
|
|
|
2017-04-28 22:32:05 +08:00
|
|
|
if ( $daemon =~ /^${daemon_patt}$/ ) {
|
|
|
|
$daemon = $Config{ZM_PATH_BIN}.'/'.$1;
|
|
|
|
} else {
|
2018-04-25 22:48:50 +08:00
|
|
|
Fatal("Invalid daemon '$daemon' specified");
|
2017-04-28 22:32:05 +08:00
|
|
|
}
|
2011-04-28 22:22:44 +08:00
|
|
|
|
2017-04-28 22:32:05 +08:00
|
|
|
my @good_args;
|
|
|
|
foreach my $arg ( @args ) {
|
|
|
|
# Detaint arguments, if they look ok
|
|
|
|
if ( $arg =~ /^(-{0,2}[\w\/?&=.-]+)$/ ) {
|
2018-04-25 22:48:50 +08:00
|
|
|
push @good_args, $1;
|
2017-04-28 22:32:05 +08:00
|
|
|
} else {
|
2018-04-25 22:48:50 +08:00
|
|
|
Fatal("Bogus argument '$arg' found");
|
2017-04-28 22:32:05 +08:00
|
|
|
}
|
|
|
|
}
|
2011-04-28 22:22:44 +08:00
|
|
|
|
2017-04-28 22:32:05 +08:00
|
|
|
logTerm();
|
|
|
|
zmDbDisconnect();
|
2011-06-21 17:19:10 +08:00
|
|
|
|
2017-04-28 22:32:05 +08:00
|
|
|
my $fd = 0;
|
2018-04-25 22:48:50 +08:00
|
|
|
while( $fd < POSIX::sysconf(&POSIX::_SC_OPEN_MAX) ) {
|
|
|
|
POSIX::close($fd++);
|
2017-04-28 22:32:05 +08:00
|
|
|
}
|
2011-06-21 17:19:10 +08:00
|
|
|
|
2017-04-28 22:32:05 +08:00
|
|
|
# Child process
|
|
|
|
$SIG{CHLD} = 'DEFAULT';
|
|
|
|
$SIG{INT} = 'DEFAULT';
|
|
|
|
$SIG{TERM} = 'DEFAULT';
|
|
|
|
$SIG{ABRT} = 'DEFAULT';
|
2011-06-21 17:19:10 +08:00
|
|
|
|
2018-04-25 22:48:50 +08:00
|
|
|
exec($daemon, @good_args) or Fatal("Can't exec: $!");
|
2017-04-28 22:32:05 +08:00
|
|
|
} else {
|
2018-04-25 22:48:50 +08:00
|
|
|
Fatal("Can't fork: $!");
|
2017-04-28 22:32:05 +08:00
|
|
|
}
|
2018-04-25 22:48:50 +08:00
|
|
|
} # end sub start
|
2011-04-28 22:22:44 +08:00
|
|
|
|
2015-05-22 03:37:08 +08:00
|
|
|
# Sends the stop signal, without waiting around to see if the process died.
|
|
|
|
sub send_stop {
|
2017-04-28 22:32:05 +08:00
|
|
|
my ( $final, $process ) = @_;
|
2011-04-28 22:22:44 +08:00
|
|
|
|
2018-05-09 03:52:33 +08:00
|
|
|
my $sigset = POSIX::SigSet->new;
|
|
|
|
my $blockset = POSIX::SigSet->new(SIGCHLD);
|
|
|
|
sigprocmask(SIG_BLOCK, $blockset, $sigset) or die "dying at block...\n";
|
|
|
|
|
2017-04-28 22:32:05 +08:00
|
|
|
my $command = $process->{command};
|
|
|
|
if ( $process->{pending} ) {
|
2015-06-23 01:37:26 +08:00
|
|
|
|
2018-04-25 22:48:50 +08:00
|
|
|
delete $cmd_hash{$command};
|
|
|
|
dPrint(ZoneMinder::Logger::INFO, "Command '$command' removed from pending list at "
|
|
|
|
.strftime('%y/%m/%d %H:%M:%S', localtime())
|
2017-04-28 22:32:05 +08:00
|
|
|
."\n"
|
2015-04-09 01:29:38 +08:00
|
|
|
);
|
2018-05-09 03:52:33 +08:00
|
|
|
sigprocmask(SIG_UNBLOCK, $blockset) or die "dying at unblock...\n";
|
2017-04-28 22:32:05 +08:00
|
|
|
return();
|
|
|
|
}
|
2011-04-28 22:22:44 +08:00
|
|
|
|
2017-04-28 22:32:05 +08:00
|
|
|
my $pid = $process->{pid};
|
2018-05-09 03:52:33 +08:00
|
|
|
if ( !$pid ) {
|
|
|
|
dPrint(ZoneMinder::Logger::ERROR, "No process with command of '$command' is running\n");
|
|
|
|
sigprocmask(SIG_UNBLOCK, $blockset) or die "dying at unblock...\n";
|
|
|
|
return();
|
|
|
|
}
|
2017-04-28 22:32:05 +08:00
|
|
|
if ( !$pid_hash{$pid} ) {
|
2018-04-25 22:48:50 +08:00
|
|
|
dPrint(ZoneMinder::Logger::ERROR, "No process with command of '$command' pid $pid is running\n");
|
2018-05-09 03:52:33 +08:00
|
|
|
sigprocmask(SIG_UNBLOCK, $blockset) or die "dying at unblock...\n";
|
2017-04-28 22:32:05 +08:00
|
|
|
return();
|
|
|
|
}
|
2011-04-28 22:22:44 +08:00
|
|
|
|
2018-04-25 22:48:50 +08:00
|
|
|
dPrint(ZoneMinder::Logger::INFO, "'$command' sending stop to pid $pid at "
|
|
|
|
.strftime('%y/%m/%d %H:%M:%S', localtime())
|
2017-04-28 22:32:05 +08:00
|
|
|
."\n"
|
|
|
|
);
|
|
|
|
$process->{keepalive} = !$final;
|
2018-05-09 04:08:24 +08:00
|
|
|
$process->{term_sent_at} = time if ! $process->{term_sent_at};
|
2018-04-24 23:41:04 +08:00
|
|
|
$process->{pending} = 0;
|
|
|
|
$terminating_processes{$command} = $process;
|
|
|
|
|
2018-04-25 22:48:50 +08:00
|
|
|
kill('TERM', $pid);
|
2018-05-09 03:52:33 +08:00
|
|
|
sigprocmask(SIG_UNBLOCK, $blockset) or die "dying at unblock...\n";
|
2017-04-28 22:32:05 +08:00
|
|
|
return $pid;
|
2015-05-22 03:37:08 +08:00
|
|
|
} # end sub send_stop
|
2011-04-28 22:22:44 +08:00
|
|
|
|
2018-04-24 23:41:04 +08:00
|
|
|
sub check_for_processes_to_kill {
|
|
|
|
# Turn off SIGCHLD
|
2017-04-01 00:25:48 +08:00
|
|
|
my $sigset = POSIX::SigSet->new;
|
2017-04-07 09:21:28 +08:00
|
|
|
my $blockset = POSIX::SigSet->new(SIGCHLD);
|
2018-04-25 22:48:50 +08:00
|
|
|
sigprocmask(SIG_BLOCK, $blockset, $sigset) or die "dying at block...\n";
|
2018-04-30 22:09:23 +08:00
|
|
|
foreach my $command ( keys %terminating_processes ) {
|
2018-04-24 23:41:04 +08:00
|
|
|
my $process = $cmd_hash{$command};
|
2018-05-09 02:12:53 +08:00
|
|
|
if ( ! $process ) {
|
|
|
|
Debug("No process found for $command");
|
2018-05-09 03:29:34 +08:00
|
|
|
delete $terminating_processes{$command};
|
|
|
|
next;
|
|
|
|
}
|
|
|
|
if ( ! $$process{pid} ) {
|
|
|
|
Warning("Have no pid for $command.");
|
|
|
|
delete $terminating_processes{$command};
|
2018-05-09 02:12:53 +08:00
|
|
|
next;
|
|
|
|
}
|
2018-04-30 22:09:23 +08:00
|
|
|
my $now = time;
|
2018-05-04 00:50:54 +08:00
|
|
|
Debug("Have process $command at pid $$process{pid} $now - $$process{term_sent_at} = " . ( $now - $$process{term_sent_at} ));
|
2018-05-02 00:08:31 +08:00
|
|
|
if ( $$process{term_sent_at} and ( $now - $$process{term_sent_at} > KILL_DELAY ) ) {
|
2018-04-23 23:16:24 +08:00
|
|
|
dPrint(ZoneMinder::Logger::WARNING, "'$$process{command}' has not stopped at "
|
2018-04-24 23:41:04 +08:00
|
|
|
.strftime('%y/%m/%d %H:%M:%S', localtime())
|
|
|
|
.' after ' . KILL_DELAY . ' seconds.'
|
|
|
|
." Sending KILL to pid $$process{pid}\n"
|
|
|
|
);
|
|
|
|
kill('KILL', $$process{pid});
|
2018-04-30 22:09:23 +08:00
|
|
|
delete $terminating_processes{$command};
|
2011-04-28 22:22:44 +08:00
|
|
|
}
|
2017-04-01 00:25:48 +08:00
|
|
|
}
|
2017-04-30 02:52:47 +08:00
|
|
|
sigprocmask(SIG_UNBLOCK, $blockset) or die "dying at unblock...\n";
|
2018-04-24 23:41:04 +08:00
|
|
|
} # end sub check_for_processess_to_kill
|
2015-05-22 03:37:08 +08:00
|
|
|
|
2016-08-23 00:08:49 +08:00
|
|
|
sub stop {
|
2017-04-28 22:32:05 +08:00
|
|
|
my ( $daemon, @args ) = @_;
|
|
|
|
my $command = join(' ', $daemon, @args );
|
|
|
|
my $process = $cmd_hash{$command};
|
|
|
|
if ( !$process ) {
|
2018-05-04 00:50:54 +08:00
|
|
|
dPrint(ZoneMinder::Logger::WARNING, "Can't find process with command of '$command'");
|
2018-04-24 23:41:04 +08:00
|
|
|
return;
|
2017-04-28 22:32:05 +08:00
|
|
|
}
|
2015-05-22 03:37:08 +08:00
|
|
|
|
2018-04-25 22:48:50 +08:00
|
|
|
send_stop(1, $process);
|
2011-04-28 22:22:44 +08:00
|
|
|
}
|
|
|
|
|
2018-04-24 23:41:04 +08:00
|
|
|
# restart is the same as stop, except that we flag the processes for restarting once it dies
|
|
|
|
# One difference is that if we don't know about the process, then we start it.
|
2016-08-23 00:08:49 +08:00
|
|
|
sub restart {
|
2018-04-24 23:41:04 +08:00
|
|
|
my ( $daemon, @args ) = @_;
|
2017-04-28 22:32:05 +08:00
|
|
|
|
2018-04-25 22:48:50 +08:00
|
|
|
my $command = join(' ', $daemon, @args);
|
2018-04-24 23:41:04 +08:00
|
|
|
dPrint(ZoneMinder::Logger::DEBUG, "Restarting $command\n");
|
2017-04-28 22:32:05 +08:00
|
|
|
my $process = $cmd_hash{$command};
|
2018-04-24 23:41:04 +08:00
|
|
|
if ( !$process ) {
|
|
|
|
dPrint(ZoneMinder::Logger::WARNING, "Can't find process with command of '$command'\n");
|
|
|
|
start($daemon, @args);
|
|
|
|
return;
|
2017-04-28 22:32:05 +08:00
|
|
|
}
|
2018-04-24 23:41:04 +08:00
|
|
|
# Start will be handled by the reaper
|
|
|
|
send_stop(0, $process);
|
|
|
|
return;
|
2011-04-28 22:22:44 +08:00
|
|
|
}
|
|
|
|
|
2016-08-23 00:08:49 +08:00
|
|
|
sub reload {
|
2017-04-28 22:32:05 +08:00
|
|
|
my $daemon = shift;
|
|
|
|
my @args = @_;
|
2011-04-28 22:22:44 +08:00
|
|
|
|
2018-04-25 22:48:50 +08:00
|
|
|
my $command = join(' ', $daemon, @args);
|
2017-04-28 22:32:05 +08:00
|
|
|
my $process = $cmd_hash{$command};
|
|
|
|
if ( $process ) {
|
|
|
|
if ( $process->{pid} ) {
|
2018-04-24 23:41:04 +08:00
|
|
|
kill('HUP', $process->{pid});
|
2011-04-28 22:22:44 +08:00
|
|
|
}
|
2017-04-28 22:32:05 +08:00
|
|
|
}
|
2011-04-28 22:22:44 +08:00
|
|
|
}
|
|
|
|
|
2016-08-23 00:08:49 +08:00
|
|
|
sub logrot {
|
2017-04-28 22:32:05 +08:00
|
|
|
logReinit();
|
2018-04-25 22:48:50 +08:00
|
|
|
foreach my $process ( values %pid_hash ) {
|
2018-01-11 01:57:37 +08:00
|
|
|
if ( $process->{pid} ) {
|
|
|
|
# && $process->{command} =~ /^zm.*\.pl/ ) {
|
2018-04-24 23:41:04 +08:00
|
|
|
kill('HUP', $process->{pid});
|
2011-04-28 22:22:44 +08:00
|
|
|
}
|
2017-04-28 22:32:05 +08:00
|
|
|
}
|
2011-04-28 22:22:44 +08:00
|
|
|
}
|
|
|
|
|
2018-04-30 22:09:23 +08:00
|
|
|
sub shutdown_sig_handler {
|
|
|
|
$zm_terminate = 1;
|
|
|
|
}
|
|
|
|
|
2016-08-23 00:08:49 +08:00
|
|
|
sub reaper {
|
2017-04-28 22:32:05 +08:00
|
|
|
my $saved_status = $!;
|
2018-04-30 22:09:23 +08:00
|
|
|
|
|
|
|
# Wait for a child to terminate
|
2018-04-25 22:48:50 +08:00
|
|
|
while ( (my $cpid = waitpid(-1, WNOHANG)) > 0 ) {
|
2017-04-28 22:32:05 +08:00
|
|
|
my $status = $?;
|
2011-04-28 22:22:44 +08:00
|
|
|
|
2017-04-28 22:32:05 +08:00
|
|
|
my $process = $pid_hash{$cpid};
|
2018-04-24 23:41:04 +08:00
|
|
|
delete $pid_hash{$cpid};
|
2011-04-28 22:22:44 +08:00
|
|
|
|
2017-04-28 22:32:05 +08:00
|
|
|
if ( !$process ) {
|
2018-04-25 22:48:50 +08:00
|
|
|
dPrint(ZoneMinder::Logger::INFO, "Can't find child with pid of '$cpid'\n");
|
2017-04-28 22:32:05 +08:00
|
|
|
next;
|
|
|
|
}
|
2018-04-24 23:41:04 +08:00
|
|
|
delete $terminating_processes{$$process{command}};
|
|
|
|
delete $$process{term_sent_at};
|
2011-04-28 22:22:44 +08:00
|
|
|
|
2017-04-28 22:32:05 +08:00
|
|
|
$process->{stopped} = time();
|
|
|
|
$process->{runtime} = ($process->{stopped}-$process->{started});
|
2018-04-24 23:41:04 +08:00
|
|
|
delete $process->{pid};
|
2011-04-28 22:22:44 +08:00
|
|
|
|
2017-04-28 22:32:05 +08:00
|
|
|
my $exit_status = $status>>8;
|
|
|
|
my $exit_signal = $status&0xfe;
|
|
|
|
my $core_dumped = $status&0x01;
|
2011-04-28 22:22:44 +08:00
|
|
|
|
2017-04-28 22:32:05 +08:00
|
|
|
my $out_str = "'$process->{command}' ";
|
|
|
|
if ( $exit_signal ) {
|
2018-04-30 22:09:23 +08:00
|
|
|
# 15 == TERM, 14 == ALARM
|
2017-04-28 22:32:05 +08:00
|
|
|
if ( $exit_signal == 15 || $exit_signal == 14 ) {
|
|
|
|
$out_str .= 'exited';
|
|
|
|
} else {
|
|
|
|
$out_str .= 'crashed';
|
|
|
|
}
|
|
|
|
$out_str .= ", signal $exit_signal";
|
|
|
|
} else {
|
|
|
|
$out_str .= 'exited ';
|
|
|
|
if ( $exit_status ) {
|
|
|
|
$out_str .= "abnormally, exit status $exit_status";
|
|
|
|
} else {
|
|
|
|
$out_str .= 'normally';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#print( ", core dumped" ) if ( $core_dumped );
|
|
|
|
$out_str .= "\n";
|
2011-04-28 22:22:44 +08:00
|
|
|
|
2017-04-28 22:32:05 +08:00
|
|
|
if ( $exit_status == 0 ) {
|
2018-04-25 22:48:50 +08:00
|
|
|
Info($out_str);
|
2017-04-28 22:32:05 +08:00
|
|
|
} else {
|
2018-04-25 22:48:50 +08:00
|
|
|
Error($out_str);
|
2017-04-28 22:32:05 +08:00
|
|
|
}
|
2011-04-28 22:22:44 +08:00
|
|
|
|
2017-04-28 22:32:05 +08:00
|
|
|
if ( $process->{keepalive} ) {
|
|
|
|
# Schedule for immediate restart
|
|
|
|
$cmd_hash{$process->{command}} = $process;
|
|
|
|
if ( !$process->{delay} || ($process->{runtime} > $Config{ZM_MAX_RESTART_DELAY} ) ) {
|
|
|
|
#start( $process->{daemon}, @{$process->{args}} );
|
|
|
|
$process->{pending} = $process->{stopped};
|
|
|
|
$process->{delay} = 5;
|
|
|
|
} else {
|
|
|
|
$process->{pending} = $process->{stopped}+$process->{delay};
|
|
|
|
$process->{delay} *= 2;
|
|
|
|
# Limit the start delay to 15 minutes max
|
|
|
|
if ( $process->{delay} > $Config{ZM_MAX_RESTART_DELAY} ) {
|
|
|
|
$process->{delay} = $Config{ZM_MAX_RESTART_DELAY};
|
2011-04-28 22:22:44 +08:00
|
|
|
}
|
2017-04-28 22:32:05 +08:00
|
|
|
}
|
2018-04-30 22:09:23 +08:00
|
|
|
Debug("Delay for $$process{command} is now $$process{delay}");
|
2018-04-24 23:41:04 +08:00
|
|
|
} else {
|
|
|
|
delete $cmd_hash{$$process{command}};
|
2011-04-28 22:22:44 +08:00
|
|
|
}
|
2018-04-30 22:09:23 +08:00
|
|
|
} # end while waitpid
|
2017-04-28 22:32:05 +08:00
|
|
|
$SIG{CHLD} = \&reaper;
|
|
|
|
$! = $saved_status;
|
2018-05-08 22:30:12 +08:00
|
|
|
Debug("Leaving reaper");
|
2011-04-28 22:22:44 +08:00
|
|
|
}
|
|
|
|
|
2016-08-23 00:08:49 +08:00
|
|
|
sub restartPending {
|
2018-04-30 22:09:23 +08:00
|
|
|
# Restart any pending processes, we list them first because cmd_hash may change in foreach
|
|
|
|
my @processes = values %cmd_hash;
|
|
|
|
foreach my $process ( @processes ) {
|
2017-04-28 22:32:05 +08:00
|
|
|
if ( $process->{pending} && $process->{pending} <= time() ) {
|
2018-04-24 23:41:04 +08:00
|
|
|
dPrint(ZoneMinder::Logger::INFO, "Starting pending process, $process->{command}\n");
|
|
|
|
start($process->{daemon}, @{$process->{args}});
|
2011-04-28 22:22:44 +08:00
|
|
|
}
|
2017-04-28 22:32:05 +08:00
|
|
|
}
|
2018-05-08 22:30:12 +08:00
|
|
|
Debug("done restartPending");
|
2011-04-28 22:22:44 +08:00
|
|
|
}
|
|
|
|
|
2016-08-23 00:08:49 +08:00
|
|
|
sub shutdownAll {
|
2017-04-28 22:32:05 +08:00
|
|
|
foreach my $pid ( keys %pid_hash ) {
|
|
|
|
# This is a quick fix because a SIGCHLD can happen and alter pid_hash while we are in here.
|
|
|
|
next if ! $pid_hash{$pid};
|
2018-04-25 22:48:50 +08:00
|
|
|
send_stop(1, $pid_hash{$pid});
|
2017-04-28 22:32:05 +08:00
|
|
|
}
|
2018-05-09 03:29:34 +08:00
|
|
|
while ( keys %terminating_processes ) {
|
2018-04-24 23:41:04 +08:00
|
|
|
check_for_processes_to_kill();
|
2018-04-30 22:09:23 +08:00
|
|
|
if ( %terminating_processes ) {
|
2018-05-09 03:29:34 +08:00
|
|
|
Debug("Still " . %terminating_processes . ' to die. sleeping');
|
2018-04-30 22:09:23 +08:00
|
|
|
sleep(1);
|
|
|
|
}
|
2017-04-28 22:32:05 +08:00
|
|
|
}
|
2018-05-09 03:29:34 +08:00
|
|
|
dPrint(ZoneMinder::Logger::INFO, 'Server shutdown at '
|
|
|
|
.strftime('%y/%m/%d %H:%M:%S', localtime())
|
|
|
|
."\n"
|
|
|
|
);
|
2018-04-25 22:48:50 +08:00
|
|
|
unlink(main::SOCK_FILE) or Error("Unable to unlink " . main::SOCK_FILE .". Error message was: $!") if ( -e main::SOCK_FILE );
|
|
|
|
unlink(ZM_PID) or Error("Unable to unlink " . ZM_PID .". Error message was: $!") if ( -e ZM_PID );
|
|
|
|
close(CLIENT);
|
|
|
|
close(SERVER);
|
2017-04-28 22:32:05 +08:00
|
|
|
exit();
|
2011-04-28 22:22:44 +08:00
|
|
|
}
|
|
|
|
|
2016-08-23 00:08:49 +08:00
|
|
|
sub check {
|
2017-04-28 22:32:05 +08:00
|
|
|
my $daemon = shift;
|
|
|
|
my @args = @_;
|
|
|
|
|
2018-04-25 22:48:50 +08:00
|
|
|
my $command = join(' ', $daemon, @args);
|
2017-04-28 22:32:05 +08:00
|
|
|
my $process = $cmd_hash{$command};
|
|
|
|
if ( !$process ) {
|
2018-04-25 22:48:50 +08:00
|
|
|
cPrint("unknown\n");
|
2017-04-28 22:32:05 +08:00
|
|
|
} elsif ( $process->{pending} ) {
|
2018-04-25 22:48:50 +08:00
|
|
|
cPrint("pending\n");
|
2017-04-28 22:32:05 +08:00
|
|
|
} else {
|
|
|
|
my $cpid = $process->{pid};
|
|
|
|
if ( ! $pid_hash{$cpid} ) {
|
2018-04-25 22:48:50 +08:00
|
|
|
cPrint("stopped\n");
|
2016-08-23 00:08:49 +08:00
|
|
|
} else {
|
2018-04-25 22:48:50 +08:00
|
|
|
cPrint("running\n");
|
2011-04-28 22:22:44 +08:00
|
|
|
}
|
2017-04-28 22:32:05 +08:00
|
|
|
}
|
2011-04-28 22:22:44 +08:00
|
|
|
}
|
|
|
|
|
2016-08-23 00:08:49 +08:00
|
|
|
sub status {
|
2017-04-28 22:32:05 +08:00
|
|
|
my $daemon = shift;
|
|
|
|
my @args = @_;
|
2011-04-28 22:22:44 +08:00
|
|
|
|
2017-04-28 22:32:05 +08:00
|
|
|
if ( defined($daemon) ) {
|
2018-04-24 23:41:04 +08:00
|
|
|
my $command = join(' ', $daemon, @args);
|
2017-04-28 22:32:05 +08:00
|
|
|
my $process = $cmd_hash{$command};
|
|
|
|
if ( ! $process ) {
|
2018-04-24 23:41:04 +08:00
|
|
|
dPrint(ZoneMinder::Logger::DEBUG, "'$command' not running\n");
|
|
|
|
return;
|
2011-04-28 22:22:44 +08:00
|
|
|
}
|
|
|
|
|
2017-04-28 22:32:05 +08:00
|
|
|
if ( $process->{pending} ) {
|
2018-04-24 23:41:04 +08:00
|
|
|
dPrint(ZoneMinder::Logger::DEBUG, "'$command' pending at "
|
|
|
|
.strftime('%y/%m/%d %H:%M:%S', localtime($process->{pending}))
|
2017-04-28 22:32:05 +08:00
|
|
|
."\n"
|
|
|
|
);
|
2015-05-22 03:37:08 +08:00
|
|
|
} else {
|
2018-04-24 23:41:04 +08:00
|
|
|
my $pid = $process->{pid};
|
|
|
|
if ( ! $pid_hash{$pid} ) {
|
|
|
|
dPrint(ZoneMinder::Logger::DEBUG, "'$command' not running\n");
|
|
|
|
return;
|
2017-04-28 22:32:05 +08:00
|
|
|
}
|
2011-06-21 17:19:10 +08:00
|
|
|
}
|
2018-04-24 23:41:04 +08:00
|
|
|
dPrint(ZoneMinder::Logger::DEBUG, "'$command' running since "
|
|
|
|
.strftime('%y/%m/%d %H:%M:%S', localtime($process->{started}))
|
2017-04-28 22:32:05 +08:00
|
|
|
.", pid = $process->{pid}"
|
|
|
|
);
|
|
|
|
} else {
|
2018-04-24 23:41:04 +08:00
|
|
|
foreach my $process ( values %pid_hash ) {
|
2017-04-28 22:32:05 +08:00
|
|
|
my $out_str = "'$process->{command}' running since "
|
2018-04-24 23:41:04 +08:00
|
|
|
.strftime('%y/%m/%d %H:%M:%S', localtime($process->{started}))
|
2017-04-28 22:32:05 +08:00
|
|
|
.", pid = $process->{pid}"
|
|
|
|
;
|
2018-04-24 23:41:04 +08:00
|
|
|
$out_str .= ", valid" if ( kill(0, $process->{pid}) );
|
2017-04-28 22:32:05 +08:00
|
|
|
$out_str .= "\n";
|
2018-04-24 23:41:04 +08:00
|
|
|
dPrint(ZoneMinder::Logger::DEBUG, $out_str);
|
2011-06-21 17:19:10 +08:00
|
|
|
}
|
2018-04-24 23:41:04 +08:00
|
|
|
foreach my $process ( values %cmd_hash ) {
|
2017-04-28 22:32:05 +08:00
|
|
|
if ( $process->{pending} ) {
|
2018-04-24 23:41:04 +08:00
|
|
|
dPrint(ZoneMinder::Logger::DEBUG, "'$process->{command}' pending at "
|
2018-05-31 22:25:16 +08:00
|
|
|
.strftime('%y/%m/%d %H:%M:%S', localtime($process->{pending}))
|
2017-04-28 22:32:05 +08:00
|
|
|
."\n"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
} # end foreach process
|
|
|
|
}
|
2018-04-24 23:41:04 +08:00
|
|
|
} # end sub status
|
2017-04-28 22:32:05 +08:00
|
|
|
|
|
|
|
sub killAll {
|
|
|
|
my $delay = shift;
|
2018-04-24 23:41:04 +08:00
|
|
|
# Why sleep before sending term?
|
|
|
|
#sleep( $delay );
|
2017-04-28 22:32:05 +08:00
|
|
|
my $killall;
|
|
|
|
if ( '@HOST_OS@' eq 'BSD' ) {
|
|
|
|
$killall = 'killall -q -';
|
|
|
|
} elsif ( '@HOST_OS@' eq 'solaris' ) {
|
|
|
|
$killall = 'pkill -';
|
|
|
|
} else {
|
|
|
|
$killall = 'killall -q -s ';
|
|
|
|
}
|
|
|
|
foreach my $daemon ( @daemons ) {
|
|
|
|
my $cmd = $killall ."TERM $daemon";
|
2018-04-25 22:48:50 +08:00
|
|
|
Debug($cmd);
|
|
|
|
qx($cmd);
|
2017-04-28 22:32:05 +08:00
|
|
|
}
|
2018-04-25 22:48:50 +08:00
|
|
|
sleep($delay);
|
2017-04-28 22:32:05 +08:00
|
|
|
foreach my $daemon ( @daemons ) {
|
|
|
|
my $cmd = $killall."KILL $daemon";
|
2018-04-25 22:48:50 +08:00
|
|
|
Debug($cmd);
|
|
|
|
qx($cmd);
|
2017-04-28 22:32:05 +08:00
|
|
|
}
|
2005-12-23 18:22:32 +08:00
|
|
|
}
|
2016-08-23 00:08:49 +08:00
|
|
|
1;
|
|
|
|
__END__
|