#!/usr/bin/env python3
import datetime
import os
import signal
import subprocess
import sys
import traceback
import cereal . messaging as messaging
import selfdrive . crash as crash
from common . basedir import BASEDIR
from common . params import Params , ParamKeyType
from common . text_window import TextWindow
from selfdrive . boardd . set_time import set_time
from selfdrive . hardware import HARDWARE , PC
from selfdrive . manager . helpers import unblock_stdout
from selfdrive . manager . process import ensure_running
from selfdrive . manager . process_config import managed_processes
from selfdrive . athena . registration import register , UNREGISTERED_DONGLE_ID
from selfdrive . swaglog import cloudlog , add_file_handler
from selfdrive . version import is_dirty , get_commit , get_version , get_origin , get_short_branch , \
terms_version , training_version , is_comma_remote
sys . path . append ( os . path . join ( BASEDIR , " pyextra " ) )
def manager_init ( ) :
# update system time from panda
set_time ( cloudlog )
# save boot log
subprocess . call ( " ./bootlog " , cwd = os . path . join ( BASEDIR , " selfdrive/loggerd " ) )
params = Params ( )
params . clear_all ( ParamKeyType . CLEAR_ON_MANAGER_START )
default_params = [
( " CompletedTrainingVersion " , " 0 " ) ,
( " HasAcceptedTerms " , " 0 " ) ,
( " OpenpilotEnabledToggle " , " 1 " ) ,
]
if not PC :
default_params . append ( ( " LastUpdateTime " , datetime . datetime . utcnow ( ) . isoformat ( ) . encode ( ' utf8 ' ) ) )
if params . get_bool ( " RecordFrontLock " ) :
params . put_bool ( " RecordFront " , True )
if not params . get_bool ( " DisableRadar_Allow " ) :
params . delete ( " DisableRadar " )
# set unset params
for k , v in default_params :
if params . get ( k ) is None :
params . put ( k , v )
# is this dashcam?
if os . getenv ( " PASSIVE " ) is not None :
params . put_bool ( " Passive " , bool ( int ( os . getenv ( " PASSIVE " ) ) ) )
if params . get ( " Passive " ) is None :
raise Exception ( " Passive must be set to continue " )
# Create folders needed for msgq
try :
os . mkdir ( " /dev/shm " )
except FileExistsError :
pass
except PermissionError :
print ( " WARNING: failed to make /dev/shm " )
# set version params
params . put ( " Version " , get_version ( ) )
params . put ( " TermsVersion " , terms_version )
params . put ( " TrainingVersion " , training_version )
params . put ( " GitCommit " , get_commit ( default = " " ) )
params . put ( " GitBranch " , get_short_branch ( default = " " ) )
params . put ( " GitRemote " , get_origin ( default = " " ) )
# set dongle id
reg_res = register ( show_spinner = True )
if reg_res :
dongle_id = reg_res
else :
serial = params . get ( " HardwareSerial " )
raise Exception ( f " Registration failed for device { serial } " )
os . environ [ ' DONGLE_ID ' ] = dongle_id # Needed for swaglog
if not is_dirty ( ) :
os . environ [ ' CLEAN ' ] = ' 1 '
cloudlog . bind_global ( dongle_id = dongle_id , version = get_version ( ) , dirty = is_dirty ( ) ,
device = HARDWARE . get_device_type ( ) )
if is_comma_remote ( ) and not ( os . getenv ( " NOLOG " ) or os . getenv ( " NOCRASH " ) or PC ) :
crash . init ( )
crash . bind_user ( id = dongle_id )
crash . bind_extra ( dirty = is_dirty ( ) , origin = get_origin ( ) , branch = get_short_branch ( ) , commit = get_commit ( ) ,
device = HARDWARE . get_device_type ( ) )
def manager_prepare ( ) :
for p in managed_processes . values ( ) :
p . prepare ( )
def manager_cleanup ( ) :
# send signals to kill all procs
for p in managed_processes . values ( ) :
p . stop ( block = False )
# ensure all are killed
for p in managed_processes . values ( ) :
p . stop ( block = True )
cloudlog . info ( " everything is dead " )
def manager_thread ( ) :
cloudlog . bind ( daemon = " manager " )
cloudlog . info ( " manager start " )
cloudlog . info ( { " environ " : os . environ } )
params = Params ( )
ignore = [ ]
if params . get ( " DongleId " , encoding = ' utf8 ' ) == UNREGISTERED_DONGLE_ID :
ignore + = [ " manage_athenad " , " uploader " ]
if os . getenv ( " NOBOARD " ) is not None :
ignore . append ( " pandad " )
if os . getenv ( " BLOCK " ) is not None :
ignore + = os . getenv ( " BLOCK " ) . split ( " , " )
ensure_running ( managed_processes . values ( ) , started = False , not_run = ignore )
started_prev = False
sm = messaging . SubMaster ( [ ' deviceState ' ] )
pm = messaging . PubMaster ( [ ' managerState ' ] )
while True :
sm . update ( )
not_run = ignore [ : ]
if sm [ ' deviceState ' ] . freeSpacePercent < 5 :
not_run . append ( " loggerd " )
started = sm [ ' deviceState ' ] . started
driverview = params . get_bool ( " IsDriverViewEnabled " )
ensure_running ( managed_processes . values ( ) , started , driverview , not_run )
# trigger an update after going offroad
if started_prev and not started and ' updated ' in managed_processes :
os . sync ( )
managed_processes [ ' updated ' ] . signal ( signal . SIGHUP )
started_prev = started
running = ' ' . join ( " %s %s \u001b [0m " % ( " \u001b [32m " if p . proc . is_alive ( ) else " \u001b [31m " , p . name )
for p in managed_processes . values ( ) if p . proc )
print ( running )
cloudlog . debug ( running )
# send managerState
msg = messaging . new_message ( ' managerState ' )
msg . managerState . processes = [ p . get_process_state_msg ( ) for p in managed_processes . values ( ) ]
pm . send ( ' managerState ' , msg )
# Exit main loop when uninstall/shutdown/reboot is needed
shutdown = False
for param in ( " DoUninstall " , " DoShutdown " , " DoReboot " ) :
if params . get_bool ( param ) :
cloudlog . warning ( f " Shutting down manager - { param } set " )
shutdown = True
if shutdown :
break
def main ( ) :
prepare_only = os . getenv ( " PREPAREONLY " ) is not None
manager_init ( )
# Start UI early so prepare can happen in the background
if not prepare_only :
managed_processes [ ' ui ' ] . start ( )
manager_prepare ( )
if prepare_only :
return
# SystemExit on sigterm
signal . signal ( signal . SIGTERM , lambda signum , frame : sys . exit ( 1 ) )
try :
manager_thread ( )
except Exception :
traceback . print_exc ( )
crash . capture_exception ( )
finally :
manager_cleanup ( )
params = Params ( )
if params . get_bool ( " DoUninstall " ) :
cloudlog . warning ( " uninstalling " )
HARDWARE . uninstall ( )
elif params . get_bool ( " DoReboot " ) :
cloudlog . warning ( " reboot " )
HARDWARE . reboot ( )
elif params . get_bool ( " DoShutdown " ) :
cloudlog . warning ( " shutdown " )
HARDWARE . shutdown ( )
if __name__ == " __main__ " :
unblock_stdout ( )
try :
main ( )
except Exception :
add_file_handler ( cloudlog )
cloudlog . exception ( " Manager failed to start " )
# Show last 3 lines of traceback
error = traceback . format_exc ( - 3 )
error = " Manager failed to start \n \n " + error
with TextWindow ( error ) as t :
t . wait_for_exit ( )
raise
# manual exit because we are forked
sys . exit ( 0 )