You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							628 lines
						
					
					
						
							17 KiB
						
					
					
				
			
		
		
	
	
							628 lines
						
					
					
						
							17 KiB
						
					
					
				#!/usr/bin/env python3
 | 
						|
import os
 | 
						|
import time
 | 
						|
import sys
 | 
						|
import fcntl
 | 
						|
import errno
 | 
						|
import signal
 | 
						|
import shutil
 | 
						|
import subprocess
 | 
						|
import datetime
 | 
						|
import textwrap
 | 
						|
from typing import Dict, List
 | 
						|
from selfdrive.swaglog import cloudlog, add_logentries_handler
 | 
						|
 | 
						|
 | 
						|
from common.basedir import BASEDIR, PARAMS
 | 
						|
from common.android import ANDROID
 | 
						|
WEBCAM = os.getenv("WEBCAM") is not None
 | 
						|
sys.path.append(os.path.join(BASEDIR, "pyextra"))
 | 
						|
os.environ['BASEDIR'] = BASEDIR
 | 
						|
 | 
						|
TOTAL_SCONS_NODES = 1005
 | 
						|
prebuilt = os.path.exists(os.path.join(BASEDIR, 'prebuilt'))
 | 
						|
 | 
						|
# Create folders needed for msgq
 | 
						|
try:
 | 
						|
  os.mkdir("/dev/shm")
 | 
						|
except FileExistsError:
 | 
						|
  pass
 | 
						|
except PermissionError:
 | 
						|
  print("WARNING: failed to make /dev/shm")
 | 
						|
 | 
						|
if ANDROID:
 | 
						|
  os.chmod("/dev/shm", 0o777)
 | 
						|
 | 
						|
def unblock_stdout():
 | 
						|
  # get a non-blocking stdout
 | 
						|
  child_pid, child_pty = os.forkpty()
 | 
						|
  if child_pid != 0:  # parent
 | 
						|
 | 
						|
    # child is in its own process group, manually pass kill signals
 | 
						|
    signal.signal(signal.SIGINT, lambda signum, frame: os.kill(child_pid, signal.SIGINT))
 | 
						|
    signal.signal(signal.SIGTERM, lambda signum, frame: os.kill(child_pid, signal.SIGTERM))
 | 
						|
 | 
						|
    fcntl.fcntl(sys.stdout, fcntl.F_SETFL,
 | 
						|
       fcntl.fcntl(sys.stdout, fcntl.F_GETFL) | os.O_NONBLOCK)
 | 
						|
 | 
						|
    while True:
 | 
						|
      try:
 | 
						|
        dat = os.read(child_pty, 4096)
 | 
						|
      except OSError as e:
 | 
						|
        if e.errno == errno.EIO:
 | 
						|
          break
 | 
						|
        continue
 | 
						|
 | 
						|
      if not dat:
 | 
						|
        break
 | 
						|
 | 
						|
      try:
 | 
						|
        sys.stdout.write(dat.decode('utf8'))
 | 
						|
      except (OSError, IOError, UnicodeDecodeError):
 | 
						|
        pass
 | 
						|
 | 
						|
    # os.wait() returns a tuple with the pid and a 16 bit value
 | 
						|
    # whose low byte is the signal number and whose high byte is the exit satus
 | 
						|
    exit_status = os.wait()[1] >> 8
 | 
						|
    os._exit(exit_status)
 | 
						|
 | 
						|
 | 
						|
if __name__ == "__main__":
 | 
						|
  unblock_stdout()
 | 
						|
 | 
						|
from common.spinner import Spinner
 | 
						|
from common.text_window import TextWindow
 | 
						|
 | 
						|
import importlib
 | 
						|
import traceback
 | 
						|
from multiprocessing import Process
 | 
						|
 | 
						|
# Run scons
 | 
						|
spinner = Spinner(noop=(__name__ != "__main__" or not ANDROID))
 | 
						|
spinner.update("0")
 | 
						|
 | 
						|
