#!/usr/bin/env python3
import datetime
import os
import signal
import sys
import traceback
from cereal import log
import cereal . messaging as messaging
import openpilot . system . sentry as sentry
from openpilot . common . params import Params , ParamKeyType
from openpilot . common . text_window import TextWindow
from openpilot . system . hardware import HARDWARE , PC
from openpilot . selfdrive . manager . helpers import unblock_stdout , write_onroad_params , save_bootlog
from openpilot . selfdrive . manager . process import ensure_running
from openpilot . selfdrive . manager . process_config import managed_processes
from openpilot . system . athena . registration import register , UNREGISTERED_DONGLE_ID
from openpilot . common . swaglog import cloudlog , add_file_handler
from openpilot . system . version import get_build_metadata , terms_version , training_version
def manager_init ( ) - > None :
save_bootlog ( )
build_metadata = get_build_metadata ( )
params = Params ( )
params . clear_all ( ParamKeyType . CLEAR_ON_MANAGER_START )
params . clear_all ( ParamKeyType . CLEAR_ON_ONROAD_TRANSITION )
params . clear_all ( ParamKeyType . CLEAR_ON_OFFROAD_TRANSITION )
if build_metadata . release_channel :
params . clear_all ( ParamKeyType . DEVELOPMENT_ONLY )
default_params : list [ tuple [ str , str | bytes ] ] = [
( " CompletedTrainingVersion " , " 0 " ) ,
( " DisengageOnAccelerator " , " 0 " ) ,
( " GsmMetered " , " 1 " ) ,
( " HasAcceptedTerms " , " 0 " ) ,
( " LanguageSetting " , " main_en " ) ,
( " OpenpilotEnabledToggle " , " 1 " ) ,
( " LongitudinalPersonality " , str ( log . LongitudinalPersonality . standard ) ) ,
]
if not PC :
default_params . append ( ( " LastUpdateTime " , datetime . datetime . utcnow ( ) . isoformat ( ) . encode ( ' utf8 ' ) ) )
if params . get_bool ( " RecordFrontLock " ) :
params . put_bool ( " RecordFront " , True )
# set unset params
for k , v in default_params :
if params . get ( k ) is None :
params . put ( k , v )
# 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 " , build_metadata . openpilot . version )
params . put ( " TermsVersion " , terms_version )
params . put ( " TrainingVersion " , training_version )
params . put ( " GitCommit " , build_metadata . openpilot . git_commit )
params . put ( " GitCommitDate " , build_metadata . openpilot . git_commit_date )
params . put ( " GitBranch " , build_metadata . channel )
params . put ( " GitRemote " , build_metadata . openpilot . git_origin )
params . put_bool ( " IsTestedBranch " , build_metadata . tested_channel )
params . put_bool ( " IsReleaseBranch " , build_metadata . release_channel )
# 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
os . environ [ ' GIT_ORIGIN ' ] = build_metadata . openpilot . git_normalized_origin # Needed for swaglog
os . environ [ ' GIT_BRANCH ' ] = build_metadata . channel # Needed for swaglog
os . environ [ ' GIT_COMMIT ' ] = build_metadata . openpilot . git_commit # Needed for swaglog
if not build_metadata . openpilot . is_dirty :
os . environ [ ' CLEAN ' ] = ' 1 '
# init logging
sentry . init ( sentry . SentryProject . SELFDRIVE )
cloudlog . bind_global ( dongle_id = dongle_id ,
version = build_metadata . openpilot . version ,
origin = build_metadata . openpilot . git_normalized_origin ,
branch = build_metadata . channel ,
commit = build_metadata . openpilot . git_commit ,
dirty = build_metadata . openpilot . is_dirty ,
device = HARDWARE . get_device_type ( ) )
# preimport all processes
for p in managed_processes . values ( ) :
p . prepare ( )
def manager_cleanup ( ) - > None :
# 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 ( ) - > None :
cloudlog . bind ( daemon = " manager " )
cloudlog . info ( " manager start " )
cloudlog . info ( { " environ " : os . environ } )
params = Params ( )
ignore : list [ str ] = [ ]
if params . get ( " DongleId " , encoding = ' utf8 ' ) in ( None , UNREGISTERED_DONGLE_ID ) :
ignore + = [ " manage_athenad " , " uploader " ]
if os . getenv ( " NOBOARD " ) is not None :
ignore . append ( " pandad " )
ignore + = [ x for x in os . getenv ( " BLOCK " , " " ) . split ( " , " ) if len ( x ) > 0 ]
sm = messaging . SubMaster ( [ ' deviceState ' , ' carParams ' ] , poll = ' deviceState ' )
pm = messaging . PubMaster ( [ ' managerState ' ] )
write_onroad_params ( False , params )
ensure_running ( managed_processes . values ( ) , False , params = params , CP = sm [ ' carParams ' ] , not_run = ignore )
started_prev = False
while True :
sm . update ( 1000 )
started = sm [ ' deviceState ' ] . started
if started and not started_prev :
params . clear_all ( ParamKeyType . CLEAR_ON_ONROAD_TRANSITION )
elif not started and started_prev :
params . clear_all ( ParamKeyType . CLEAR_ON_OFFROAD_TRANSITION )
# update onroad params, which drives boardd's safety setter thread
if started != started_prev :
write_onroad_params ( started , params )
started_prev = started
ensure_running ( managed_processes . values ( ) , started , params = params , CP = sm [ ' carParams ' ] , not_run = ignore )
running = ' ' . join ( " {} {} \u001b [0m " . format ( " \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 ' , valid = True )
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 ) :
shutdown = True
params . put ( " LastManagerExitReason " , f " { param } { datetime . datetime . now ( ) } " )
cloudlog . warning ( f " Shutting down manager - { param } set " )
if shutdown :
break
def main ( ) - > None :
manager_init ( )
if os . getenv ( " PREPAREONLY " ) is not None :
return
# SystemExit on sigterm
signal . signal ( signal . SIGTERM , lambda signum , frame : sys . exit ( 1 ) )
try :
manager_thread ( )
except Exception :
traceback . print_exc ( )
sentry . 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 KeyboardInterrupt :
print ( " got CTRL-C, exiting " )
except Exception :
add_file_handler ( cloudlog )
cloudlog . exception ( " Manager failed to start " )
try :
managed_processes [ ' ui ' ] . stop ( )
except Exception :
pass
# 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 )