#!/usr/bin/env python3 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								import  os 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								import  shutil 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								import  threading 
 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								from  openpilot . system . hardware . hw  import  Paths 
 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								from  openpilot . common . swaglog  import  cloudlog 
 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								from  openpilot . system . loggerd . config  import  get_available_bytes ,  get_available_percent 
 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								from  openpilot . system . loggerd . uploader  import  listdir_by_creation 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								from  openpilot . system . loggerd . xattr_cache  import  getxattr 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								MIN_BYTES  =  5  *  1024  *  1024  *  1024 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								MIN_PERCENT  =  10 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								DELETE_LAST  =  [ ' boot ' ,  ' crash ' ] 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								PRESERVE_ATTR_NAME  =  ' user.preserve ' 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								PRESERVE_ATTR_VALUE  =  b ' 1 ' 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								PRESERVE_COUNT  =  5 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								def  has_preserve_xattr ( d :  str )  - >  bool : 
 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								  return  getxattr ( os . path . join ( Paths . log_root ( ) ,  d ) ,  PRESERVE_ATTR_NAME )  ==  PRESERVE_ATTR_VALUE 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								def  get_preserved_segments ( dirs_by_creation :  list [ str ] )  - >  set [ str ] : 
 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								  # skip deleting most recent N preserved segments (and their prior segment) 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								  preserved  =  set ( ) 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								  for  n ,  d  in  enumerate ( filter ( has_preserve_xattr ,  reversed ( dirs_by_creation ) ) ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    if  n  ==  PRESERVE_COUNT : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      break 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    date_str ,  _ ,  seg_str  =  d . rpartition ( " -- " ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    # ignore non-segment directories 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    if  not  date_str : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      continue 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    try : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      seg_num  =  int ( seg_str ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    except  ValueError : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      continue 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								    # preserve segment and two prior 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    for  _seg_num  in  range ( max ( 0 ,  seg_num  -  2 ) ,  seg_num  +  1 ) : 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								      preserved . add ( f " { date_str } -- { _seg_num } " ) 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  return  preserved 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								def  deleter_thread ( exit_event :  threading . Event ) : 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  while  not  exit_event . is_set ( ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    out_of_bytes  =  get_available_bytes ( default = MIN_BYTES  +  1 )  <  MIN_BYTES 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    out_of_percent  =  get_available_percent ( default = MIN_PERCENT  +  1 )  <  MIN_PERCENT 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    if  out_of_percent  or  out_of_bytes : 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								      dirs  =  listdir_by_creation ( Paths . log_root ( ) ) 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								      preserved_dirs  =  get_preserved_segments ( dirs ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      # remove the earliest directory we can 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								      for  delete_dir  in  sorted ( dirs ,  key = lambda  d :  ( d  in  DELETE_LAST ,  d  in  preserved_dirs ) ) : 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								        delete_path  =  os . path . join ( Paths . log_root ( ) ,  delete_dir ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        if  any ( name . endswith ( " .lock " )  for  name  in  os . listdir ( delete_path ) ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          continue 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        try : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          cloudlog . info ( f " deleting  { delete_path } " ) 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								          shutil . rmtree ( delete_path ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          break 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        except  OSError : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          cloudlog . exception ( f " issue deleting  { delete_path } " ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      exit_event . wait ( .1 ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    else : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      exit_event . wait ( 30 ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								def  main ( ) : 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  deleter_thread ( threading . Event ( ) ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								if  __name__  ==  " __main__ " : 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  main ( )