if not prebuilt:
 | 
						|
  for retry in [True, False]:
 | 
						|
    # run scons
 | 
						|
    env = os.environ.copy()
 | 
						|
    env['SCONS_PROGRESS'] = "1"
 | 
						|
    env['SCONS_CACHE'] = "1"
 | 
						|
 | 
						|
    nproc = os.cpu_count()
 | 
						|
    j_flag = "" if nproc is None else "-j%d" % (nproc - 1)
 | 
						|
    scons = subprocess.Popen(["scons", j_flag], cwd=BASEDIR, env=env, stderr=subprocess.PIPE)
 | 
						|
 | 
						|
    compile_output = []
 | 
						|
 | 
						|
    # Read progress from stderr and update spinner
 | 
						|
    while scons.poll() is None:
 | 
						|
      try:
 | 
						|
        line = scons.stderr.readline()  # type: ignore
 | 
						|
        if line is None:
 | 
						|
          continue
 | 
						|
        line = line.rstrip()
 | 
						|
 | 
						|
        prefix = b'progress: '
 | 
						|
        if line.startswith(prefix):
 | 
						|
          i = int(line[len(prefix):])
 | 
						|
          if spinner is not None:
 | 
						|
            spinner.update("%d" % (70.0 * (i / TOTAL_SCONS_NODES)))
 | 
						|
        elif len(line):
 | 
						|
          compile_output.append(line)
 | 
						|
          print(line.decode('utf8', 'replace'))
 | 
						|
      except Exception:
 | 
						|
        pass
 | 
						|
 | 
						|
    if scons.returncode != 0:
 | 
						|
      # Read remaining output
 | 
						|
      r = scons.stderr.read().split(b'\n')   # type: ignore
 | 
						|
      compile_output += r
 | 
						|
 | 
						|
      if retry:
 | 
						|
        if not os.getenv("CI"):
 | 
						|
          print("scons build failed, cleaning in")
 | 
						|
          for i in range(3, -1, -1):
 | 
						|
            print("....%d" % i)
 | 
						|
            time.sleep(1)
 | 
						|
          subprocess.check_call(["scons", "-c"], cwd=BASEDIR, env=env)
 | 
						|
          shutil.rmtree("/tmp/scons_cache")
 | 
						|
        else:
 | 
						|
          print("scons build failed after retry")
 | 
						|
          sys.exit(1)
 | 
						|
      else:
 | 
						|
        # Build failed log errors
 | 
						|
        errors = [line.decode('utf8', 'replace') for line in compile_output
 | 
						|
                  if any([err in line for err in [b'error: ', b'not found, needed by target']])]
 | 
						|
        error_s = "\n".join(errors)
 | 
						|
        add_logentries_handler(cloudlog)
 | 
						|
        cloudlog.error("scons build failed\n" + error_s)
 | 
						|
 | 
						|
        # Show TextWindow
 | 
						|
        no_ui = __name__ != "__main__" or not ANDROID
 | 
						|
        error_s = "\n \n".join(["\n".join(textwrap.wrap(e, 65)) for e in errors])
 | 
						|
        with TextWindow("openpilot failed to build\n \n" + error_s, noop=no_ui) as t:
 | 
						|
          t.wait_for_exit()
 | 
						|
 | 
						|
        exit(1)
 | 
						|
    else:
 | 
						|
      break
 | 
						|
 | 
						|
import cereal
 | 
						|
import cereal.messaging as messaging
 | 
						|
 | 
						|
from common.params import Params
 | 
						|
import selfdrive.crash as crash
 | 
						|
from selfdrive.registration import register
 | 
						|
from selfdrive.version import version, dirty
 | 
						|
from selfdrive.loggerd.config import ROOT
 | 
						|
from selfdrive.launcher import launcher
 | 
						|
from common import android
 | 
						|
from common.apk import update_apks, pm_apply_packages, start_offroad
 | 
						|
 | 
						|
ThermalStatus = cereal.log.ThermalData.ThermalStatus
 | 
						|
 | 
						|
# comment out anything you don't want to run
 | 
						|
