2017-07-28 16:38:19 +00:00
|
|
|
#!/usr/bin/env python3
|
2017-03-08 17:28:55 +00:00
|
|
|
#
|
2018-02-20 00:53:15 +00:00
|
|
|
# Copyright 2017 Ettus Research, a National Instruments Company
|
2017-03-08 17:28:55 +00:00
|
|
|
#
|
2018-02-20 00:53:15 +00:00
|
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
2017-03-08 17:28:55 +00:00
|
|
|
#
|
|
|
|
|
"""
|
|
|
|
|
Main executable for the USRP Hardware Daemon
|
|
|
|
|
"""
|
|
|
|
|
from __future__ import print_function
|
2017-03-28 00:07:44 +00:00
|
|
|
import sys
|
2017-11-21 22:53:28 +00:00
|
|
|
import time
|
2017-05-11 01:04:31 +00:00
|
|
|
import argparse
|
2017-04-20 01:45:29 +00:00
|
|
|
from gevent import signal
|
2017-03-08 17:28:55 +00:00
|
|
|
import usrp_mpm as mpm
|
2017-05-05 21:59:35 +00:00
|
|
|
from usrp_mpm.mpmtypes import SharedState
|
2018-01-12 02:58:06 +00:00
|
|
|
from usrp_mpm.sys_utils import watchdog
|
2017-03-28 00:07:44 +00:00
|
|
|
|
2020-07-27 20:01:59 +00:00
|
|
|
# pylint: disable=wrong-import-order
|
|
|
|
|
# We have to import threading here because it must be imported after
|
|
|
|
|
# gevent.monkey.patch_all is called in rpc_server.py.
|
|
|
|
|
# (imported in the usrp_mpm __init__.py)
|
|
|
|
|
from threading import Event, Thread
|
|
|
|
|
|
2017-03-28 00:07:44 +00:00
|
|
|
_PROCESSES = []
|
2020-07-27 20:01:59 +00:00
|
|
|
_KILL_EVENT = Event()
|
2020-07-24 13:35:35 +00:00
|
|
|
# This Global Variable is used by the Simulator to make the spawn_processes,
|
|
|
|
|
# and by extension the main method, exit without waiting for the simulator to stop.
|
|
|
|
|
# See process_manager.py:bootstrap() for more information.
|
|
|
|
|
JOIN_PROCESSES = True
|
2017-03-08 17:28:55 +00:00
|
|
|
|
2017-05-11 01:04:31 +00:00
|
|
|
def setup_arg_parser():
|
|
|
|
|
"""
|
|
|
|
|
Create an arg parser
|
|
|
|
|
"""
|
|
|
|
|
parser = argparse.ArgumentParser(description="USRP Hardware Daemon")
|
2020-07-24 13:35:35 +00:00
|
|
|
parser.add_argument(
|
|
|
|
|
'--no-logbuf',
|
|
|
|
|
dest='use_logbuf',
|
|
|
|
|
help="Do not send log messages to UHD",
|
|
|
|
|
action="store_false",
|
|
|
|
|
)
|
2017-05-11 01:04:31 +00:00
|
|
|
parser.add_argument(
|
|
|
|
|
'--daemon',
|
|
|
|
|
help="Run as daemon",
|
|
|
|
|
action="store_true",
|
|
|
|
|
)
|
|
|
|
|
parser.add_argument(
|
|
|
|
|
'--init-only',
|
|
|
|
|
help="Don't start the RPC server, terminate after running initialization",
|
|
|
|
|
action="store_true",
|
|
|
|
|
)
|
|
|
|
|
parser.add_argument(
|
|
|
|
|
'--override-db-pids',
|
|
|
|
|
help="Provide a comma-separated list of daughterboard PIDs that are " \
|
|
|
|
|
"used instead of whatever else the code may find",
|
|
|
|
|
default=None
|
|
|
|
|
)
|
2017-06-07 05:04:08 +00:00
|
|
|
parser.add_argument(
|
|
|
|
|
'--discovery-addr',
|
|
|
|
|
help="Bind discovery socket to this address only. Defaults to all " \
|
|
|
|
|
"addresses.",
|
|
|
|
|
default="0.0.0.0",
|
|
|
|
|
)
|
2017-05-12 21:27:59 +00:00
|
|
|
parser.add_argument(
|
|
|
|
|
'--default-args',
|
|
|
|
|
help="Provide a comma-separated list of key=value pairs that are" \
|
|
|
|
|
"used as defaults for device initialization.",
|
|
|
|
|
default=None
|
|
|
|
|
)
|
2017-11-15 06:27:14 +00:00
|
|
|
parser.add_argument(
|
|
|
|
|
'-v',
|
|
|
|
|
'--verbose',
|
|
|
|
|
help="Increase verbosity level",
|
|
|
|
|
action="count",
|
|
|
|
|
default=0
|
|
|
|
|
)
|
|
|
|
|
parser.add_argument(
|
|
|
|
|
'-q',
|
|
|
|
|
'--quiet',
|
|
|
|
|
help="Decrease verbosity level",
|
|
|
|
|
action="count",
|
|
|
|
|
default=0
|
|
|
|
|
)
|
2017-05-11 01:04:31 +00:00
|
|
|
return parser
|
|
|
|
|
|
|
|
|
|
def parse_args():
|
|
|
|
|
"""
|
|
|
|
|
Return a fully parse args object
|
|
|
|
|
"""
|
|
|
|
|
args = setup_arg_parser().parse_args()
|
2017-05-12 21:27:59 +00:00
|
|
|
args.default_args = args.default_args or ''
|
|
|
|
|
try:
|
|
|
|
|
args.default_args = {
|
2018-01-03 21:12:27 +00:00
|
|
|
x.split('=')[0].strip(): x.split('=')[1].strip()
|
|
|
|
|
if x.find('=') != -1 else ''
|
2017-05-12 21:27:59 +00:00
|
|
|
for x in args.default_args.split(',')
|
|
|
|
|
if len(x)
|
|
|
|
|
}
|
|
|
|
|
except IndexError:
|
2018-01-03 21:12:27 +00:00
|
|
|
print("Could not parse default device args: `{}'".format(
|
|
|
|
|
args.default_args))
|
2017-05-11 01:04:31 +00:00
|
|
|
return args
|
|
|
|
|
|
|
|
|
|
|
2017-04-20 01:45:29 +00:00
|
|
|
def kill_time(sig, frame):
|
2017-03-28 00:07:44 +00:00
|
|
|
"""
|
2020-07-27 20:01:59 +00:00
|
|
|
kill all processes by setting _KILL_EVENT
|
2017-03-28 00:07:44 +00:00
|
|
|
to be used in a signal handler
|
2020-07-27 20:01:59 +00:00
|
|
|
"""
|
|
|
|
|
_KILL_EVENT.set()
|
2017-04-20 01:45:29 +00:00
|
|
|
|
2020-07-27 20:01:59 +00:00
|
|
|
def kill_thread():
|
|
|
|
|
"""
|
|
|
|
|
Kill all processes after _KILL_EVENT is triggered
|
2017-04-20 01:45:29 +00:00
|
|
|
If all processes are properly terminated, this will exit
|
2017-03-28 00:07:44 +00:00
|
|
|
"""
|
2020-07-27 20:01:59 +00:00
|
|
|
_KILL_EVENT.wait()
|
2017-04-20 01:45:29 +00:00
|
|
|
log = mpm.get_main_logger().getChild('kill')
|
2017-03-28 00:07:44 +00:00
|
|
|
for proc in _PROCESSES:
|
|
|
|
|
proc.terminate()
|
2017-04-27 22:07:25 +00:00
|
|
|
log.info("Terminating pid: {0}".format(proc.pid))
|
2017-03-28 00:07:44 +00:00
|
|
|
for proc in _PROCESSES:
|
2020-07-27 20:01:59 +00:00
|
|
|
proc.join()
|
2017-04-27 22:07:25 +00:00
|
|
|
log.info("System exiting")
|
2017-03-28 00:07:44 +00:00
|
|
|
sys.exit(0)
|
2017-03-08 17:28:55 +00:00
|
|
|
|
2018-01-15 23:42:15 +00:00
|
|
|
def init_only(log, default_args):
|
2017-03-08 17:28:55 +00:00
|
|
|
"""
|
2018-01-10 01:49:34 +00:00
|
|
|
Run the full initialization immediately and return
|
2017-03-08 17:28:55 +00:00
|
|
|
"""
|
2017-03-28 01:03:52 +00:00
|
|
|
# Create the periph_manager for this device
|
|
|
|
|
# This call will be forwarded to the device specific implementation
|
2018-02-28 22:20:56 +00:00
|
|
|
# e.g. in periph_manager/n3xx.py
|
2018-01-10 01:49:34 +00:00
|
|
|
# Which implementation is called will be determined during
|
|
|
|
|
# configuration with cmake (-DMPM_DEVICE).
|
|
|
|
|
# mgr is thus derived from PeriphManagerBase
|
|
|
|
|
# (see periph_manager/base.py)
|
|
|
|
|
from usrp_mpm.periph_manager import periph_manager
|
2017-04-20 01:45:29 +00:00
|
|
|
log.info("Spawning periph manager...")
|
2018-01-10 01:49:34 +00:00
|
|
|
ctor_time_start = time.time()
|
2018-01-15 23:42:15 +00:00
|
|
|
mgr = periph_manager(default_args)
|
2018-01-10 01:49:34 +00:00
|
|
|
ctor_duration = time.time() - ctor_time_start
|
|
|
|
|
log.info("Ctor Duration: {:.02f} s".format(ctor_duration))
|
|
|
|
|
init_time_start = time.time()
|
2018-01-15 23:42:15 +00:00
|
|
|
init_result = mgr.init(default_args)
|
2018-01-10 01:49:34 +00:00
|
|
|
init_duration = time.time() - init_time_start
|
|
|
|
|
if init_result:
|
|
|
|
|
log.info("Initialization successful! Duration: {:.02f} s"
|
|
|
|
|
.format(init_duration))
|
|
|
|
|
else:
|
|
|
|
|
log.warning("Initialization failed! Duration: {:.02f} s"
|
|
|
|
|
.format(init_duration))
|
|
|
|
|
log.info("Terminating on user request before launching RPC server.")
|
|
|
|
|
mgr.deinit()
|
|
|
|
|
return init_result
|
|
|
|
|
|
|
|
|
|
def spawn_processes(log, args):
|
|
|
|
|
"""
|
|
|
|
|
Launch the subprocesses and hang until completion.
|
|
|
|
|
"""
|
|
|
|
|
shared = SharedState()
|
|
|
|
|
log.info("Spawning RPC process...")
|
|
|
|
|
_PROCESSES.append(
|
2018-01-15 23:42:15 +00:00
|
|
|
mpm.spawn_rpc_process(
|
|
|
|
|
mpm.mpmtypes.MPM_RPC_PORT, shared, args.default_args))
|
2018-01-12 02:58:06 +00:00
|
|
|
log.debug("RPC process has PID: %d", _PROCESSES[-1].pid)
|
|
|
|
|
if watchdog.has_watchdog():
|
|
|
|
|
watchdog.transfer_control(_PROCESSES[-1].pid)
|
2017-04-20 01:45:29 +00:00
|
|
|
log.info("Spawning discovery process...")
|
2017-03-28 00:07:44 +00:00
|
|
|
_PROCESSES.append(
|
2018-01-10 01:49:34 +00:00
|
|
|
mpm.spawn_discovery_process(shared, args.discovery_addr)
|
2017-06-07 05:04:08 +00:00
|
|
|
)
|
2018-01-12 02:58:06 +00:00
|
|
|
log.debug("Discovery process has PID: %d", _PROCESSES[-1].pid)
|
2017-04-20 01:45:29 +00:00
|
|
|
log.info("Processes launched. Registering signal handlers.")
|
2020-07-27 20:01:59 +00:00
|
|
|
# Launch the kill thread
|
|
|
|
|
# This is used because we cannot block in a signal handler,
|
|
|
|
|
# meaning we cannot join threads
|
|
|
|
|
Thread(target=kill_thread, daemon=False).start()
|
2017-03-28 00:07:44 +00:00
|
|
|
signal.signal(signal.SIGTERM, kill_time)
|
|
|
|
|
signal.signal(signal.SIGINT, kill_time)
|
2020-07-24 13:35:35 +00:00
|
|
|
if JOIN_PROCESSES:
|
|
|
|
|
for proc in _PROCESSES:
|
|
|
|
|
proc.join()
|
2017-04-20 01:45:29 +00:00
|
|
|
return True
|
2017-03-08 17:28:55 +00:00
|
|
|
|
2018-01-10 01:49:34 +00:00
|
|
|
def main():
|
|
|
|
|
"""
|
|
|
|
|
Go, go, go!
|
|
|
|
|
|
|
|
|
|
Main process loop.
|
|
|
|
|
"""
|
|
|
|
|
args = parse_args()
|
|
|
|
|
log = mpm.get_main_logger(
|
2020-07-24 13:35:35 +00:00
|
|
|
use_logbuf=args.use_logbuf,
|
2018-01-10 01:49:34 +00:00
|
|
|
log_default_delta=args.verbose-args.quiet
|
|
|
|
|
).getChild('main')
|
2018-03-30 21:19:35 +00:00
|
|
|
version_string = mpm.__version__
|
2020-07-02 13:19:34 +00:00
|
|
|
if mpm.__githash__:
|
2018-03-30 21:19:35 +00:00
|
|
|
version_string += "-g" + mpm.__githash__
|
|
|
|
|
log.info("Launching USRP/MPM, version: %s", version_string)
|
2018-07-17 18:58:57 +00:00
|
|
|
if args.init_only:
|
|
|
|
|
# If --init-only is provided, we force disable init during boot time so
|
|
|
|
|
# we can properly time it in init_only().
|
|
|
|
|
args.default_args['skip_boot_init'] = "1"
|
2018-01-15 23:42:15 +00:00
|
|
|
if args.override_db_pids is not None:
|
|
|
|
|
log.warning('Overriding daughterboard PIDs!')
|
|
|
|
|
args.default_args['override_db_pids'] = args.override_db_pids
|
2018-01-10 01:49:34 +00:00
|
|
|
if args.init_only:
|
2018-01-15 23:42:15 +00:00
|
|
|
return init_only(log, args.default_args)
|
2018-01-10 01:49:34 +00:00
|
|
|
return spawn_processes(log, args)
|
|
|
|
|
|
2017-03-08 17:28:55 +00:00
|
|
|
if __name__ == '__main__':
|
2020-07-02 13:19:34 +00:00
|
|
|
sys.exit(not main())
|