make install work also for rpi boxes
- in addition to to_system, we have now to_<box> with every type of box - install is not needed to run under sudo anymore
This commit is contained in:
30
README.md
30
README.md
@ -1,13 +1,19 @@
|
||||
# Installation tools for APUs and Raspberry Pi boxes
|
||||
|
||||
The APU is a Linux box to be used as a control box at LIN sample environment
|
||||
Supports teh following boxes used at LIN sample environment:
|
||||
|
||||
- bare-apu: Linux box
|
||||
- controlbox: from FRM2
|
||||
- ionopimax: PLC from Sfera Labs with compute module 3
|
||||
- ionopi: PLC from Sfrea labs with Raspberry Pi 3
|
||||
- dual-eth-rpi: dual ethernet Raspberry Pi with compute module 4
|
||||
|
||||
`cfg/` contains the configuration files for all boxes
|
||||
|
||||
`to_system/` contains the files to be installed in the system
|
||||
|
||||
We have also the Raspberry based PLC Iono Pi Max
|
||||
and other Raspberry Pi based boxes.
|
||||
`to_<box>/` system files specific for a box (box names as in above list)
|
||||
|
||||
|
||||
## config files
|
||||
|
||||
@ -16,9 +22,12 @@ filename: cfg/(hostname)_(hexdigits).cfg
|
||||
* (hostname) the hostname to be given on startup
|
||||
* (hexdigits) the last six digits of the ethernet adress of the first interface on the APU (leftmost plug)
|
||||
|
||||
content of the config files:
|
||||
### example content of a config files for a control box:
|
||||
|
||||
```
|
||||
[BOX]
|
||||
type=controlbox
|
||||
|
||||
[NETWORK]
|
||||
eth0=192.168.127.254 # leftmost socket: connect here a moxa with factory settings
|
||||
eth1=192.168.2.2 # connected device will get this ip via DHCP
|
||||
@ -38,6 +47,19 @@ port=5000 # the port for the frappy server
|
||||
startup_text=startup...|HOST|ADDR # startup text, 3 lines separated with |
|
||||
```
|
||||
|
||||
the [BOX] section is optional for controlbox and bare-apu
|
||||
|
||||
|
||||
### example content of a config files for a control box:
|
||||
|
||||
```
|
||||
[BOX]
|
||||
type=ionopi
|
||||
|
||||
[NETWORK]
|
||||
eth0=wan
|
||||
```
|
||||
|
||||
## network configuration
|
||||
|
||||
The example above fits the most cases.
|
||||
|
196
install.py
196
install.py
@ -12,37 +12,36 @@ if bytes == str:
|
||||
import sys
|
||||
import os
|
||||
import filecmp
|
||||
import shutil
|
||||
import re
|
||||
import types
|
||||
import socket
|
||||
from pathlib import Path
|
||||
from subprocess import Popen, PIPE
|
||||
from ipaddress import IPv4Interface
|
||||
from os.path import join, getmtime, exists, basename
|
||||
from os.path import getmtime
|
||||
|
||||
if os.geteuid() != 0:
|
||||
exit("You need to have root privileges to run this script.\nPlease try again, this time using 'sudo'. Exiting.")
|
||||
|
||||
def exit():
|
||||
print('please restart sudo ./install.py again')
|
||||
sys.exit()
|
||||
|
||||
|
||||
try:
|
||||
import serial
|
||||
except ImportError:
|
||||
if 'yes'.startswith(input('install pyserial? [y]')):
|
||||
os.system('pip3 install --break-system-packages pyserial')
|
||||
os.system('sudo pip3 install --break-system-packages pyserial')
|
||||
serial = None
|
||||
|
||||
try:
|
||||
from utils import BoxInfo, check_service, unix_cmd, change_firewall
|
||||
from utils import BoxInfo, check_service, unix_cmd, change_firewall, UndefinedConfigFile
|
||||
except ImportError:
|
||||
if 'yes'.startswith(input('install netifaces? [y]')):
|
||||
os.system('pip3 install --break-system-packages netifaces')
|
||||
os.system('sudo pip3 install --break-system-packages netifaces')
|
||||
serial = None
|
||||
|
||||
if serial is None:
|
||||
print('please restart sudo ./install.py again')
|
||||
print('please restart ./install.py again')
|
||||
exit()
|
||||
|
||||
|
||||
@ -51,36 +50,62 @@ TOOLS = BoxInfo.TOOLS
|
||||
more_info = False
|
||||
|
||||
DEL = '__to_delete__'
|
||||
STARTUP_TEXT = f'{TOOLS}/startup_display.txt'
|
||||
STARTUP_TEXT = TOOLS / 'startup_display.txt'
|
||||
|
||||
COMMENT = "; please refer to README.md for help"
|
||||
|
||||
CONFIG_TEMPLATE = f"""[NETWORK]
|
||||
{COMMENT}
|
||||
TEMPLATES = {}
|
||||
TEMPLATES['bare-apu'] = f"""{COMMENT}
|
||||
|
||||
[BOX]
|
||||
type=bare-apu
|
||||
|
||||
[NETWORK]
|
||||
eth0=wan
|
||||
eth1=192.168.2.2
|
||||
eth2=192.168.3.3
|
||||
eth3=192.168.127.254
|
||||
|
||||
[ROUTER]
|
||||
{COMMENT}
|
||||
3001=192.168.127.254:3001
|
||||
"""
|
||||
|
||||
CONTROLBOX_TEMPLATE = f"""[NETWORK]
|
||||
{COMMENT}
|
||||
eth0=dhcp
|
||||
TEMPLATES['controlbox'] = f"""{COMMENT}
|
||||
|
||||
[BOX]
|
||||
type=controlbox
|
||||
|
||||
[NETWORK]
|
||||
eth0=wan
|
||||
eth1=192.168.1.1
|
||||
eth2=192.168.2.2
|
||||
eth3=192.168.3.3
|
||||
|
||||
[DISPLAY]
|
||||
{COMMENT}
|
||||
line0=startup...
|
||||
line1=HOST
|
||||
line2=ADDR
|
||||
"""
|
||||
|
||||
TEMPLATES['dual-eth-rpi'] = f"""{COMMENT}
|
||||
|
||||
[BOX]
|
||||
type=dual-eth-rpi
|
||||
|
||||
[NETWORK]
|
||||
eth0=wan
|
||||
eth1=192.168.1.1
|
||||
"""
|
||||
|
||||
GENERIC_TEMPLATE = f"""{COMMENT}
|
||||
|
||||
[BOX]
|
||||
type=%s
|
||||
|
||||
[NETWORK]
|
||||
eth0=wan
|
||||
"""
|
||||
|
||||
DHCP_HEADER = """
|
||||
default-lease-time 600;
|
||||
max-lease-time 7200;
|
||||
@ -133,14 +158,22 @@ box = BoxInfo()
|
||||
box.hostname_changed = False
|
||||
dhcp_server_cfg = []
|
||||
|
||||
TO_SYSTEM = [f'{TOOLS}/to_system', f'{TOOLS}/{box.typ}_system']
|
||||
TO_SYSTEM = [TOOLS / 'to_system', TOOLS / f'{box.typ}_system']
|
||||
|
||||
|
||||
def do_cmd(command):
|
||||
unix_cmd(command, doit)
|
||||
def do_cmd(*command):
|
||||
unix_cmd(*command, execute=doit) # with sudo
|
||||
show.dirty = True
|
||||
|
||||
|
||||
def copyfiles(srcdir, dstdir, files):
|
||||
unix_cmd('cp', *(str(srcdir / f) for f in files), str(dstdir))
|
||||
|
||||
|
||||
def deletefiles(srcdir, files):
|
||||
unix_cmd('rm', '-f', *(str(srcdir / f) for f in files))
|
||||
|
||||
|
||||
def frappy(cfg=None, port=None, requirements='', **kwds):
|
||||
if not cfg:
|
||||
return None
|
||||
@ -173,8 +206,8 @@ def router(firewall=False, **opts):
|
||||
if not opts:
|
||||
return None
|
||||
try:
|
||||
os.remove(join(TO_SYSTEM[0], 'etc/nftables.conf'))
|
||||
with open(f'{TOOLS}/requirements.txt') as f:
|
||||
os.remove(TO_SYSTEM[0] / 'etc/nftables.conf')
|
||||
with open(TOOLS / 'requirements.txt') as f:
|
||||
pip_requirements['root']['tools'] = f.read()
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
@ -202,20 +235,23 @@ def display(**opts):
|
||||
def pip():
|
||||
for user, requirements in pip_requirements.items():
|
||||
if user == 'root':
|
||||
tmpname = join('/root', 'pip_requirements.tmp')
|
||||
pipcmd = 'pip3 install -r %s' % tmpname
|
||||
tmpname = '/root/pip_requirements.tmp'
|
||||
pipcmd = ['sudo pip3 install -r', tmpname]
|
||||
else:
|
||||
tmpname = join('/home', user, 'pip_requirements.tmp')
|
||||
pipcmd = 'sudo --user %s pip3 install --user --break-system-packages -r %s' % (user, tmpname)
|
||||
tmpname = f'/home/{user}/pip_requirements.tmp'
|
||||
pipcmd = ['pip3 install --user --break-system-packages -r', tmpname]
|
||||
filename = tmpname.replace('.tmp', '.txt')
|
||||
content = ''.join('# --- for %s ---\n%s\n' % kv for kv in requirements.items())
|
||||
if content:
|
||||
if write_when_new(filename, content, True):
|
||||
if doit:
|
||||
os.rename(filename, tmpname)
|
||||
if os.system(pipcmd) == 0:
|
||||
if os.system(' '.join(pipcmd)) == 0:
|
||||
os.rename(tmpname, filename)
|
||||
else:
|
||||
os.remove(tmpname)
|
||||
else:
|
||||
os.remove(tmpname)
|
||||
else:
|
||||
print(pipcmd)
|
||||
# unix_cmd(pipcmd, doit, stdout=None)
|
||||
@ -225,7 +261,7 @@ def pip():
|
||||
SERVICES = dict(router=router, frappy=frappy, display=display)
|
||||
|
||||
|
||||
def write_when_new(filename, content, ignore_reduction=False):
|
||||
def write_when_new(filename, content, as_root=False, ignore_reduction=False):
|
||||
if content is None:
|
||||
lines = []
|
||||
else:
|
||||
@ -237,6 +273,9 @@ def write_when_new(filename, content, ignore_reduction=False):
|
||||
old = fil.read()
|
||||
except FileNotFoundError:
|
||||
old = None
|
||||
#except Exception as e:
|
||||
# print('can not read', filename, '-> do not check')
|
||||
# return False
|
||||
if old == content:
|
||||
return False
|
||||
if ignore_reduction:
|
||||
@ -249,10 +288,12 @@ def write_when_new(filename, content, ignore_reduction=False):
|
||||
return False
|
||||
if doit:
|
||||
if lines:
|
||||
with open(filename, 'w') as fil:
|
||||
with tempfile.NamedTemporaryFile('w') as fil:
|
||||
fil.write(content)
|
||||
fil.flush()
|
||||
unix_cmd('cp', fil.name, filename, sudo=as_root)
|
||||
else:
|
||||
os.remove(filename)
|
||||
unix_cmd('rm', '-f', filename, sudo=as_root)
|
||||
elif more_info:
|
||||
print('.' * 80)
|
||||
print('changes in', filename)
|
||||
@ -314,7 +355,7 @@ def replace_in_file(filename, pat, repl):
|
||||
if not success:
|
||||
raise ValueError(f'{pat} not in {filename}')
|
||||
|
||||
write_when_new(filename, newcontent)
|
||||
write_when_new(filename, newcontent, as_root=True)
|
||||
return success[0]
|
||||
|
||||
|
||||
@ -356,17 +397,17 @@ def create_if(name, cfg):
|
||||
|
||||
def walk(action):
|
||||
for rootpath in TO_SYSTEM:
|
||||
if not exists(rootpath):
|
||||
if not rootpath.exists():
|
||||
continue
|
||||
os.chdir(rootpath)
|
||||
for dirpath, _, files in os.walk('.'):
|
||||
syspath = dirpath[1:] # remove leading '.'
|
||||
action.dirpath = dirpath
|
||||
syspath = Path(dirpath[1:]) # remove leading '.' -> absolute path
|
||||
action.dirpath = Path(dirpath)
|
||||
action.syspath = syspath
|
||||
if files:
|
||||
match, mismatch, missing = filecmp.cmpfiles(dirpath, syspath, files)
|
||||
if mismatch:
|
||||
newer = [f for f in mismatch if getmtime(join(syspath, f)) > getmtime(join(dirpath, f))]
|
||||
newer = [f for f in mismatch if getmtime(syspath / f) > getmtime(dirpath / f)]
|
||||
if newer:
|
||||
action.newer(newer)
|
||||
if len(newer) < len(mismatch):
|
||||
@ -375,12 +416,12 @@ def walk(action):
|
||||
if missing:
|
||||
if DEL in missing:
|
||||
missing.remove(DEL)
|
||||
with open(join(dirpath, DEL)) as fil:
|
||||
with open(dirpath / DEL) as fil:
|
||||
to_delete = []
|
||||
for fname in fil:
|
||||
fname = fname.strip()
|
||||
if fname and exists(join(syspath, fname)):
|
||||
if exists(join(dirpath, fname)):
|
||||
if fname and exists(syspath / fname):
|
||||
if (dirpath / fname).exists():
|
||||
print('ERROR: %s in %s, but also in repo -> ignored' % (fname, DEL))
|
||||
else:
|
||||
to_delete.append(fname)
|
||||
@ -390,9 +431,8 @@ def walk(action):
|
||||
|
||||
|
||||
class Walker:
|
||||
def __init__(self):
|
||||
self.dirpath = None
|
||||
self.syspath = None
|
||||
dirpath = None
|
||||
syspath = None
|
||||
|
||||
|
||||
class Show(Walker):
|
||||
@ -403,7 +443,7 @@ class Show(Walker):
|
||||
if more_info:
|
||||
for f in files:
|
||||
if f.endswith(more_info):
|
||||
print('diff %s %s' % (join(self.dirpath, f), join(self.syspath, f)))
|
||||
print('diff %s %s' % (self.dirpath / f, self.syspath / f))
|
||||
print('.' * 80)
|
||||
else:
|
||||
print('%s %s:\n %s' % (title, self.syspath, ' '.join(files)))
|
||||
@ -412,7 +452,7 @@ class Show(Walker):
|
||||
self.dirty = True
|
||||
if more_info:
|
||||
for f in files:
|
||||
print('cat %s' % join(dirpath, f))
|
||||
print('cat %s' % (dirpath / f))
|
||||
print('.' * 80)
|
||||
else:
|
||||
print('%s %s:\n %s' % (title, self.syspath, ' '.join(files)))
|
||||
@ -432,41 +472,65 @@ class Show(Walker):
|
||||
|
||||
class Do(Walker):
|
||||
def newer(self, files):
|
||||
for file in files:
|
||||
shutil.copy(join(self.syspath, file), join(self.dirpath, file))
|
||||
copyfiles(self.syspath, self.dirpath, files)
|
||||
|
||||
def older(self, files):
|
||||
for file in files:
|
||||
shutil.copy(join(self.dirpath, file), join(self.syspath, file))
|
||||
older = newer
|
||||
|
||||
def missing(self, files):
|
||||
self.older(files)
|
||||
missing = newer
|
||||
|
||||
def delete(self, files):
|
||||
for file in files:
|
||||
os.remove(join(self.syspath, file))
|
||||
deletefiles(self.syspath, files)
|
||||
|
||||
|
||||
def handle_config():
|
||||
dhcp_server_cfg.clear()
|
||||
try:
|
||||
config = box.read_config()
|
||||
except UndefinedConfigFile as e:
|
||||
print(e)
|
||||
cfgfile = box.cfgfile
|
||||
newhostname = box.hostname
|
||||
if not cfgfile:
|
||||
template = CONFIG_TEMPLATE
|
||||
if cfgfile:
|
||||
if box.hwtype != 'apu':
|
||||
try:
|
||||
typ = config.get('BOX', {}).get('type')
|
||||
if typ:
|
||||
box.typ = typ
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
if box.typ == 'cm4':
|
||||
print('WARNING: missing type in BOX section')
|
||||
print('This is a cm4 so guess its a dual-eth-rpi')
|
||||
box.typ = 'dual-eth-rpi'
|
||||
elif box.typ == 'cm3':
|
||||
print('WARNING: missing type in BOX section')
|
||||
print('This is a cm3 so guess its a ionopimax')
|
||||
box.typ = 'ionopimax'
|
||||
else:
|
||||
raise ValueError(f'unknown type={typ} defined in BOX section')
|
||||
else:
|
||||
if box.typ == 'apu':
|
||||
# determine if display is present
|
||||
disp = serial.Serial('/dev/ttyS1', baudrate=115200, timeout=1)
|
||||
disp.write(b'\x1b\x1b\x01\xf3')
|
||||
display_available = disp.read(8)[0:4] == b'\x1b\x1b\x05\xf3'
|
||||
if display_available:
|
||||
typ = 'controlbox'
|
||||
template = CONTROLBOX_TEMPLATE
|
||||
box.typ = 'controlbox'
|
||||
else:
|
||||
typ = 'bare apu'
|
||||
else:
|
||||
typ = 'rpi'
|
||||
print('no cfg file found for this', typ,
|
||||
box.typ = 'bare-apu'
|
||||
elif box.typ == 'cm4':
|
||||
box.typ = 'dual-eth-rpi'
|
||||
print('This is a cm4, so guess its a dual-eth-rpi - please check type in cfg file')
|
||||
elif box.typ == 'cm3':
|
||||
print('This is a cm3 so guess its a ionopimax - please check type in cfg file')
|
||||
box.typ = 'ionopimax'
|
||||
template = TEMPLATES.get(box.typ)
|
||||
if template is None:
|
||||
box.typ = box.typ or 'unknown-box'
|
||||
template = GENERIC_TEMPLATE % box.typ
|
||||
|
||||
print('no cfg file found for this', box.typ,
|
||||
f'with id {box.id:06x} (hostname={box.hostname})')
|
||||
newhostname = input('enter host name: ')
|
||||
if not newhostname:
|
||||
@ -483,7 +547,7 @@ def handle_config():
|
||||
newhostname = basename(cfgfile).rpartition('_')[0]
|
||||
if doit:
|
||||
print('bash sethostname.sh')
|
||||
os.system(f'bash {TOOLS}/sethostname.sh')
|
||||
unix_cmd('bash', f'{TOOLS}/sethostname.sh')
|
||||
else:
|
||||
if cfgfile:
|
||||
print('replace host name %r by %r' % (box.hostname, newhostname))
|
||||
@ -498,12 +562,14 @@ def handle_config():
|
||||
netcfg = config['NETWORK']
|
||||
for name in netcfg:
|
||||
if name not in box.macaddr:
|
||||
print(name)
|
||||
print(box.macaddr)
|
||||
print(f'{name} is not a valid network interface name')
|
||||
raise RuntimeError('network interface name system does not match')
|
||||
for ifname in box.macaddr:
|
||||
content = create_if(ifname, netcfg.get(ifname, 'off'))
|
||||
# content = '\n'.join('%s=%s' % kv for kv in content.items())
|
||||
todo = write_when_new(f'/etc/network/interfaces.d/{ifname}', content)
|
||||
todo = write_when_new(f'/etc/network/interfaces.d/{ifname}', content, as_root=True)
|
||||
if todo and not more_info:
|
||||
print('change', ifname)
|
||||
show.dirty = True
|
||||
@ -522,7 +588,7 @@ def handle_config():
|
||||
content.append(' range %s %s;\n' % rng)
|
||||
content.append('}\n')
|
||||
content = ''.join(content)
|
||||
todo = write_when_new('/etc/dhcp/dhcpd.conf', content)
|
||||
todo = write_when_new('/etc/dhcp/dhcpd.conf', content, as_root=True)
|
||||
if todo:
|
||||
print('change dhcpd.conf')
|
||||
to_start['isc-dhcp-server'] = 'restart'
|
||||
@ -532,7 +598,7 @@ def handle_config():
|
||||
r'INTERFACESv4="(.*)"', ' '.join([n for n in box.macaddr if n != box.main_if]))
|
||||
except FileNotFoundError:
|
||||
if 'yes'.startswith(input('install isc-dhcp-server? [y]')):
|
||||
os.system('apt-get install isc-dhcp-server')
|
||||
unix_cmd('apt-get install isc-dhcp-server')
|
||||
exit()
|
||||
elif doit:
|
||||
unix_cmd('systemctl stop isc-dhcp-server')
|
||||
@ -560,7 +626,7 @@ def handle_config():
|
||||
to_start[service] = 'enable'
|
||||
elif not active:
|
||||
to_start[service] = 'restart'
|
||||
if write_when_new(f'/etc/systemd/system/{service}.service', servicecfg):
|
||||
if write_when_new(f'/etc/systemd/system/{service}.service', servicecfg, as_root=True):
|
||||
show.dirty = True
|
||||
reload_systemd = True
|
||||
if servicecfg and to_start.get('service') is None:
|
||||
@ -618,7 +684,7 @@ else:
|
||||
doit = True
|
||||
handle_config()
|
||||
walk(Do())
|
||||
with open(f'{TOOLS}/current', 'w') as f:
|
||||
with open(TOOLS / 'current', 'w') as f:
|
||||
f.write(result)
|
||||
elif 'more'.startswith(answer.lower()):
|
||||
more_info = True
|
||||
|
51
to_dual-eth-rpi/boot/config.txt
Executable file
51
to_dual-eth-rpi/boot/config.txt
Executable file
@ -0,0 +1,51 @@
|
||||
# For more options and information see
|
||||
# http://rptl.io/configtxt
|
||||
# Some settings may impact device functionality. See link above for details
|
||||
|
||||
# Uncomment some or all of these to enable the optional hardware interfaces
|
||||
dtparam=i2c_arm=off
|
||||
#dtparam=i2s=on
|
||||
#dtparam=spi=on
|
||||
|
||||
# Enable audio (loads snd_bcm2835)
|
||||
dtparam=audio=on
|
||||
|
||||
# Additional overlays and parameters are documented
|
||||
# /boot/firmware/overlays/README
|
||||
|
||||
# Automatically load overlays for detected cameras
|
||||
camera_auto_detect=1
|
||||
|
||||
# Automatically load overlays for detected DSI displays
|
||||
display_auto_detect=1
|
||||
|
||||
# Automatically load initramfs files, if found
|
||||
auto_initramfs=1
|
||||
|
||||
# Enable DRM VC4 V3D driver
|
||||
dtoverlay=vc4-kms-v3d
|
||||
max_framebuffers=2
|
||||
|
||||
# Don't have the firmware create an initial video= setting in cmdline.txt.
|
||||
# Use the kernel's default instead.
|
||||
disable_fw_kms_setup=1
|
||||
|
||||
# Run in 64-bit mode
|
||||
arm_64bit=1
|
||||
|
||||
# Disable compensation for displays with overscan
|
||||
disable_overscan=1
|
||||
|
||||
# Run as fast as firmware / board allows
|
||||
arm_boost=1
|
||||
|
||||
[cm4]
|
||||
# Enable host mode on the 2711 built-in XHCI USB controller.
|
||||
# This line should be removed if the legacy DWC2 controller is required
|
||||
# (e.g. for USB device mode) or if USB support is not required.
|
||||
otg_mode=1
|
||||
|
||||
[all]
|
||||
# runtime clock
|
||||
dtoverlay=mcp7941x
|
||||
|
2
to_dual-eth-rpi/etc/udev/rules.d/__delete__
Normal file
2
to_dual-eth-rpi/etc/udev/rules.d/__delete__
Normal file
@ -0,0 +1,2 @@
|
||||
99-ionopimax.rules
|
||||
99-ionopi.rules
|
51
to_ionopi/boot/config.txt
Executable file
51
to_ionopi/boot/config.txt
Executable file
@ -0,0 +1,51 @@
|
||||
# For more options and information see
|
||||
# http://rptl.io/configtxt
|
||||
# Some settings may impact device functionality. See link above for details
|
||||
|
||||
# Uncomment some or all of these to enable the optional hardware interfaces
|
||||
dtparam=i2c_arm=off
|
||||
#dtparam=i2s=on
|
||||
#dtparam=spi=on
|
||||
|
||||
# Enable audio (loads snd_bcm2835)
|
||||
dtparam=audio=on
|
||||
|
||||
# Additional overlays and parameters are documented
|
||||
# /boot/firmware/overlays/README
|
||||
|
||||
# Automatically load overlays for detected cameras
|
||||
camera_auto_detect=1
|
||||
|
||||
# Automatically load overlays for detected DSI displays
|
||||
display_auto_detect=1
|
||||
|
||||
# Automatically load initramfs files, if found
|
||||
auto_initramfs=1
|
||||
|
||||
# Enable DRM VC4 V3D driver
|
||||
dtoverlay=vc4-kms-v3d
|
||||
max_framebuffers=2
|
||||
|
||||
# Don't have the firmware create an initial video= setting in cmdline.txt.
|
||||
# Use the kernel's default instead.
|
||||
disable_fw_kms_setup=1
|
||||
|
||||
# Run in 64-bit mode
|
||||
arm_64bit=1
|
||||
|
||||
# Disable compensation for displays with overscan
|
||||
disable_overscan=1
|
||||
|
||||
# Run as fast as firmware / board allows
|
||||
arm_boost=1
|
||||
|
||||
[cm4]
|
||||
# Enable host mode on the 2711 built-in XHCI USB controller.
|
||||
# This line should be removed if the legacy DWC2 controller is required
|
||||
# (e.g. for USB device mode) or if USB support is not required.
|
||||
otg_mode=1
|
||||
|
||||
[all]
|
||||
dtoverlay=ionopi
|
||||
# runtime clock
|
||||
dtoverlay=i2c-rtc,mcp7941x
|
1
to_ionopi/etc/udev/rules.d/99-ionopi.rules
Normal file
1
to_ionopi/etc/udev/rules.d/99-ionopi.rules
Normal file
@ -0,0 +1 @@
|
||||
SUBSYSTEM=="ionopi", PROGRAM="/bin/sh -c 'find -L /sys/class/ionopi/ -maxdepth 2 -exec chown root:ionopi {} \; || true'"
|
1
to_ionopi/etc/udev/rules.d/__delete__
Normal file
1
to_ionopi/etc/udev/rules.d/__delete__
Normal file
@ -0,0 +1 @@
|
||||
99-ionopimax.rules
|
51
to_ionopimax/boot/config.txt
Executable file
51
to_ionopimax/boot/config.txt
Executable file
@ -0,0 +1,51 @@
|
||||
# For more options and information see
|
||||
# http://rptl.io/configtxt
|
||||
# Some settings may impact device functionality. See link above for details
|
||||
|
||||
# Uncomment some or all of these to enable the optional hardware interfaces
|
||||
dtparam=i2c_arm=off
|
||||
#dtparam=i2s=on
|
||||
#dtparam=spi=on
|
||||
|
||||
# Enable audio (loads snd_bcm2835)
|
||||
dtparam=audio=on
|
||||
|
||||
# Additional overlays and parameters are documented
|
||||
# /boot/firmware/overlays/README
|
||||
|
||||
# Automatically load overlays for detected cameras
|
||||
camera_auto_detect=1
|
||||
|
||||
# Automatically load overlays for detected DSI displays
|
||||
display_auto_detect=1
|
||||
|
||||
# Automatically load initramfs files, if found
|
||||
auto_initramfs=1
|
||||
|
||||
# Enable DRM VC4 V3D driver
|
||||
dtoverlay=vc4-kms-v3d
|
||||
max_framebuffers=2
|
||||
|
||||
# Don't have the firmware create an initial video= setting in cmdline.txt.
|
||||
# Use the kernel's default instead.
|
||||
disable_fw_kms_setup=1
|
||||
|
||||
# Run in 64-bit mode
|
||||
arm_64bit=1
|
||||
|
||||
# Disable compensation for displays with overscan
|
||||
disable_overscan=1
|
||||
|
||||
# Run as fast as firmware / board allows
|
||||
arm_boost=1
|
||||
|
||||
[cm4]
|
||||
# Enable host mode on the 2711 built-in XHCI USB controller.
|
||||
# This line should be removed if the legacy DWC2 controller is required
|
||||
# (e.g. for USB device mode) or if USB support is not required.
|
||||
otg_mode=1
|
||||
|
||||
[all]
|
||||
dtoverlay=ionopimax
|
||||
# runtime clock
|
||||
dtoverlay=i2c-rtc,mcp7941x
|
1
to_ionopimax/etc/udev/rules.d/99-ionopimax.rules
Normal file
1
to_ionopimax/etc/udev/rules.d/99-ionopimax.rules
Normal file
@ -0,0 +1 @@
|
||||
SUBSYSTEM=="ionopimax", PROGRAM="/bin/sh -c 'find -L /sys/class/ionopimax/ -maxdepth 2 -exec chown root:ionopimax {} \; || true'"
|
1
to_ionopimax/etc/udev/rules.d/__delete__
Normal file
1
to_ionopimax/etc/udev/rules.d/__delete__
Normal file
@ -0,0 +1 @@
|
||||
99-ionopi.rules
|
36
utils.py
36
utils.py
@ -3,6 +3,7 @@ import socket
|
||||
import threading
|
||||
import re
|
||||
from glob import glob
|
||||
from pathlib import Path
|
||||
from configparser import ConfigParser
|
||||
from netifaces import interfaces, ifaddresses, gateways, AF_INET, AF_LINK
|
||||
from subprocess import Popen, PIPE
|
||||
@ -19,13 +20,18 @@ else:
|
||||
os.system(cmd)
|
||||
|
||||
|
||||
class UndefinedConfigFile(Exception):
|
||||
"""config file not found or ambiguous"""
|
||||
|
||||
|
||||
class BoxInfo:
|
||||
TOOLS = '/home/l_samenv/boxtools'
|
||||
CFGPATH = f'{TOOLS}/cfg/%s_%06x.cfg'
|
||||
TOOLS = Path('/home/l_samenv/boxtools')
|
||||
CFGPATH = str(TOOLS / 'cfg' / '%s_%06x.cfg')
|
||||
BOX_TYPES = {
|
||||
'00:0d:b9': 'apu', # bare apu or control box
|
||||
'b8:27:eb': 'cm3', # iono pi
|
||||
'd8:3a:dd': 'cm4', # dual-eth-rpi
|
||||
'b8:27:eb': 'cm3', # guess iono pi max
|
||||
'e4:5f:01': 'ionopi', # guess iono pi
|
||||
'd8:3a:dd': 'cm4', # guess dual-eth-rpi
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
@ -50,6 +56,7 @@ class BoxInfo:
|
||||
self.id = int(''.join(addr.split(':')[-3:]), 16) & 0xffffff
|
||||
self.typ = self.BOX_TYPES.get(addr[:8])
|
||||
self.main_if = ifname
|
||||
self.hwtype = self.typ # this is one of the values in BOX_TYPE and will not change
|
||||
|
||||
def get_macaddr(self):
|
||||
return self.macaddr.get(self.main_if)
|
||||
@ -57,9 +64,9 @@ class BoxInfo:
|
||||
def read_config(self, section=None):
|
||||
cfgfiles = glob(self.CFGPATH % ('*', self.id))
|
||||
if len(cfgfiles) > 1:
|
||||
raise ValueError('ambiguous cfgfile: %r' % cfgfiles)
|
||||
raise AmbiguousConfigFile('ambiguous cfgfile: %r' % cfgfiles)
|
||||
if section and not cfgfiles:
|
||||
raise ValueError('no cfg file found for %s' % self.id)
|
||||
raise UndefinedConfigFile('no cfg file found for %s' % self.id)
|
||||
if cfgfiles:
|
||||
self.cfgfile = cfgfiles[0]
|
||||
else:
|
||||
@ -141,14 +148,16 @@ class MainIf:
|
||||
return self.carrier, self.ip, self.hostnameresult[0], self.gateway
|
||||
|
||||
|
||||
def unix_cmd(command, execute=None, stdout=PIPE):
|
||||
if execute != False: # None or True
|
||||
def unix_cmd(cmd, *args, execute=None, stdout=PIPE, sudo=True):
|
||||
command = cmd.split() + list(args)
|
||||
sudo = ['sudo'] if sudo else []
|
||||
if execute is not False: # None or True
|
||||
if execute:
|
||||
print('$ %s' % command)
|
||||
result = Popen(command.split(), stdout=stdout).communicate()[0]
|
||||
print('$', *command)
|
||||
result = Popen(sudo + command, stdout=stdout).communicate()[0]
|
||||
return (result or b'').decode()
|
||||
else:
|
||||
print('> %s' % command)
|
||||
print('>', *command)
|
||||
|
||||
|
||||
def check_service(service, set_on=None, execute=None):
|
||||
@ -185,15 +194,12 @@ def change_firewall(set_on, ports, execute=None):
|
||||
ports.add(22) # always add ssh
|
||||
active, enabled = check_service('nftables')
|
||||
if not set_on:
|
||||
if os.geteuid() == 0:
|
||||
check_service('nftables', False, execute)
|
||||
else:
|
||||
print('need sudo rights to modify firewall')
|
||||
return active or enabled
|
||||
|
||||
pattern = re.compile('(tcp dport ({.*}) ct state new accept)', re.MULTILINE | re.DOTALL)
|
||||
|
||||
for filename in FIREWALL_CONF, os.path.join(BoxInfo.TOOLS, 'nftables.conf'):
|
||||
for filename in FIREWALL_CONF, BoxInfo.TOOLS / 'nftables.conf':
|
||||
with open(filename) as f:
|
||||
content = f.read()
|
||||
|
||||
|
Reference in New Issue
Block a user