managed_processes = {
 | 
						|
  "thermald": "selfdrive.thermald.thermald",
 | 
						|
  "uploader": "selfdrive.loggerd.uploader",
 | 
						|
  "deleter": "selfdrive.loggerd.deleter",
 | 
						|
  "controlsd": "selfdrive.controls.controlsd",
 | 
						|
  "plannerd": "selfdrive.controls.plannerd",
 | 
						|
  "radard": "selfdrive.controls.radard",
 | 
						|
  "dmonitoringd": "selfdrive.monitoring.dmonitoringd",
 | 
						|
  "ubloxd": ("selfdrive/locationd", ["./ubloxd"]),
 | 
						|
  "loggerd": ("selfdrive/loggerd", ["./loggerd"]),
 | 
						|
  "logmessaged": "selfdrive.logmessaged",
 | 
						|
  "locationd": "selfdrive.locationd.locationd",
 | 
						|
  "tombstoned": "selfdrive.tombstoned",
 | 
						|
  "logcatd": ("selfdrive/logcatd", ["./logcatd"]),
 | 
						|
  "proclogd": ("selfdrive/proclogd", ["./proclogd"]),
 | 
						|
  "boardd": ("selfdrive/boardd", ["./boardd"]),   # not used directly
 | 
						|
  "pandad": "selfdrive.pandad",
 | 
						|
  "ui": ("selfdrive/ui", ["./ui"]),
 | 
						|
  "calibrationd": "selfdrive.locationd.calibrationd",
 | 
						|
  "paramsd": "selfdrive.locationd.paramsd",
 | 
						|
  "camerad": ("selfdrive/camerad", ["./camerad"]),
 | 
						|
  "sensord": ("selfdrive/sensord", ["./sensord"]),
 | 
						|
  "clocksd": ("selfdrive/clocksd", ["./clocksd"]),
 | 
						|
  "gpsd": ("selfdrive/sensord", ["./gpsd"]),
 | 
						|
  "updated": "selfdrive.updated",
 | 
						|
  "dmonitoringmodeld": ("selfdrive/modeld", ["./dmonitoringmodeld"]),
 | 
						|
  "modeld": ("selfdrive/modeld", ["./modeld"]),
 | 
						|
}
 | 
						|
 | 
						|
daemon_processes = {
 | 
						|
  "manage_athenad": ("selfdrive.athena.manage_athenad", "AthenadPid"),
 | 
						|
}
 | 
						|
 | 
						|
running: Dict[str, Process] = {}
 | 
						|
def get_running():
 | 
						|
  return running
 | 
						|
 | 
						|
# due to qualcomm kernel bugs SIGKILLing camerad sometimes causes page table corruption
 | 
						|
unkillable_processes = ['camerad']
 | 
						|
 | 
						|
# processes to end with SIGINT instead of SIGTERM
 | 
						|
interrupt_processes: List[str] = []
 | 
						|
 | 
						|
# processes to end with SIGKILL instead of SIGTERM
 | 
						|
kill_processes = ['sensord']
 | 
						|
 | 
						|
# processes to end if thermal conditions exceed Green parameters
 | 
						|
green_temp_processes = ['uploader']
 | 
						|
 | 
						|
persistent_processes = [
 | 
						|
  'thermald',
 | 
						|
  'logmessaged',
 | 
						|
  'ui',
 | 
						|
  'uploader',
 | 
						|
]
 | 
						|
 | 
						|
if ANDROID:
 | 
						|
  persistent_processes += [
 | 
						|
    'logcatd',
 | 
						|
    'tombstoned',
 | 
						|
    'updated',
 | 
						|
    'deleter',
 | 
						|
  ]
 | 
						|
 | 
						|
car_started_processes = [
 | 
						|
  'controlsd',
 | 
						|
  'plannerd',
 | 
						|
  'loggerd',
 | 
						|
  'radard',
 | 
						|
  'dmonitoringd',
 | 
						|
  'calibrationd',
 | 
						|
  'paramsd',
 | 
						|
  'camerad',
 | 
						|
  'modeld',
 | 
						|
  'proclogd',
 | 
						|
  'ubloxd',
 | 
						|
  'locationd',
 | 
						|
]
 | 
						|
 | 
						|
driver_view_processes = [
 | 
						|
  'camerad',
 | 
						|
  'dmonitoringd',
 | 
						|
  'dmonitoringmodeld'
 | 
						|
]
 | 
						|
 | 
						|
if WEBCAM:
 | 
						|
  car_started_processes += [
 | 
						|
    'dmonitoringmodeld',
 | 
						|
  ]
 | 
						|
 | 
						|
if ANDROID:
 | 
						|
  car_started_processes += [
 | 
						|
    'sensord',
 | 
						|
    'clocksd',
 | 
						|
    'gpsd',
 | 
						|
    'dmonitoringmodeld',
 | 
						|
  ]
 | 
						|
 | 
						|
