manager cleanup (#2634)

* start cleanup

* add uninstall to hw abstraction layer

* cleanup

* litte more

* don't build at import time

* fix tests

* don't build by default

* sync
pull/2721/head
Adeeb Shihadeh 4 years ago committed by GitHub
parent bc1cfa6d4f
commit 220853730d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      Jenkinsfile
  2. 12
      selfdrive/hardware/base.py
  3. 6
      selfdrive/hardware/eon/hardware.py
  4. 9
      selfdrive/hardware/pc/hardware.py
  5. 10
      selfdrive/hardware/tici/hardware.py
  6. 121
      selfdrive/manager.py

4
Jenkinsfile vendored

@ -124,7 +124,7 @@ pipeline {
stage('Replay Tests') { stage('Replay Tests') {
steps { steps {
phone_steps("eon2", [ phone_steps("eon2", [
["camerad/modeld replay", "cd selfdrive/test/process_replay && ./camera_replay.py"], ["camerad/modeld replay", "QCOM_REPLAY=1 scons -j4 && cd selfdrive/test/process_replay && ./camera_replay.py"],
]) ])
} }
} }
@ -132,7 +132,7 @@ pipeline {
stage('HW + Unit Tests') { stage('HW + Unit Tests') {
steps { steps {
phone_steps("eon", [ phone_steps("eon", [
["build cereal", "SCONS_CACHE=1 scons -j4 cereal/"], ["build", "SCONS_CACHE=1 scons -j4"],
["test sounds", "nosetests -s selfdrive/test/test_sounds.py"], ["test sounds", "nosetests -s selfdrive/test/test_sounds.py"],
["test boardd loopback", "nosetests -s selfdrive/boardd/tests/test_boardd_loopback.py"], ["test boardd loopback", "nosetests -s selfdrive/boardd/tests/test_boardd_loopback.py"],
["test loggerd", "CI=1 python selfdrive/loggerd/tests/test_loggerd.py"], ["test loggerd", "CI=1 python selfdrive/loggerd/tests/test_loggerd.py"],

@ -16,6 +16,14 @@ class HardwareBase:
except Exception: except Exception:
return default return default
@abstractmethod
def reboot(self, reason=None):
pass
@abstractmethod
def uninstall(self):
pass
@abstractmethod @abstractmethod
def get_sound_card_online(self): def get_sound_card_online(self):
pass pass
@ -32,10 +40,6 @@ class HardwareBase:
def get_subscriber_info(self): def get_subscriber_info(self):
pass pass
@abstractmethod
def reboot(self, reason=None):
pass
@abstractmethod @abstractmethod
def get_network_type(self): def get_network_type(self):
pass pass

@ -98,6 +98,12 @@ class Android(HardwareBase):
"i32", "1" # wait "i32", "1" # wait
]) ])
def uninstall(self):
with open('/cache/recovery/command', 'w') as f:
f.write('--wipe_data\n')
# IPowerManager.reboot(confirm=false, reason="recovery", wait=true)
self.reboot(reason="recovery")
def get_sim_info(self): def get_sim_info(self):
# Used for athena # Used for athena
# TODO: build using methods from this class # TODO: build using methods from this class

@ -11,6 +11,12 @@ class Pc(HardwareBase):
def get_sound_card_online(self): def get_sound_card_online(self):
return True return True
def reboot(self, reason=None):
print("REBOOT!")
def uninstall(self):
print("uninstall")
def get_imei(self, slot): def get_imei(self, slot):
return "%015d" % random.randint(0, 1 << 32) return "%015d" % random.randint(0, 1 << 32)
@ -20,9 +26,6 @@ class Pc(HardwareBase):
def get_subscriber_info(self): def get_subscriber_info(self):
return "" return ""
def reboot(self, reason=None):
print("REBOOT!")
def get_network_type(self): def get_network_type(self):
return NetworkType.wifi return NetworkType.wifi

@ -35,12 +35,16 @@ class Tici(HardwareBase):
def get_sound_card_online(self): def get_sound_card_online(self):
return True return True
def get_serial(self):
return self.get_cmdline()['androidboot.serialno']
def reboot(self, reason=None): def reboot(self, reason=None):
subprocess.check_output(["sudo", "reboot"]) subprocess.check_output(["sudo", "reboot"])
def uninstall(self):
# TODO: implement uninstall. reboot to factory reset?
pass
def get_serial(self):
return self.get_cmdline()['androidboot.serialno']
def get_network_type(self): def get_network_type(self):
primary_connection = self.nm.Get(NM, 'PrimaryConnection', dbus_interface=DBUS_PROPS) primary_connection = self.nm.Get(NM, 'PrimaryConnection', dbus_interface=DBUS_PROPS)
primary_connection = self.bus.get_object(NM, primary_connection) primary_connection = self.bus.get_object(NM, primary_connection)

@ -1,37 +1,33 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import datetime
import importlib
import os import os
import time
import sys import sys
import fcntl import fcntl
import errno import errno
import signal import signal
import shutil import shutil
import subprocess import subprocess
import datetime
import textwrap import textwrap
from typing import Dict, List import time
from selfdrive.swaglog import cloudlog, add_logentries_handler import traceback
from multiprocessing import Process
from typing import Dict, List
from common.basedir import BASEDIR from common.basedir import BASEDIR
from common.spinner import Spinner
from common.text_window import TextWindow
from selfdrive.hardware import HARDWARE, EON, PC from selfdrive.hardware import HARDWARE, EON, PC
WEBCAM = os.getenv("WEBCAM") is not None from selfdrive.swaglog import cloudlog, add_logentries_handler
sys.path.append(os.path.join(BASEDIR, "pyextra"))
os.environ['BASEDIR'] = BASEDIR os.environ['BASEDIR'] = BASEDIR
sys.path.append(os.path.join(BASEDIR, "pyextra"))
TOTAL_SCONS_NODES = 1040 TOTAL_SCONS_NODES = 1040
prebuilt = os.path.exists(os.path.join(BASEDIR, 'prebuilt')) WEBCAM = os.getenv("WEBCAM") is not None
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 EON:
os.chmod("/dev/shm", 0o777)
def unblock_stdout(): def unblock_stdout():
# get a non-blocking stdout # get a non-blocking stdout
@ -42,8 +38,7 @@ def unblock_stdout():
signal.signal(signal.SIGINT, lambda signum, frame: os.kill(child_pid, signal.SIGINT)) 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)) 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_SETFL, fcntl.fcntl(sys.stdout, fcntl.F_GETFL) | os.O_NONBLOCK)
fcntl.fcntl(sys.stdout, fcntl.F_GETFL) | os.O_NONBLOCK)
while True: while True:
try: try:
@ -70,12 +65,6 @@ def unblock_stdout():
if __name__ == "__main__": if __name__ == "__main__":
unblock_stdout() unblock_stdout()
from common.spinner import Spinner
from common.text_window import TextWindow
import importlib
import traceback
from multiprocessing import Process
# Run scons # Run scons
spinner = Spinner() spinner = Spinner()
@ -83,7 +72,7 @@ spinner.update("0")
if __name__ != "__main__": if __name__ != "__main__":
spinner.close() spinner.close()
if not prebuilt: def build():
for retry in [True, False]: for retry in [True, False]:
# run scons # run scons
env = os.environ.copy() env = os.environ.copy()
@ -91,7 +80,7 @@ if not prebuilt:
env['SCONS_CACHE'] = "1" env['SCONS_CACHE'] = "1"
nproc = os.cpu_count() nproc = os.cpu_count()
j_flag = "" if nproc is None else "-j%d" % (nproc - 1) j_flag = "" if nproc is None else f"-j{nproc - 1}"
scons = subprocess.Popen(["scons", j_flag], cwd=BASEDIR, env=env, stderr=subprocess.PIPE) scons = subprocess.Popen(["scons", j_flag], cwd=BASEDIR, env=env, stderr=subprocess.PIPE)
compile_output = [] compile_output = []
@ -148,7 +137,9 @@ if not prebuilt:
else: else:
break break
import cereal if __name__ == "__main__" and not PREBUILT:
build()
import cereal.messaging as messaging import cereal.messaging as messaging
from common.params import Params from common.params import Params
@ -159,7 +150,6 @@ from selfdrive.loggerd.config import ROOT
from selfdrive.launcher import launcher from selfdrive.launcher import launcher
from selfdrive.hardware.eon.apk import update_apks, pm_apply_packages, start_offroad from selfdrive.hardware.eon.apk import update_apks, pm_apply_packages, start_offroad
ThermalStatus = cereal.log.ThermalData.ThermalStatus
# comment out anything you don't want to run # comment out anything you don't want to run
managed_processes = { managed_processes = {
@ -245,13 +235,7 @@ driver_view_processes = [
'dmonitoringmodeld' 'dmonitoringmodeld'
] ]
if WEBCAM: if not PC or WEBCAM:
car_started_processes += [
'dmonitoringd',
'dmonitoringmodeld',
]
if not PC:
car_started_processes += [ car_started_processes += [
'ubloxd', 'ubloxd',
'dmonitoringd', 'dmonitoringd',
@ -267,7 +251,6 @@ if EON:
def register_managed_process(name, desc, car_started=False): def register_managed_process(name, desc, car_started=False):
global managed_processes, car_started_processes, persistent_processes global managed_processes, car_started_processes, persistent_processes
print("registering %s" % name)
managed_processes[name] = desc managed_processes[name] = desc
if car_started: if car_started:
car_started_processes.append(name) car_started_processes.append(name)
@ -323,22 +306,22 @@ def start_daemon_process(name):
params.put(pid_param, str(proc.pid)) params.put(pid_param, str(proc.pid))
def prepare_managed_process(p): def prepare_managed_process(p, build=False):
proc = managed_processes[p] proc = managed_processes[p]
if isinstance(proc, str): if isinstance(proc, str):
# import this python # import this python
cloudlog.info("preimporting %s" % proc) cloudlog.info("preimporting %s" % proc)
importlib.import_module(proc) importlib.import_module(proc)
elif os.path.isfile(os.path.join(BASEDIR, proc[0], "Makefile")): elif os.path.isfile(os.path.join(BASEDIR, proc[0], "SConscript")) and build:
# build this process # build this process
cloudlog.info("building %s" % (proc,)) cloudlog.info("building %s" % (proc,))
try: try:
subprocess.check_call(["make", "-j4"], cwd=os.path.join(BASEDIR, proc[0])) subprocess.check_call(["scons", "u", "-j4", "."], cwd=os.path.join(BASEDIR, proc[0]))
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
# make clean if the build failed # clean and retry if the build failed
cloudlog.warning("building %s failed, make clean" % (proc, )) cloudlog.warning("building %s failed, cleaning and retrying" % (proc, ))
subprocess.check_call(["make", "clean"], cwd=os.path.join(BASEDIR, proc[0])) subprocess.check_call(["scons", "-u", "-c", "."], cwd=os.path.join(BASEDIR, proc[0]))
subprocess.check_call(["make", "-j4"], cwd=os.path.join(BASEDIR, proc[0])) subprocess.check_call(["scons", "-u", "-j4", "."], cwd=os.path.join(BASEDIR, proc[0]))
def join_process(process, timeout): def join_process(process, timeout):
@ -404,21 +387,23 @@ def send_managed_process_signal(name, sig):
# ****************** run loop ****************** # ****************** run loop ******************
def manager_init(should_register=True): def manager_init():
if should_register: # Create folders needed for msgq
try:
os.mkdir("/dev/shm")
except FileExistsError:
pass
except PermissionError:
print("WARNING: failed to make /dev/shm")
# set dongle id
reg_res = register() reg_res = register()
if reg_res: if reg_res:
dongle_id = reg_res dongle_id = reg_res
else: else:
raise Exception("server registration failed") 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 os.environ['DONGLE_ID'] = dongle_id
cloudlog.info("dirty is %d" % dirty)
if not dirty: if not dirty:
os.environ['CLEAN'] = '1' os.environ['CLEAN'] = '1'
@ -435,12 +420,11 @@ def manager_init(should_register=True):
# ensure shared libraries are readable by apks # ensure shared libraries are readable by apks
if EON: if EON:
os.chmod(BASEDIR, 0o755) os.chmod(BASEDIR, 0o755)
os.chmod("/dev/shm", 0o777)
os.chmod(os.path.join(BASEDIR, "cereal"), 0o755) os.chmod(os.path.join(BASEDIR, "cereal"), 0o755)
os.chmod(os.path.join(BASEDIR, "cereal", "libmessaging_shared.so"), 0o755) os.chmod(os.path.join(BASEDIR, "cereal", "libmessaging_shared.so"), 0o755)
def manager_thread(): def manager_thread():
# now loop
thermal_sock = messaging.sub_sock('thermal')
cloudlog.info("manager start") cloudlog.info("manager start")
cloudlog.info({"environ": os.environ}) cloudlog.info({"environ": os.environ})
@ -448,8 +432,6 @@ def manager_thread():
# save boot log # save boot log
subprocess.call(["./loggerd", "--bootlog"], cwd=os.path.join(BASEDIR, "selfdrive/loggerd")) subprocess.call(["./loggerd", "--bootlog"], cwd=os.path.join(BASEDIR, "selfdrive/loggerd"))
params = Params()
# start daemon processes # start daemon processes
for p in daemon_processes: for p in daemon_processes:
start_daemon_process(p) start_daemon_process(p)
@ -472,6 +454,8 @@ def manager_thread():
started_prev = False started_prev = False
logger_dead = False logger_dead = False
params = Params()
thermal_sock = messaging.sub_sock('thermal')
while 1: while 1:
msg = messaging.recv_sock(thermal_sock, wait=True) msg = messaging.recv_sock(thermal_sock, wait=True)
@ -502,6 +486,7 @@ def manager_thread():
# trigger an update after going offroad # trigger an update after going offroad
if started_prev: if started_prev:
os.sync()
send_managed_process_signal("updated", signal.SIGHUP) send_managed_process_signal("updated", signal.SIGHUP)
started_prev = msg.thermal.started started_prev = msg.thermal.started
@ -514,25 +499,18 @@ def manager_thread():
if params.get("DoUninstall", encoding='utf8') == "1": if params.get("DoUninstall", encoding='utf8') == "1":
break break
def manager_prepare(spinner=None): def manager_prepare():
# build all processes # build all processes
os.chdir(os.path.dirname(os.path.abspath(__file__))) os.chdir(os.path.dirname(os.path.abspath(__file__)))
# Spinner has to start from 70 here # Spinner has to start from 70 here
total = 100.0 if prebuilt else 30.0 total = 100.0 if PREBUILT else 30.0
for i, p in enumerate(managed_processes): for i, p in enumerate(managed_processes):
if spinner is not None: perc = (100.0 - total) + total * (i + 1) / len(managed_processes)
spinner.update("%d" % ((100.0 - total) + total * (i + 1) / len(managed_processes),)) spinner.update(str(int(perc)))
prepare_managed_process(p) 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)
HARDWARE.reboot(reason="recovery")
def main(): def main():
params = Params() params = Params()
params.manager_start() params.manager_start()
@ -558,7 +536,7 @@ def main():
if params.get(k) is None: if params.get(k) is None:
params.put(k, v) params.put(k, v)
# is this chffrplus? # is this dashcam?
if os.getenv("PASSIVE") is not None: if os.getenv("PASSIVE") is not None:
params.put("Passive", str(int(os.getenv("PASSIVE")))) params.put("Passive", str(int(os.getenv("PASSIVE"))))
@ -568,7 +546,7 @@ def main():
if EON: if EON:
update_apks() update_apks()
manager_init() manager_init()
manager_prepare(spinner) manager_prepare()
spinner.close() spinner.close()
if os.getenv("PREPAREONLY") is not None: if os.getenv("PREPAREONLY") is not None:
@ -586,7 +564,8 @@ def main():
cleanup_all_processes(None, None) cleanup_all_processes(None, None)
if params.get("DoUninstall", encoding='utf8') == "1": if params.get("DoUninstall", encoding='utf8') == "1":
uninstall() cloudlog.warning("uninstalling")
HARDWARE.uninstall()
if __name__ == "__main__": if __name__ == "__main__":
@ -598,7 +577,7 @@ if __name__ == "__main__":
# Show last 3 lines of traceback # Show last 3 lines of traceback
error = traceback.format_exc(-3) error = traceback.format_exc(-3)
error = "Manager failed to start\n \n" + error error = "Manager failed to start\n\n" + error
spinner.close() spinner.close()
with TextWindow(error) as t: with TextWindow(error) as t:
t.wait_for_exit() t.wait_for_exit()

Loading…
Cancel
Save