# include <stdio.h>
# include <stdlib.h>
# include <stdint.h>
# include <stdbool.h>
# include <string.h>
# include <assert.h>
# include <time.h>
# include <errno.h>
# include <unistd.h>
# include <sys/stat.h>
# include <pthread.h>
# include <bzlib.h>
# include "common/swaglog.h"
# include "logger.h"
# include <capnp/serialize.h>
# include "cereal/gen/cpp/log.capnp.h"
static void log_sentinel ( LoggerState * s , cereal : : Sentinel : : SentinelType type ) {
capnp : : MallocMessageBuilder msg ;
auto event = msg . initRoot < cereal : : Event > ( ) ;
event . setLogMonoTime ( nanos_since_boot ( ) ) ;
auto sen = event . initSentinel ( ) ;
sen . setType ( type ) ;
auto words = capnp : : messageToFlatArray ( msg ) ;
auto bytes = words . asBytes ( ) ;
logger_log ( s , bytes . begin ( ) , bytes . size ( ) , true ) ;
}
static int mkpath ( char * file_path ) {
assert ( file_path & & * file_path ) ;
char * p ;
for ( p = strchr ( file_path + 1 , ' / ' ) ; p ; p = strchr ( p + 1 , ' / ' ) ) {
* p = ' \0 ' ;
if ( mkdir ( file_path , 0777 ) = = - 1 ) {
if ( errno ! = EEXIST ) {
* p = ' / ' ;
return - 1 ;
}
}
* p = ' / ' ;
}
return 0 ;
}
void logger_init ( LoggerState * s , const char * log_name , const uint8_t * init_data , size_t init_data_len , bool has_qlog ) {
memset ( s , 0 , sizeof ( * s ) ) ;
if ( init_data ) {
s - > init_data = ( uint8_t * ) malloc ( init_data_len ) ;
assert ( s - > init_data ) ;
memcpy ( s - > init_data , init_data , init_data_len ) ;
s - > init_data_len = init_data_len ;
}
umask ( 0 ) ;
pthread_mutex_init ( & s - > lock , NULL ) ;
s - > part = - 1 ;
s - > has_qlog = has_qlog ;
time_t rawtime = time ( NULL ) ;
struct tm timeinfo ;
localtime_r ( & rawtime , & timeinfo ) ;
strftime ( s - > route_name , sizeof ( s - > route_name ) ,
" %Y-%m-%d--%H-%M-%S " , & timeinfo ) ;
snprintf ( s - > log_name , sizeof ( s - > log_name ) , " %s " , log_name ) ;
}
static LoggerHandle * logger_open ( LoggerState * s , const char * root_path ) {
int err ;
LoggerHandle * h = NULL ;
for ( int i = 0 ; i < LOGGER_MAX_HANDLES ; i + + ) {
if ( s - > handles [ i ] . refcnt = = 0 ) {
h = & s - > handles [ i ] ;
break ;
}
}
assert ( h ) ;
snprintf ( h - > segment_path , sizeof ( h - > segment_path ) ,
" %s/%s--%d " , root_path , s - > route_name , s - > part ) ;
snprintf ( h - > log_path , sizeof ( h - > log_path ) , " %s/%s.bz2 " , h - > segment_path , s - > log_name ) ;
snprintf ( h - > qlog_path , sizeof ( h - > qlog_path ) , " %s/qlog.bz2 " , h - > segment_path ) ;
snprintf ( h - > lock_path , sizeof ( h - > lock_path ) , " %s.lock " , h - > log_path ) ;
err = mkpath ( h - > log_path ) ;
if ( err ) return NULL ;
FILE * lock_file = fopen ( h - > lock_path , " wb " ) ;
if ( lock_file = = NULL ) return NULL ;
fclose ( lock_file ) ;
h - > log_file = fopen ( h - > log_path , " wb " ) ;
if ( h - > log_file = = NULL ) goto fail ;
if ( s - > has_qlog ) {
h - > qlog_file = fopen ( h - > qlog_path , " wb " ) ;
if ( h - > qlog_file = = NULL ) goto fail ;
}
int bzerror ;
h - > bz_file = BZ2_bzWriteOpen ( & bzerror , h - > log_file , 9 , 0 , 30 ) ;
if ( bzerror ! = BZ_OK ) goto fail ;
if ( s - > has_qlog ) {
h - > bz_qlog = BZ2_bzWriteOpen ( & bzerror , h - > qlog_file , 9 , 0 , 30 ) ;
if ( bzerror ! = BZ_OK ) goto fail ;
}
if ( s - > init_data ) {
BZ2_bzWrite ( & bzerror , h - > bz_file , s - > init_data , s - > init_data_len ) ;
if ( bzerror ! = BZ_OK ) goto fail ;
if ( s - > has_qlog ) {
// init data goes in the qlog too
BZ2_bzWrite ( & bzerror , h - > bz_qlog , s - > init_data , s - > init_data_len ) ;
if ( bzerror ! = BZ_OK ) goto fail ;
}
}
pthread_mutex_init ( & h - > lock , NULL ) ;
h - > refcnt + + ;
return h ;
fail :
LOGE ( " logger failed to open files " ) ;
if ( h - > bz_file ) {
BZ2_bzWriteClose ( & bzerror , h - > bz_file , 0 , NULL , NULL ) ;
h - > bz_file = NULL ;
}
if ( h - > bz_qlog ) {
BZ2_bzWriteClose ( & bzerror , h - > bz_qlog , 0 , NULL , NULL ) ;
h - > bz_qlog = NULL ;
}
if ( h - > qlog_file ) {
fclose ( h - > qlog_file ) ;
h - > qlog_file = NULL ;
}
if ( h - > log_file ) {
fclose ( h - > log_file ) ;
h - > log_file = NULL ;
}
return NULL ;
}
int logger_next ( LoggerState * s , const char * root_path ,
char * out_segment_path , size_t out_segment_path_len ,
int * out_part ) {
bool is_start_of_route = ! s - > cur_handle ;
if ( ! is_start_of_route ) log_sentinel ( s , cereal : : Sentinel : : SentinelType : : END_OF_SEGMENT ) ;
pthread_mutex_lock ( & s - > lock ) ;
s - > part + + ;
LoggerHandle * next_h = logger_open ( s , root_path ) ;
if ( ! next_h ) {
pthread_mutex_unlock ( & s - > lock ) ;
return - 1 ;
}
if ( s - > cur_handle ) {
lh_close ( s - > cur_handle ) ;
}
s - > cur_handle = next_h ;
if ( out_segment_path ) {
snprintf ( out_segment_path , out_segment_path_len , " %s " , next_h - > segment_path ) ;
}
if ( out_part ) {
* out_part = s - > part ;
}
pthread_mutex_unlock ( & s - > lock ) ;
log_sentinel ( s , is_start_of_route ? cereal : : Sentinel : : SentinelType : : START_OF_ROUTE : cereal : : Sentinel : : SentinelType : : START_OF_SEGMENT ) ;
return 0 ;
}
LoggerHandle * logger_get_handle ( LoggerState * s ) {
pthread_mutex_lock ( & s - > lock ) ;
LoggerHandle * h = s - > cur_handle ;
if ( h ) {
pthread_mutex_lock ( & h - > lock ) ;
h - > refcnt + + ;
pthread_mutex_unlock ( & h - > lock ) ;
}
pthread_mutex_unlock ( & s - > lock ) ;
return h ;
}
void logger_log ( LoggerState * s , uint8_t * data , size_t data_size , bool in_qlog ) {
pthread_mutex_lock ( & s - > lock ) ;
if ( s - > cur_handle ) {
lh_log ( s - > cur_handle , data , data_size , in_qlog ) ;
}
pthread_mutex_unlock ( & s - > lock ) ;
}
void logger_close ( LoggerState * s ) {
log_sentinel ( s , cereal : : Sentinel : : SentinelType : : END_OF_ROUTE ) ;
pthread_mutex_lock ( & s - > lock ) ;
free ( s - > init_data ) ;
if ( s - > cur_handle ) {
lh_close ( s - > cur_handle ) ;
}
pthread_mutex_unlock ( & s - > lock ) ;
}
void lh_log ( LoggerHandle * h , uint8_t * data , size_t data_size , bool in_qlog ) {
pthread_mutex_lock ( & h - > lock ) ;
assert ( h - > refcnt > 0 ) ;
int bzerror ;
BZ2_bzWrite ( & bzerror , h - > bz_file , data , data_size ) ;
if ( in_qlog & & h - > bz_qlog ! = NULL ) {
BZ2_bzWrite ( & bzerror , h - > bz_qlog , data , data_size ) ;
}
pthread_mutex_unlock ( & h - > lock ) ;
}
void lh_close ( LoggerHandle * h ) {
pthread_mutex_lock ( & h - > lock ) ;
assert ( h - > refcnt > 0 ) ;
h - > refcnt - - ;
if ( h - > refcnt = = 0 ) {
if ( h - > bz_file ) {
int bzerror ;
BZ2_bzWriteClose ( & bzerror , h - > bz_file , 0 , NULL , NULL ) ;
h - > bz_file = NULL ;
}
if ( h - > bz_qlog ) {
int bzerror ;
BZ2_bzWriteClose ( & bzerror , h - > bz_qlog , 0 , NULL , NULL ) ;
h - > bz_qlog = NULL ;
}
if ( h - > qlog_file ) {
fclose ( h - > qlog_file ) ;
h - > qlog_file = NULL ;
}
fclose ( h - > log_file ) ;
h - > log_file = NULL ;
unlink ( h - > lock_path ) ;
pthread_mutex_unlock ( & h - > lock ) ;
pthread_mutex_destroy ( & h - > lock ) ;
return ;
}
pthread_mutex_unlock ( & h - > lock ) ;
}