def register_managed_process(name, desc, car_started=False):
 | 
						|
  global managed_processes, car_started_processes, persistent_processes
 | 
						|
  print("registering %s" % name)
 | 
						|
  managed_processes[name] = desc
 | 
						|
  if car_started:
 | 
						|
    car_started_processes.append(name)
 | 
						|
  else:
 | 
						|
    persistent_processes.append(name)
 | 
						|
 | 
						|
# ****************** process management functions ******************
 | 
						|
def nativelauncher(pargs, cwd):
 | 
						|
  # exec the process
 | 
						|
  os.chdir(cwd)
 | 
						|
 | 
						|
  # because when extracted from pex zips permissions get lost -_-
 | 
						|
  os.chmod(pargs[0], 0o700)
 | 
						|
 | 
						|
  os.execvp(pargs[0], pargs)
 | 
						|
 | 
						|
def start_managed_process(name):
 | 
						|
  if name in running or name not in managed_processes:
 | 
						|
    return
 | 
						|
  proc = managed_processes[name]
 | 
						|
  if isinstance(proc, str):
 | 
						|
    cloudlog.info("starting python %s" % proc)
 | 
						|
    running[name] = Process(name=name, target=launcher, args=(proc,))
 | 
						|
  else:
 | 
						|
    pdir, pargs = proc
 | 
						|
    cwd = os.path.join(BASEDIR, pdir)
 | 
						|
    cloudlog.info("starting process %s" % name)
 | 
						|
    running[name] = Process(name=name, target=nativelauncher, args=(pargs, cwd))
 | 
						|
  running[name].start()
 | 
						|
 | 
						|
def start_daemon_process(name):
 | 
						|
  params = Params()
 | 
						|
  proc, pid_param = daemon_processes[name]
 | 
						|
  pid = params.get(pid_param, encoding='utf-8')
 | 
						|
 | 
						|
  if pid is not None:
 | 
						|
    try:
 | 
						|
      os.kill(int(pid), 0)
 | 
						|
      with open(f'/proc/{pid}/cmdline') as f:
 | 
						|
        if proc in f.read():
 | 
						|
          # daemon is running
 | 
						|
          return
 | 
						|
    except (OSError, FileNotFoundError):
 | 
						|
      # process is dead
 | 
						|
      pass
 | 
						|
 | 
						|
  cloudlog.info("starting daemon %s" % name)
 | 
						|
  proc = subprocess.Popen(['python', '-m', proc],  # pylint: disable=subprocess-popen-preexec-fn
 | 
						|
                          stdin=open('/dev/null', 'r'),
 | 
						|
                          stdout=open('/dev/null', 'w'),
 | 
						|
                          stderr=open('/dev/null', 'w'),
 | 
						|
                          preexec_fn=os.setpgrp)
 | 
						|
 | 
						|
  params.put(pid_param, str(proc.pid))
 | 
						|
 | 
						|
def prepare_managed_process(p):
 | 
						|
  proc = managed_processes[p]
 | 
						|
  if isinstance(proc, str):
 | 
						|
    # import this python
 | 
						|
    cloudlog.info("preimporting %s" % proc)
 | 
						|
    importlib.import_module(proc)
 | 
						|
  elif os.path.isfile(os.path.join(BASEDIR, proc[0], "Makefile")):
 | 
						|
    # build this process
 | 
						|
    cloudlog.info("building %s" % (proc,))
 | 
						|
    try:
 | 
						|
      subprocess.check_call(["make", "-j4"], cwd=os.path.join(BASEDIR, proc[0]))
 | 
						|
    except subprocess.CalledProcessError:
 | 
						|
      # make clean if the build failed
 | 
						|
      cloudlog.warning("building %s failed, make clean" % (proc, ))
 | 
						|
      subprocess.check_call(["make", "clean"], cwd=os.path.join(BASEDIR, proc[0]))
 | 
						|
      subprocess.check_call(["make", "-j4"], cwd=os.path.join(BASEDIR, proc[0]))
 | 
						|
 | 
						|
 | 
						|
