diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Memory.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/Memory.pm.in index 834e0747c..2529c8786 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Memory.pm.in +++ b/scripts/ZoneMinder/lib/ZoneMinder/Memory.pm.in @@ -147,7 +147,7 @@ our $mem_data = { last_write_index => { type=>'uint32', seq=>$mem_seq++ }, last_read_index => { type=>'uint32', seq=>$mem_seq++ }, state => { type=>'uint32', seq=>$mem_seq++ }, - last_event => { type=>'uint32', seq=>$mem_seq++ }, + last_event => { type=>'uint64', seq=>$mem_seq++ }, action => { type=>'uint32', seq=>$mem_seq++ }, brightness => { type=>'int32', seq=>$mem_seq++ }, hue => { type=>'int32', seq=>$mem_seq++ }, @@ -167,7 +167,6 @@ our $mem_data = { last_read_time => { type=>'time_t64', seq=>$mem_seq++ }, control_state => { type=>'uint8[256]', seq=>$mem_seq++ }, alarm_cause => { type=>'int8[256]', seq=>$mem_seq++ }, - } }, trigger_data => { type=>'TriggerData', seq=>$mem_seq++, 'contents'=> { diff --git a/scripts/zmdc.pl.in b/scripts/zmdc.pl.in index 4b58e2b19..c70fbe9b7 100644 --- a/scripts/zmdc.pl.in +++ b/scripts/zmdc.pl.in @@ -240,11 +240,11 @@ use Sys::MemInfo qw(totalmem freemem totalswap freeswap); use ZoneMinder::Server qw(CpuLoad); #use Data::Dumper; -# We count 10 of these, so total timeout is this value *10. -use constant KILL_DELAY => 10; # seconds +use constant KILL_DELAY => 60; # seconds to wait between sending TERM and sending KILL our %cmd_hash; our %pid_hash; +our %terminating_processes; sub run { my $fd = 0; @@ -268,6 +268,7 @@ sub run { Error( "Can't open pid file at " . ZM_PID ); } + # Tell any existing processes to die, wait 1 second between TERM and KILL killAll( 1 ); dPrint( ZoneMinder::Logger::INFO, 'Socket should be open at ' .main::SOCK_FILE ); @@ -303,7 +304,6 @@ sub run { if ( ! ( $secs_count % 60 ) ) { $dbh = zmDbConnect() if ! $dbh->ping(); my @cpuload = CpuLoad(); - dPrint( ZoneMinder::Logger::DEBUG, 'Updating Server record' ); if ( ! defined $dbh->do(q{UPDATE Servers SET Status=?,CpuLoad=?,TotalMem=?,FreeMem=?,TotalSwap=?,FreeSwap=? WHERE Id=?}, undef, 'Running', $cpuload[0], &totalmem, &freemem, &totalswap, &freeswap, $Config{ZM_SERVER_ID} ) ) { Error("Failed Updating status of Server record for Id=$Config{ZM_SERVER_ID}".$dbh->errstr()); @@ -366,7 +366,10 @@ sub run { #print( "Select timed out\n" ); restartPending(); } - } + + check_for_processes_to_kill(); + + } # end while dPrint( ZoneMinder::Logger::INFO, 'Server exiting at ' .strftime( '%y/%m/%d %H:%M:%S', localtime() ) ."\n" @@ -515,44 +518,33 @@ sub send_stop { ."\n" ); $process->{keepalive} = !$final; + $process->{term_sent_at} = time; + $process->{pending} = 0; + $terminating_processes{$command} = $process; + + kill( 'TERM', $pid ); return $pid; } # end sub send_stop -sub kill_until_dead { - my ( $process ) = @_; -# Now check it has actually gone away, if not kill -9 it - my $count = 0; +sub check_for_processes_to_kill { + # Turn off SIGCHLD my $sigset = POSIX::SigSet->new; my $blockset = POSIX::SigSet->new(SIGCHLD); sigprocmask(SIG_BLOCK, $blockset, $sigset ) or die "dying at block...\n"; - while( $process and $$process{pid} and kill( 0, $$process{pid} ) ) { - if ( $count++ > 10 ) { + foreach my $command ( %terminating_processes ) { + my $process = $cmd_hash{$command}; + if ( $$process{term_sent_at} - time > KILL_DELAY ) { dPrint(ZoneMinder::Logger::WARNING, "'$$process{command}' has not stopped at " - .strftime('%y/%m/%d %H:%M:%S', localtime()) - .' after ' . ( KILL_DELAY * $count ) . ' seconds.' - ." Sending KILL to pid $$process{pid}\n" - ); - kill( 'KILL', $$process{pid} ); - last; + .strftime('%y/%m/%d %H:%M:%S', localtime()) + .' after ' . KILL_DELAY . ' seconds.' + ." Sending KILL to pid $$process{pid}\n" + ); + kill('KILL', $$process{pid}); } - - # THe purpose of the signal blocking is to simplify the concurrency - sigprocmask(SIG_UNBLOCK, $blockset) or die "dying at unblock...\n"; - sleep( KILL_DELAY ); - sigprocmask(SIG_BLOCK, $blockset, $sigset ) or die "dying at block...\n"; } sigprocmask(SIG_UNBLOCK, $blockset) or die "dying at unblock...\n"; -} - -sub _stop { - my ($final, $process ) = @_; - - my $pid = send_stop( $final, $process ); - return if ! $pid; - delete( $cmd_hash{$$process{command}} ); - kill_until_dead( $process ); -} +} # end sub check_for_processess_to_kill sub stop { my ( $daemon, @args ) = @_; @@ -560,57 +552,49 @@ sub stop { my $process = $cmd_hash{$command}; if ( !$process ) { dPrint( ZoneMinder::Logger::WARNING, "Can't find process with command of '$command'\n" ); - return(); + return; } - _stop( 1, $process ); + send_stop( 1, $process ); } +# 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. sub restart { - my $daemon = shift; - my @args = @_; + my ( $daemon, @args ) = @_; - my $command = $daemon; - $command .= ' '.join( ' ', ( @args ) ) if @args; - dPrint ( ZoneMinder::Logger::DEBUG, "Restarting $command\n"); + my $command = join(' ', $daemon, @args ); + dPrint(ZoneMinder::Logger::DEBUG, "Restarting $command\n"); my $process = $cmd_hash{$command}; - if ( $process ) { - dPrint( ZoneMinder::Logger::DEBUG, "Have process" ); - if ( $process->{pid} ) { - dPrint( ZoneMinder::Logger::DEBUG, "Have process pid " .$process->{pid} ); - my $cpid = $process->{pid}; - if ( defined($pid_hash{$cpid}) ) { - dPrint( ZoneMinder::Logger::DEBUG, "Have process pid hash " .$process->{pid} ); - _stop( 0, $process ); - return; - } else { - dPrint( ZoneMinder::Logger::DEBUG, "Not sending stop" ); - } - } + if ( !$process ) { + dPrint(ZoneMinder::Logger::WARNING, "Can't find process with command of '$command'\n"); + start($daemon, @args); + return; } - start( $daemon, @args ); + # Start will be handled by the reaper + send_stop(0, $process); + return; } sub reload { my $daemon = shift; my @args = @_; - my $command = $daemon; - $command .= ' '.join( ' ', ( @args ) ) if ( @args ); + my $command = join(' ', $daemon, @args ) ; my $process = $cmd_hash{$command}; if ( $process ) { if ( $process->{pid} ) { - kill( 'HUP', $process->{pid} ); + kill('HUP', $process->{pid}); } } } sub logrot { logReinit(); - foreach my $process ( values( %pid_hash ) ) { + foreach my $process ( values(%pid_hash) ) { if ( $process->{pid} ) { # && $process->{command} =~ /^zm.*\.pl/ ) { - kill( 'HUP', $process->{pid} ); + kill('HUP', $process->{pid}); } } } @@ -621,16 +605,18 @@ sub reaper { my $status = $?; my $process = $pid_hash{$cpid}; - delete( $pid_hash{$cpid} ); + delete $pid_hash{$cpid}; if ( !$process ) { dPrint( ZoneMinder::Logger::INFO, "Can't find child with pid of '$cpid'\n" ); next; } + delete $terminating_processes{$$process{command}}; + delete $$process{term_sent_at}; $process->{stopped} = time(); $process->{runtime} = ($process->{stopped}-$process->{started}); - delete( $process->{pid} ); + delete $process->{pid}; my $exit_status = $status>>8; my $exit_signal = $status&0xfe; @@ -677,6 +663,8 @@ sub reaper { $process->{delay} = $Config{ZM_MAX_RESTART_DELAY}; } } + } else { + delete $cmd_hash{$$process{command}}; } } $SIG{CHLD} = \&reaper; @@ -687,8 +675,8 @@ sub restartPending { # Restart any pending processes foreach my $process ( values( %cmd_hash ) ) { if ( $process->{pending} && $process->{pending} <= time() ) { - dPrint( ZoneMinder::Logger::INFO, "Starting pending process, $process->{command}\n" ); - start( $process->{daemon}, @{$process->{args}} ); + dPrint(ZoneMinder::Logger::INFO, "Starting pending process, $process->{command}\n"); + start($process->{daemon}, @{$process->{args}}); } } } @@ -699,21 +687,12 @@ sub shutdownAll { next if ! $pid_hash{$pid}; send_stop( 1, $pid_hash{$pid} ); } - 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}; - - my $process = $pid_hash{$pid}; - - kill_until_dead( $process ); - delete( $cmd_hash{$$process{command}} ); - delete( $pid_hash{$pid} ); + while ( %terminating_processes ) { + check_for_processes_to_kill(); + sleep(1) if %terminating_processes; } -if ( 0 ) { - killAll( 5 ); -} - dPrint( ZoneMinder::Logger::INFO, "Server shutdown at " - .strftime( '%y/%m/%d %H:%M:%S', localtime() ) + dPrint(ZoneMinder::Logger::INFO, "Server shutdown at " + .strftime('%y/%m/%d %H:%M:%S', localtime()) ."\n" ); unlink( main::SOCK_FILE ) or Error( "Unable to unlink " . main::SOCK_FILE .". Error message was: $!" ) if ( -e main::SOCK_FILE ); @@ -748,53 +727,54 @@ sub status { my @args = @_; if ( defined($daemon) ) { - my $command = join( ' ', $daemon, @args ); + my $command = join(' ', $daemon, @args); my $process = $cmd_hash{$command}; if ( ! $process ) { - dPrint( ZoneMinder::Logger::DEBUG, "'$command' not running\n" ); - return(); + dPrint(ZoneMinder::Logger::DEBUG, "'$command' not running\n"); + return; } if ( $process->{pending} ) { - dPrint( ZoneMinder::Logger::DEBUG, "'$process->{command}' pending at " - .strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{pending} ) ) + dPrint(ZoneMinder::Logger::DEBUG, "'$command' pending at " + .strftime('%y/%m/%d %H:%M:%S', localtime($process->{pending})) ."\n" ); } else { - my $cpid = $process->{pid}; - if ( ! $pid_hash{$cpid} ) { - dPrint( ZoneMinder::Logger::DEBUG, "'$command' not running\n" ); - return(); + my $pid = $process->{pid}; + if ( ! $pid_hash{$pid} ) { + dPrint(ZoneMinder::Logger::DEBUG, "'$command' not running\n"); + return; } } - dPrint( ZoneMinder::Logger::DEBUG, "'$process->{command}' running since " - .strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{started} ) ) + dPrint(ZoneMinder::Logger::DEBUG, "'$command' running since " + .strftime('%y/%m/%d %H:%M:%S', localtime($process->{started})) .", pid = $process->{pid}" ); } else { - foreach my $process ( values(%pid_hash) ) { + foreach my $process ( values %pid_hash ) { my $out_str = "'$process->{command}' running since " - .strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{started}) ) + .strftime('%y/%m/%d %H:%M:%S', localtime($process->{started})) .", pid = $process->{pid}" ; - $out_str .= ", valid" if ( kill( 0, $process->{pid} ) ); + $out_str .= ", valid" if ( kill(0, $process->{pid}) ); $out_str .= "\n"; - dPrint( ZoneMinder::Logger::DEBUG, $out_str ); + dPrint(ZoneMinder::Logger::DEBUG, $out_str); } - foreach my $process ( values( %cmd_hash ) ) { + foreach my $process ( values %cmd_hash ) { if ( $process->{pending} ) { - dPrint( ZoneMinder::Logger::DEBUG, "'$process->{command}' pending at " - .strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{pending} ) ) + dPrint(ZoneMinder::Logger::DEBUG, "'$process->{command}' pending at " + .strftime( '%y/%m/%d %H:%M:%S', localtime($process->{pending})) ."\n" ); } } # end foreach process } -} +} # end sub status sub killAll { my $delay = shift; - sleep( $delay ); + # Why sleep before sending term? + #sleep( $delay ); my $killall; if ( '@HOST_OS@' eq 'BSD' ) { $killall = 'killall -q -'; @@ -815,6 +795,5 @@ sub killAll { qx( $cmd ); } } - 1; __END__ diff --git a/src/zm_db.cpp b/src/zm_db.cpp index 186551673..d4a927f3b 100644 --- a/src/zm_db.cpp +++ b/src/zm_db.cpp @@ -31,8 +31,10 @@ bool zmDbConnected = false; bool zmDbConnect() { // For some reason having these lines causes memory corruption and crashing on newer debian/ubuntu // But they really need to be here in order to prevent a double open of mysql - if ( zmDbConnected ) + if ( zmDbConnected ) { + Warning("Calling zmDbConnect when already connected"); return true; + } if ( !mysql_init(&dbconn) ) { Error("Can't initialise database connection: %s", mysql_error(&dbconn)); diff --git a/src/zm_ffmpeg.cpp b/src/zm_ffmpeg.cpp index b4d7e323a..4d78c45ab 100644 --- a/src/zm_ffmpeg.cpp +++ b/src/zm_ffmpeg.cpp @@ -27,9 +27,9 @@ void FFMPEGInit() { static bool bInit = false; if ( !bInit ) { - if ( logDebugging() ) - av_log_set_level( AV_LOG_DEBUG ); - else + //if ( logDebugging() ) + //av_log_set_level( AV_LOG_DEBUG ); + //else av_log_set_level( AV_LOG_QUIET ); av_register_all(); avformat_network_init(); diff --git a/src/zm_ffmpeg_camera.cpp b/src/zm_ffmpeg_camera.cpp index d77c1e788..29db3600d 100644 --- a/src/zm_ffmpeg_camera.cpp +++ b/src/zm_ffmpeg_camera.cpp @@ -146,13 +146,7 @@ FfmpegCamera::~FfmpegCamera() { } void FfmpegCamera::Initialise() { - if ( logDebugging() ) - av_log_set_level( AV_LOG_DEBUG ); - else - av_log_set_level( AV_LOG_QUIET ); - - av_register_all(); - avformat_network_init(); + FFMPEGInit(); } void FfmpegCamera::Terminate() { diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index d41291b79..01a67581b 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -410,14 +410,15 @@ Monitor::Monitor( + (image_buffer_count*camera->ImageSize()) + 64; /* Padding used to permit aligning the images buffer to 64 byte boundary */ - Debug( 1, "mem.size=%d", mem_size ); + Debug(1, "mem.size SharedData=%d TriggerData=%d VideoStoreData=%d total=%d", + sizeof(SharedData), sizeof(TriggerData), sizeof(VideoStoreData), mem_size); mem_ptr = NULL; - storage = new Storage( storage_id ); - Debug(1, "Storage path: %s", storage->Path() ); + storage = new Storage(storage_id); + Debug(1, "Storage path: %s", storage->Path()); // Should maybe store this for later use char monitor_dir[PATH_MAX] = ""; - snprintf( monitor_dir, sizeof(monitor_dir), "%s/%d", storage->Path(), id ); + snprintf(monitor_dir, sizeof(monitor_dir), "%s/%d", storage->Path(), id); if ( purpose == CAPTURE ) { struct stat statbuf; @@ -916,9 +917,9 @@ void Monitor::actionReload() { void Monitor::actionEnable() { shared_data->action |= RELOAD; + db_mutex.lock(); static char sql[ZM_SQL_SML_BUFSIZ]; snprintf(sql, sizeof(sql), "UPDATE Monitors SET Enabled = 1 WHERE Id = %d", id); - db_mutex.lock(); if ( mysql_query(&dbconn, sql) ) { Error("Can't run query: %s", mysql_error(&dbconn)); } diff --git a/src/zm_monitor.h b/src/zm_monitor.h index 0f78b55a4..800dab9db 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -106,44 +106,44 @@ protected: typedef enum { CLOSE_TIME, CLOSE_IDLE, CLOSE_ALARM } EventCloseMode; - /* sizeof(SharedData) expected to be 336 bytes on 32bit and 64bit */ + /* sizeof(SharedData) expected to be 340 bytes on 32bit and 64bit */ typedef struct { uint32_t size; /* +0 */ uint32_t last_write_index; /* +4 */ uint32_t last_read_index; /* +8 */ uint32_t state; /* +12 */ - uint32_t last_event; /* +16 */ - uint32_t action; /* +20 */ - int32_t brightness; /* +24 */ - int32_t hue; /* +28 */ - int32_t colour; /* +32 */ - int32_t contrast; /* +36 */ - int32_t alarm_x; /* +40 */ - int32_t alarm_y; /* +44 */ - uint8_t valid; /* +48 */ - uint8_t active; /* +49 */ - uint8_t signal; /* +50 */ - uint8_t format; /* +51 */ - uint32_t imagesize; /* +52 */ - uint32_t epadding1; /* +56 */ - uint32_t epadding2; /* +60 */ + uint64_t last_event; /* +16 */ + uint32_t action; /* +24 */ + int32_t brightness; /* +28 */ + int32_t hue; /* +32 */ + int32_t colour; /* +36 */ + int32_t contrast; /* +40 */ + int32_t alarm_x; /* +44 */ + int32_t alarm_y; /* +48 */ + uint8_t valid; /* +52 */ + uint8_t active; /* +53 */ + uint8_t signal; /* +54 */ + uint8_t format; /* +55 */ + uint32_t imagesize; /* +56 */ + uint32_t epadding1; /* +60 */ + uint32_t epadding2; /* +64 */ /* ** This keeps 32bit time_t and 64bit time_t identical and compatible as long as time is before 2038. ** Shared memory layout should be identical for both 32bit and 64bit and is multiples of 16. */ - union { /* +64 */ + union { /* +68 */ time_t startup_time; /* When the zmc process started. zmwatch uses this to see how long the process has been running without getting any images */ uint64_t extrapad1; }; - union { /* +72 */ + union { /* +76 */ time_t last_write_time; uint64_t extrapad2; }; - union { /* +80 */ + union { /* +84 */ time_t last_read_time; uint64_t extrapad3; }; - uint8_t control_state[256]; /* +88 */ + uint8_t control_state[256]; /* +92 */ char alarm_cause[256]; @@ -174,7 +174,7 @@ protected: //sizeOf(VideoStoreData) expected to be 4104 bytes on 32bit and 64bit typedef struct { uint32_t size; - unsigned long long current_event; + uint64_t current_event; char event_file[4096]; timeval recording; // used as both bool and a pointer to the timestamp when recording should begin //uint32_t frameNumber; @@ -206,7 +206,6 @@ protected: int last_state; uint64_t last_event; - public: MonitorLink( int p_id, const char *p_name ); ~MonitorLink(); @@ -318,8 +317,6 @@ protected: #endif // ZM_MEM_MAPPED off_t mem_size; unsigned char *mem_ptr; - Storage *storage; - SharedData *shared_data; TriggerData *trigger_data; VideoStoreData *video_store_data; @@ -329,8 +326,8 @@ protected: Snapshot *pre_event_buffer; Camera *camera; - - Event *event; + Event *event; + Storage *storage; int n_zones; Zone **zones; diff --git a/web/api/app/Plugin/CakePHP-Enum-Behavior b/web/api/app/Plugin/CakePHP-Enum-Behavior index ea90c0cd7..ca91b87fd 160000 --- a/web/api/app/Plugin/CakePHP-Enum-Behavior +++ b/web/api/app/Plugin/CakePHP-Enum-Behavior @@ -1 +1 @@ -Subproject commit ea90c0cd7f6e24333a90885e563b5d30b793db29 +Subproject commit ca91b87fda8e006e4fca2ed870f24f9a29c2905d diff --git a/web/api/app/Plugin/Crud b/web/api/app/Plugin/Crud index 0bd63fb46..1351dde6b 160000 --- a/web/api/app/Plugin/Crud +++ b/web/api/app/Plugin/Crud @@ -1 +1 @@ -Subproject commit 0bd63fb464957080ead342db58ca9e01532cf1ef +Subproject commit 1351dde6b4c75b215099ae8bcf5a21d6c6e10298