def join_process(process, timeout):
 | 
						|
  # Process().join(timeout) will hang due to a python 3 bug: https://bugs.python.org/issue28382
 | 
						|
  # We have to poll the exitcode instead
 | 
						|
  t = time.time()
 | 
						|
  while time.time() - t < timeout and process.exitcode is None:
 | 
						|
    time.sleep(0.001)
 | 
						|
 | 
						|
 | 
						|
def kill_managed_process(name):
 | 
						|
  if name not in running or name not in managed_processes:
 | 
						|
    return
 | 
						|
  cloudlog.info("killing %s" % name)
 | 
						|
 | 
						|
  if running[name].exitcode is None:
 | 
						|
    if name in interrupt_processes:
 | 
						|
      os.kill(running[name].pid, signal.SIGINT)
 | 
						|
    elif name in kill_processes:
 | 
						|
      os.kill(running[name].pid, signal.SIGKILL)
 | 
						|
    else:
 | 
						|
      running[name].terminate()
 | 
						|
 | 
						|
    join_process(running[name], 5)
 | 
						|
 | 
						|
    if running[name].exitcode is None:
 | 
						|
      if name in unkillable_processes:
 | 
						|
        cloudlog.critical("unkillable process %s failed to exit! rebooting in 15 if it doesn't die" % name)
 | 
						|
        join_process(running[name], 15)
 | 
						|
        if running[name].exitcode is None:
 | 
						|
          cloudlog.critical("unkillable process %s failed to die!" % name)
 | 
						|
          if ANDROID:
 | 
						|
            cloudlog.critical("FORCE REBOOTING PHONE!")
 | 
						|
            os.system("date >> /sdcard/unkillable_reboot")
 | 
						|
            os.system("reboot")
 | 
						|
          raise RuntimeError
 | 
						|
      else:
 | 
						|
        cloudlog.info("killing %s with SIGKILL" % name)
 | 
						|
        os.kill(running[name].pid, signal.SIGKILL)
 | 
						|
        running[name].join()
 | 
						|
 | 
						|
  cloudlog.info("%s is dead with %d" % (name, running[name].exitcode))
 | 
						|
  del running[name]
 | 
						|
 | 
						|
 | 
						|
def cleanup_all_processes(signal, frame):
 | 
						|
  cloudlog.info("caught ctrl-c %s %s" % (signal, frame))
 | 
						|
 | 
						|
  if ANDROID:
 | 
						|
    pm_apply_packages('disable')
 | 
						|
 | 
						|
  for name in list(running.keys()):
 | 
						|
    kill_managed_process(name)
 | 
						|
  cloudlog.info("everything is dead")
 | 
						|
 | 
						|
 | 
						|
def send_managed_process_signal(name, sig):
 | 
						|
  if name not in running or name not in managed_processes:
 | 
						|
    return
 | 
						|
  cloudlog.info(f"sending signal {sig} to {name}")
 | 
						|
  os.kill(running[name].pid, sig)
 | 
						|
 | 
						|
 | 
						|
# ****************** run loop ******************
 | 
						|
 | 
						|
def manager_init(should_register=True):
 | 
						|
  if should_register:
 | 
						|
    reg_res = register()
 | 
						|
    if reg_res:
 | 
						|
      dongle_id = reg_res
 | 
						|
    else:
 | 
						|
      raise Exception("server registration failed")
 | 
						|
  else:
 | 
						|
    dongle_id = "c"*16
 | 
						|
 | 
						|
  # set dongle id
 | 
						|
  cloudlog.info("dongle id is " + dongle_id)
 | 
						|
  os.environ['DONGLE_ID'] = dongle_id
 | 
						|
 | 
						|
  cloudlog.info("dirty is %d" % dirty)
 | 
						|
  if not dirty:
 | 
						|
    os.environ['CLEAN'] = '1'
 | 
						|
 | 
						|
  cloudlog.bind_global(dongle_id=dongle_id, version=version, dirty=dirty, is_eon=True)
 | 
						|
  crash.bind_user(id=dongle_id)
 | 
						|
  crash.bind_extra(version=version, dirty=dirty, is_eon=True)
 | 
						|
 | 
						|
  os.umask(0)
 | 
						|
  try:
 | 
						|
    os.mkdir(ROOT, 0o777)
 | 
						|
  except OSError:
 | 
						|
    pass
 | 
						|
 | 
						|
  # ensure shared libraries are readable by apks
 | 
						|
  if ANDROID:
 | 
						|
    os.chmod(BASEDIR, 0o755)
 | 
						|
    os.chmod(os.path.join(BASEDIR, "cereal"), 0o755)
 | 
						|
    os.chmod(os.path.join(BASEDIR, "cereal", "libmessaging_shared.so"), 0o755)
 | 
						|
 | 
						|
def manager_thread():
 | 
						|
  # now loop
 | 
						|
  thermal_sock = messaging.sub_sock('thermal')
 | 
						|
 | 
						|
  cloudlog.info("manager start")
 | 
						|
  cloudlog.info({"environ": os.environ})
 | 
						|
 | 
						|
  # save boot log
 | 
						|
  subprocess.call(["./loggerd", "--bootlog"], cwd=os.path.join(BASEDIR, "selfdrive/loggerd"))
 | 
						|
 | 
						|
  params = Params()
 | 
						|
 | 
						|
  # start daemon processes
 | 
						|
  for p in daemon_processes:
 | 
						|
    start_daemon_process(p)
 | 
						|
 | 
						|
  # start persistent processes
 | 
						|
  for p in persistent_processes:
 | 
						|
    start_managed_process(p)
 | 
						|
 | 
						|
  # start offroad
 | 
						|
  if ANDROID:
 | 
						|
    pm_apply_packages('enable')
 | 
						|
    start_offroad()
 | 
						|
 | 
						|
  if os.getenv("NOBOARD") is None:
 | 
						|
    start_managed_process("pandad")
 | 
						|
 | 
						|
  if os.getenv("BLOCK") is not None:
 | 
						|
    for k in os.getenv("BLOCK").split(","):
 | 
						|
      del managed_processes[k]
 | 
						|
 | 
						|
  started_prev = False
 | 
						|
  logger_dead = False
 | 
						|
 | 
						|
  while 1:
 | 
						|
    msg = messaging.recv_sock(thermal_sock, wait=True)
 | 
						|
 | 
						|
    # heavyweight batch processes are gated on favorable thermal conditions
 | 
						|
    if msg.thermal.thermalStatus >= ThermalStatus.yellow:
 | 
						|
      for p in green_temp_processes:
 | 
						|
        if p in persistent_processes:
 | 
						|
          kill_managed_process(p)
 | 
						|
    else:
 | 
						|
      for p in green_temp_processes:
 | 
						|
        if p in persistent_processes:
 | 
						|
          start_managed_process(p)
 | 
						|
 | 
						|
    if msg.thermal.freeSpace < 0.05:
 | 
						|
      logger_dead = True
 | 
						|
 | 
						|
    if msg.thermal.started:
 | 
						|
      for p in car_started_processes:
 | 
						|
        if p == "loggerd" and logger_dead:
 | 
						|
          kill_managed_process(p)
 | 
						|
        else:
 | 
						|
          start_managed_process(p)
 | 
						|
    else:
 | 
						|
      logger_dead = False
 | 
						|
      driver_view = params.get("IsDriverViewEnabled") == b"1"
 | 
						|
 | 
						|
      # TODO: refactor how manager manages processes
 | 
						|
      for p in reversed(car_started_processes):
 | 
						|
        if p not in driver_view_processes or not driver_view:
 | 
						|
          kill_managed_process(p)
 | 
						|
 | 
						|
      for p in driver_view_processes:
 | 
						|
        if driver_view:
 | 
						|
          start_managed_process(p)
 | 
						|
        else:
 | 
						|
          kill_managed_process(p)
 | 
						|
 | 
						|
      # trigger an update after going offroad
 | 
						|
      if started_prev:
 | 
						|
        send_managed_process_signal("updated", signal.SIGHUP)
 | 
						|
 | 
						|
    started_prev = msg.thermal.started
 | 
						|
 | 
						|
    # check the status of all processes, did any of them die?
 | 
						|
    running_list = ["%s%s\u001b[0m" % ("\u001b[32m" if running[p].is_alive() else "\u001b[31m", p) for p in running]
 | 
						|
    cloudlog.debug(' '.join(running_list))
 | 
						|
 | 
						|
    # Exit main loop when uninstall is needed
 | 
						|
    if params.get("DoUninstall", encoding='utf8') == "1":
 | 
						|
      break
 | 
						|
 | 
						|
def manager_prepare(spinner=None):
 | 
						|
  # build all processes
 | 
						|
  os.chdir(os.path.dirname(os.path.abspath(__file__)))
 | 
						|
 | 
						|
  # Spinner has to start from 70 here
 | 
						|
  total = 100.0 if prebuilt else 30.0
 | 
						|
 | 
						|
  for i, p in enumerate(managed_processes):
 | 
						|
    if spinner is not None:
 | 
						|
      spinner.update("%d" % ((100.0 - total) + total * (i + 1) / len(managed_processes),))
 | 
						|
    prepare_managed_process(p)
 | 
						|
 | 
						|
def uninstall():
 | 
						|
  cloudlog.warning("uninstalling")
 | 
						|
  with open('/cache/recovery/command', 'w') as f:
 | 
						|
    f.write('--wipe_data\n')
 | 
						|
  # IPowerManager.reboot(confirm=false, reason="recovery", wait=true)
 | 
						|
  android.reboot(reason="recovery")
 | 
						|
 | 
						|
def main():
 | 
						|
  os.environ['PARAMS_PATH'] = PARAMS
 | 
						|
 | 
						|
  if ANDROID:
 | 
						|
    # the flippening!
 | 
						|
    os.system('LD_LIBRARY_PATH="" content insert --uri content://settings/system --bind name:s:user_rotation --bind value:i:1')
 | 
						|
 | 
						|
    # disable bluetooth
 | 
						|
    os.system('service call bluetooth_manager 8')
 | 
						|
 | 
						|
  params = Params()
 | 
						|
  params.manager_start()
 | 
						|
 | 
						|
  default_params = [
 | 
						|
    ("CommunityFeaturesToggle", "0"),
 | 
						|
    ("CompletedTrainingVersion", "0"),
 | 
						|
    ("IsRHD", "0"),
 | 
						|
    ("IsMetric", "0"),
 | 
						|
    ("RecordFront", "0"),
 | 
						|
    ("HasAcceptedTerms", "0"),
 | 
						|
    ("HasCompletedSetup", "0"),
 | 
						|
    ("IsUploadRawEnabled", "1"),
 | 
						|
    ("IsLdwEnabled", "1"),
 | 
						|
    ("IsGeofenceEnabled", "-1"),
 | 
						|
    ("SpeedLimitOffset", "0"),
 | 
						|
    ("LongitudinalControl", "0"),
 | 
						|
    ("LimitSetSpeed", "0"),
 | 
						|
    ("LimitSetSpeedNeural", "0"),
 | 
						|
    ("LastUpdateTime", datetime.datetime.utcnow().isoformat().encode('utf8')),
 | 
						|
    ("OpenpilotEnabledToggle", "1"),
 | 
						|
    ("LaneChangeEnabled", "1"),
 | 
						|
    ("IsDriverViewEnabled", "0"),
 | 
						|
  ]
 | 
						|
 | 
						|
  # set unset params
 | 
						|
  for k, v in default_params:
 | 
						|
    if params.get(k) is None:
 | 
						|
      params.put(k, v)
 | 
						|
 | 
						|
  # is this chffrplus?
 | 
						|
  if os.getenv("PASSIVE") is not None:
 | 
						|
    params.put("Passive", str(int(os.getenv("PASSIVE"))))
 | 
						|
 | 
						|
  if params.get("Passive") is None:
 | 
						|
    raise Exception("Passive must be set to continue")
 | 
						|
 | 
						|
  if ANDROID:
 | 
						|
    update_apks()
 | 
						|
  manager_init()
 | 
						|
  manager_prepare(spinner)
 | 
						|
  spinner.close()
 | 
						|
 | 
						|
  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()
 | 
						|
    crash.capture_exception()
 | 
						|
  finally:
 | 
						|
    cleanup_all_processes(None, None)
 | 
						|
 | 
						|
  if params.get("DoUninstall", encoding='utf8') == "1":
 | 
						|
    uninstall()
 | 
						|
 | 
						|
 | 
						|
if __name__ == "__main__":
 | 
						|
  try:
 | 
						|
    main()
 | 
						|
  except Exception:
 | 
						|
    add_logentries_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)
 | 
						|
 |