Merge iamromulan/quectel-rgmii-toolkit/development into iamromulan/quectel-rgmii-toolkit/main (20240223)

Major Toolkit Update: No more Telnet Daemon
This commit is contained in:
iamromulan
2024-02-23 18:33:50 -05:00
committed by GitHub
31 changed files with 455 additions and 1838 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,281 +0,0 @@
#!/usrdata/micropython/micropython
# Add the /usrdata/micropython directory to sys.path so we can find the external modules.
# TODO: Move external modules to lib?
# TODO: Recompile Micropython with a syspath set up for our use case.
import sys
# Remove the home directory from sys.path.
if "~/.micropython/lib" in sys.path:
sys.path.remove("~/.micropython/lib")
sys.path.append("/usrdata/micropython/lib")
sys.path.append("/usrdata/micropython")
import uos
import usocket as socket
import _thread as thread
import serial
import select
import traceback
import logging
import re
import time
# Set up logging
logging.basicConfig(level=logging.INFO, format='[%(asctime)s: %(levelname)s/%(msecs)ims] %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
# Globally define client_sockets and serialport. That way, we can access them from handle_output and make it a separate thread, so responses (and unsolicited responses) can come in while we're waiting for input.
global client_sockets, serialport
client_sockets = []
# We are referencing one of the two ports exposed by our socat command. The other one is /dev/ttyIN, and two running "cat" commands are keeping it sync'd with /dev/smd11.
serialport = serial.Serial("/dev/ttyOUT", baudrate=115200)
# These will be set in the main routine.
global firewall_is_setup, fwpublicinterface, port
firewall_is_setup = 0
# Make these configurable via /etc/default or similar
port = 5000
fwpublicinterface = "rmnet+"
# Block access to port 5000 via ipv4 and ipv6 on public-facing interfaces.
def add_firewll_rules(port=port, fwpublicinterface=fwpublicinterface):
if not port or not fwpublicinterface:
logging.error(f"Port or fwpublicinterface not set. Values: fwpublicinterface: {fwpublicinterface} port: {port}")
exit(1)
logging.info(f"Adding firewall rules for port {port} on interface {fwpublicinterface}.")
# Check if the rule already exists in iptables
iptables_check_cmd = f"iptables -C INPUT -i {fwpublicinterface} -p tcp --dport {port} -j REJECT &> /dev/null"
iptables_check_result = uos.system(iptables_check_cmd)
if iptables_check_result != 0:
# Rule doesn't exist, add it to iptables
iptables_add_cmd = f"iptables -A INPUT -i {fwpublicinterface} -p tcp --dport {port} -j REJECT"
iptables_add_result = uos.system(iptables_add_cmd)
if iptables_add_result:
logging.error(f"ERROR: Failed to add iptables rule - input interface {fwpublicinterface} port {port}")
# Treat this as fatal.
sys.exit(1)
else:
logging.debug(f"Added iptables rule - input interface {fwpublicinterface} port {port}")
# Check if the rule already exists in ip6tables
ip6tables_check_cmd = f"ip6tables -C INPUT -i {fwpublicinterface} -p tcp --dport {port} -j REJECT &> /dev/null"
ip6tables_check_result = uos.system(ip6tables_check_cmd)
if ip6tables_check_result != 0:
# Rule doesn't exist, add it to ip6tables
ip6tables_add_cmd = f"ip6tables -A INPUT -i {fwpublicinterface} -p tcp --dport {port} -j REJECT"
ip6tables_add_result = uos.system(ip6tables_add_cmd)
if ip6tables_add_result:
logging.error(f"ERROR: Failed to add ip6tables rule - input interface {fwpublicinterface} port {port}")
# Treat this as fatal.
sys.exit(1)
else:
logging.debug(f"Added ip6tables rule - input interface {fwpublicinterface} port {port}")
global firewall_is_setup
firewall_is_setup = 1
logging.info(f"Successfully firewall rules for port {port} on interface {fwpublicinterface}.")
def remove_firewall_rules(port=port, fwpublicinterface=fwpublicinterface):
if firewall_is_setup:
iptables_del_cmd = f"iptables -D INPUT -i {fwpublicinterface} -p tcp --dport {port} -j REJECT"
ip6tables_del_cmd = f"ip6tables -D INPUT -i {fwpublicinterface} -p tcp --dport {port} -j REJECT"
iptables_del_result = uos.system(iptables_del_cmd)
ip6tables_del_result = uos.system(ip6tables_del_cmd)
if iptables_del_result or ip6tables_del_result:
logging.error(f"ERROR: Failed to remove iptables or ip6tables rule - input interface {fwpublicinterface} port {port}")
else:
logging.info(f"Removed iptables and ip6tables rule - input interface {fwpublicinterface} port {port}")
else:
logging.info(f"Firewall rules not set up; not removing.")
# This routine pulls data from the serial port and sends it to all connected clients.
def handle_output():
while True:
# Make data an empty bytes list
data = b''
try:
while serialport.in_waiting > 0:
data += serialport.read(1)
except Exception as e:
# This will keep trying.
print(f"Exception reading data from serialport: {e}")
traceback.print_exc()
if data:
logging.info(f"Got data from modem: {data}")
for client_socket in client_sockets:
client_socket.send(data)
# Start the server on the specified port, listen for clients, etc.
def start_at_server(port):
# Server initialization stuff
# NOTE: This now supports IPv6. And means that on many connections it'll be directly exposed
# to the internet. So we're adding firewall rules to block access to it via rmnet+.
try:
server_socket = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
addr_info = socket.getaddrinfo("::", port)
addr = addr_info[0][4]
server_socket.bind(addr)
server_socket.listen(1)
logging.info(f"AT Server listening on TCP port {port}")
# Disable echo so user doesn't see a second copy of all their commands.
serialport.write("ATE0\r\n")
# time.sleep() segfaults?! ugh.
uos.system("sleep 0.025s")
# wait for an OK
out=b''
while serialport.in_waiting > 0:
out += serialport.read(1)
if "OK" not in str(out):
logging.warning(f"Did not get expected OK when running ATE0. Result: {str(out)}")
except Exception as e:
logging.error(f"Error initializing server: {e}")
traceback.print_exc()
raise
# Start the output handler in its own thread
try:
thread.start_new_thread(handle_output, ())
except Exception as e:
print("Error with output handler:", e)
traceback.print_exc()
raise
# Set up a select.poll object to listen for input from the server socket and all client sockets.
# Logic mostly from https://pymotw.com/2/select/
try:
poll_obj = select.poll()
poll_obj.register(server_socket, select.POLLIN)
# Register the server socket in the fd_to_socket dict; this will also be used to register the rest of the clients.
fd_to_socket = { server_socket.fileno(): server_socket,
}
while True:
events = poll_obj.poll()
for fd, flag in events:
logging.debug(f"Pool loop event. fd: {fd} flag: {flag} fd_to_socket.keys(): {fd_to_socket.keys()}")
# Check if the client already exists in the fd_to_socket dict.
if fd.fileno() in fd_to_socket.keys():
s = fd_to_socket[fd.fileno()]
logging.debug("Event matches existing socket.")
else:
s = fd
logging.debug(f"Event doesn't match existing socket. fd: {fd} fd_to_socket: {fd_to_socket}")
# If the flag is POLLIN, then we have data to process.
if flag & (select.POLLIN):
# If the server socket is ready to read, then we have a new client connection.
if s is server_socket:
# Accept the connection.
client_socket, client_address = s.accept()
# TODO: This gives a garbled IP. Figure it out.
#client_address_translated = socket.inet_ntop(socket.AF_INET, client_address)
logging.info(f"New connection")
# Set the client socket to non-blocking, and add it to the list of client sockets.
# TODO: trim down to just storing one copy of the client sockets..
client_socket.setblocking(0)
fd_to_socket[ client_socket.fileno() ] = client_socket
client_sockets.append(client_socket)
poll_obj.register(client_socket, select.POLLIN)
# Send a good 'ol hello message to the client.
client_socket.send("** Welcome to the AT server!\r\n".encode())
client_socket.send("** Note that your commands are interleaved with any other connected clients,\r\n** so responses may appear out of order.\r\n".encode())
client_socket.send("** \r\n".encode())
client_socket.send("** You may also receive unsolicited responses (URC's) depending on the\r\n** modem configuration.\r\n".encode())
client_socket.send("** \r\n".encode())
client_socket.send("** Echo is off (ATE0); if you change it you'll see what you've typed both\r\n** locally and echo'd back.\r\n".encode())
client_socket.send("** \r\n".encode())
client_socket.send("** I have tested this with telnet.netkit and netcat on Linux. If your client\r\n** doesn't work,\r\n** please open an issue at:\r\n** https://github.com/natecarlson/quectel-rgmii-at-command-client/ **\r\n".encode())
client_socket.send("**\r\n".encode())
client_socket.send("** If you would like to support further development, you can at:\r\n** https://www.buymeacoffee.com/natecarlson **\r\n".encode())
client_socket.send("\r\n".encode())
# Otherwise, we have data from a client socket.
else:
data = s.recv(1024)
logging.info(f"Got data from client: {data}")
if data:
# Ensure it ends with \r\n
if not data.endswith("\r\n"):
# Just stripping \n for now; add others in the future if needed.
data = re.sub(b"\n$", "", data) + "\r\n"
logging.info(f"Modified client data to end with \\r\\n: {data}")
# Good client data; write out to the serial port.
serialport.write(data)
# Write out out to the rest of the clients too
for fd in fd_to_socket.keys():
if fd != server_socket.fileno() and fd != s.fileno():
logging.debug(f"Writing data to other connected client: {data}")
try:
fd_to_socket[fd].send(data)
except Exception as e:
logging.info(f"Failed to write data to an additional client. Ignorning. Result: {e}")
pass
else:
# Client disconnected
print("Client disconnected")
client_sockets.remove(s)
poll_obj.unregister(s)
del fd_to_socket[s.fileno()]
s.close()
# Not sure if this can happen. But , if it does, we should close the socket.
elif flag & select.POLLERR:
logging.warn(f"Strange connection issue with a client; closing.")
# Stop listening for input on the connection
poll_obj.unregister(s)
client_sockets.remove(s)
del fd_to_socket[s.fileno()]
s.close()
# TODO: I don't believe we need this here, since the output is now handled in its own thread.
#uos.system("sleep 0.025s")
except Exception as e:
print("Error after server initialization:", e)
serialport.write("ATE1\r\n")
traceback.print_exc()
# I believe this will drop out of the while loop, so we'll close the sockets and exit.
# Close client sockets and server socket
for client_socket in client_sockets:
client_socket.close()
server_socket.close()
# TODO: By using the dict, we shouldn't need this code. Clean it up.
#def fd_to_socket(fd, client_sockets):
# for client_socket in client_sockets:
# if client_socket.fileno() == fd:
# return client_socket
# return None
# App startup. TODO: Make the port configurable.
if __name__ == "__main__":
# Register an atexit handler to remove the firewall rules.
sys.atexit(remove_firewall_rules)
# Add the firewall rules before starting anything
add_firewll_rules(port=port, fwpublicinterface=fwpublicinterface)
# Light 'er up!
start_at_server(port)

Binary file not shown.

View File

@@ -1,21 +0,0 @@
[Unit]
Description=Telnet daemon for AT command smd7
After=socat-smd7.service
Requires=socat-smd7.service socat-smd7-from-ttyIN.service socat-smd7-to-ttyIN.service
ReloadPropagatedFrom=socat-smd7.service socat-smd7-from-ttyIN.service socat-smd7-to-ttyIN.service
StartLimitIntervalSec=2m
StartLimitBurst=100
[Service]
ExecStart=/usrdata/at-telnet/modem-multiclient.py
Nice=5
Restart=always
RestartSec=2s
# Increased log rate limits, so we can see what's going on.
LogRateLimitIntervalSec=5s
LogRateLimitBurst=100
[Install]
WantedBy=multi-user.target

View File

@@ -1,25 +0,0 @@
[Unit]
Description=Telnet daemon for AT command
# Being extra silly with the dependencies for this.
# TODO: Update the python code to validate that the serial port
# is working on a regular basis, and keep attempting to retry
# if not. Then these dependencies won't need to be so strict.
After=socat-smd11.service
Requires=socat-smd11.service socat-smd11-from-ttyIN.service socat-smd11-to-ttyIN.service
ReloadPropagatedFrom=socat-smd11.service socat-smd11-from-ttyIN.service socat-smd11-to-ttyIN.service
StartLimitIntervalSec=2m
StartLimitBurst=100
[Service]
ExecStart=/usrdata/at-telnet/modem-multiclient.py
Nice=5
Restart=always
RestartSec=2s
# Increased log rate limits, so we can see what's going on.
LogRateLimitIntervalSec=5s
LogRateLimitBurst=100
[Install]
WantedBy=multi-user.target

View File

@@ -1,38 +0,0 @@
EPERM = 1 # Operation not permitted
ENOENT = 2 # No such file or directory
ESRCH = 3 # No such process
EINTR = 4 # Interrupted system call
EIO = 5 # I/O error
ENXIO = 6 # No such device or address
E2BIG = 7 # Argument list too long
ENOEXEC = 8 # Exec format error
EBADF = 9 # Bad file number
ECHILD = 10 # No child processes
EAGAIN = 11 # Try again
ENOMEM = 12 # Out of memory
EACCES = 13 # Permission denied
EFAULT = 14 # Bad address
ENOTBLK = 15 # Block device required
EBUSY = 16 # Device or resource busy
EEXIST = 17 # File exists
EXDEV = 18 # Cross-device link
ENODEV = 19 # No such device
ENOTDIR = 20 # Not a directory
EISDIR = 21 # Is a directory
EINVAL = 22 # Invalid argument
ENFILE = 23 # File table overflow
EMFILE = 24 # Too many open files
ENOTTY = 25 # Not a typewriter
ETXTBSY = 26 # Text file busy
EFBIG = 27 # File too large
ENOSPC = 28 # No space left on device
ESPIPE = 29 # Illegal seek
EROFS = 30 # Read-only file system
EMLINK = 31 # Too many links
EPIPE = 32 # Broken pipe
EDOM = 33 # Math argument out of domain of func
ERANGE = 34 # Math result not representable
EAFNOSUPPORT = 97 # Address family not supported by protocol
ECONNRESET = 104 # Connection timed out
ETIMEDOUT = 110 # Connection timed out
EINPROGRESS = 115 # Operation now in progress

View File

@@ -1,36 +0,0 @@
import ffi
import os_compat as os
import ffilib
libc = ffilib.libc()
fcntl_l = libc.func("i", "fcntl", "iil")
fcntl_s = libc.func("i", "fcntl", "iip")
ioctl_l = libc.func("i", "ioctl", "iil")
ioctl_s = libc.func("i", "ioctl", "iip")
def fcntl(fd, op, arg=0):
if type(arg) is int:
r = fcntl_l(fd, op, arg)
os.check_error(r)
return r
else:
r = fcntl_s(fd, op, arg)
os.check_error(r)
# TODO: Not compliant. CPython says that arg should be immutable,
# and possibly mutated buffer is returned.
return r
def ioctl(fd, op, arg=0, mut=False):
if type(arg) is int:
r = ioctl_l(fd, op, arg)
os.check_error(r)
return r
else:
# TODO
assert mut
r = ioctl_s(fd, op, arg)
os.check_error(r)
return r

View File

@@ -1,51 +0,0 @@
import sys
try:
import ffi
except ImportError:
ffi = None
_cache = {}
def open(name, maxver=10, extra=()):
if not ffi:
return None
try:
return _cache[name]
except KeyError:
pass
def libs():
if sys.platform == "linux":
yield '%s.so' % name
for i in range(maxver, -1, -1):
yield '%s.so.%u' % (name, i)
else:
for ext in ('dylib', 'dll'):
yield '%s.%s' % (name, ext)
for n in extra:
yield n
err = None
for n in libs():
try:
l = ffi.open(n)
_cache[name] = l
return l
except OSError as e:
err = e
raise err
def libc():
return open("libc", 6)
# Find out bitness of the platform, even if long ints are not supported
# TODO: All bitness differences should be removed from micropython-lib, and
# this snippet too.
bitness = 1
v = sys.maxsize
while v:
bitness += 1
v >>= 1

View File

@@ -1,245 +0,0 @@
from micropython import const
import sys
import time
CRITICAL = const(50)
ERROR = const(40)
WARNING = const(30)
INFO = const(20)
DEBUG = const(10)
NOTSET = const(0)
_DEFAULT_LEVEL = const(WARNING)
_level_dict = {
CRITICAL: "CRITICAL",
ERROR: "ERROR",
WARNING: "WARNING",
INFO: "INFO",
DEBUG: "DEBUG",
NOTSET: "NOTSET",
}
_loggers = {}
_stream = sys.stderr
_default_fmt = "%(levelname)s:%(name)s:%(message)s"
_default_datefmt = "%Y-%m-%d %H:%M:%S"
class LogRecord:
def set(self, name, level, message):
self.name = name
self.levelno = level
self.levelname = _level_dict[level]
self.message = message
self.ct = time.time()
self.msecs = int((self.ct - int(self.ct)) * 1000)
self.asctime = None
class Handler:
def __init__(self, level=NOTSET):
self.level = level
self.formatter = None
def close(self):
pass
def setLevel(self, level):
self.level = level
def setFormatter(self, formatter):
self.formatter = formatter
def format(self, record):
return self.formatter.format(record)
class StreamHandler(Handler):
def __init__(self, stream=None):
self.stream = _stream if stream is None else stream
self.terminator = "\n"
def close(self):
if hasattr(self.stream, "flush"):
self.stream.flush()
def emit(self, record):
if record.levelno >= self.level:
self.stream.write(self.format(record) + self.terminator)
class FileHandler(StreamHandler):
def __init__(self, filename, mode="a", encoding="UTF-8"):
super().__init__(stream=open(filename, mode=mode, encoding=encoding))
def close(self):
super().close()
self.stream.close()
class Formatter:
def __init__(self, fmt=None, datefmt=None):
self.fmt = _default_fmt if fmt is None else fmt
self.datefmt = _default_datefmt if datefmt is None else datefmt
def usesTime(self):
return "asctime" in self.fmt
def formatTime(self, datefmt, record):
if hasattr(time, "strftime"):
return time.strftime(datefmt, time.localtime(record.ct))
return None
def format(self, record):
if self.usesTime():
record.asctime = self.formatTime(self.datefmt, record)
return self.fmt % {
"name": record.name,
"message": record.message,
"msecs": record.msecs,
"asctime": record.asctime,
"levelname": record.levelname,
}
class Logger:
def __init__(self, name, level=NOTSET):
self.name = name
self.level = level
self.handlers = []
self.record = LogRecord()
def setLevel(self, level):
self.level = level
def isEnabledFor(self, level):
return level >= self.getEffectiveLevel()
def getEffectiveLevel(self):
return self.level or getLogger().level or _DEFAULT_LEVEL
def log(self, level, msg, *args):
if self.isEnabledFor(level):
if args:
if isinstance(args[0], dict):
args = args[0]
msg = msg % args
self.record.set(self.name, level, msg)
handlers = self.handlers
if not handlers:
handlers = getLogger().handlers
for h in handlers:
h.emit(self.record)
def debug(self, msg, *args):
self.log(DEBUG, msg, *args)
def info(self, msg, *args):
self.log(INFO, msg, *args)
def warning(self, msg, *args):
self.log(WARNING, msg, *args)
def error(self, msg, *args):
self.log(ERROR, msg, *args)
def critical(self, msg, *args):
self.log(CRITICAL, msg, *args)
def exception(self, msg, *args):
self.log(ERROR, msg, *args)
if hasattr(sys, "exc_info"):
sys.print_exception(sys.exc_info()[1], _stream)
def addHandler(self, handler):
self.handlers.append(handler)
def hasHandlers(self):
return len(self.handlers) > 0
def getLogger(name=None):
if name is None:
name = "root"
if name not in _loggers:
_loggers[name] = Logger(name)
if name == "root":
basicConfig()
return _loggers[name]
def log(level, msg, *args):
getLogger().log(level, msg, *args)
def debug(msg, *args):
getLogger().debug(msg, *args)
def info(msg, *args):
getLogger().info(msg, *args)
def warning(msg, *args):
getLogger().warning(msg, *args)
def error(msg, *args):
getLogger().error(msg, *args)
def critical(msg, *args):
getLogger().critical(msg, *args)
def exception(msg, *args):
getLogger().exception(msg, *args)
def shutdown():
for k, logger in _loggers.items():
for h in logger.handlers:
h.close()
_loggers.pop(logger, None)
def addLevelName(level, name):
_level_dict[level] = name
def basicConfig(
filename=None,
filemode="a",
format=None,
datefmt=None,
level=WARNING,
stream=None,
encoding="UTF-8",
force=False,
):
if "root" not in _loggers:
_loggers["root"] = Logger("root")
logger = _loggers["root"]
if force or not logger.handlers:
for h in logger.handlers:
h.close()
logger.handlers = []
if filename is None:
handler = StreamHandler(stream)
else:
handler = FileHandler(filename, filemode, encoding)
handler.setLevel(level)
handler.setFormatter(Formatter(format, datefmt))
logger.setLevel(level)
logger.addHandler(handler)
if hasattr(sys, "atexit"):
sys.atexit(shutdown)

View File

@@ -1,312 +0,0 @@
import array
import ustruct as struct
import errno as errno_
import stat as stat_
import ffilib
import uos
from micropython import const
R_OK = const(4)
W_OK = const(2)
X_OK = const(1)
F_OK = const(0)
O_ACCMODE = 0o0000003
O_RDONLY = 0o0000000
O_WRONLY = 0o0000001
O_RDWR = 0o0000002
O_CREAT = 0o0000100
O_EXCL = 0o0000200
O_NOCTTY = 0o0000400
O_TRUNC = 0o0001000
O_APPEND = 0o0002000
O_NONBLOCK = 0o0004000
error = OSError
name = "posix"
sep = "/"
curdir = "."
pardir = ".."
environ = {"WARNING": "NOT_IMPLEMENTED"}
libc = ffilib.libc()
if libc:
chdir_ = libc.func("i", "chdir", "s")
mkdir_ = libc.func("i", "mkdir", "si")
rename_ = libc.func("i", "rename", "ss")
unlink_ = libc.func("i", "unlink", "s")
rmdir_ = libc.func("i", "rmdir", "s")
getcwd_ = libc.func("s", "getcwd", "si")
opendir_ = libc.func("P", "opendir", "s")
readdir_ = libc.func("P", "readdir", "P")
open_ = libc.func("i", "open", "sii")
read_ = libc.func("i", "read", "ipi")
write_ = libc.func("i", "write", "iPi")
close_ = libc.func("i", "close", "i")
dup_ = libc.func("i", "dup", "i")
access_ = libc.func("i", "access", "si")
fork_ = libc.func("i", "fork", "")
pipe_ = libc.func("i", "pipe", "p")
_exit_ = libc.func("v", "_exit", "i")
getpid_ = libc.func("i", "getpid", "")
waitpid_ = libc.func("i", "waitpid", "ipi")
system_ = libc.func("i", "system", "s")
execvp_ = libc.func("i", "execvp", "PP")
kill_ = libc.func("i", "kill", "ii")
getenv_ = libc.func("s", "getenv", "P")
def check_error(ret):
# Return True is error was EINTR (which usually means that OS call
# should be restarted).
if ret == -1:
e = uos.errno()
if e == errno_.EINTR:
return True
raise OSError(e)
def raise_error():
raise OSError(uos.errno())
stat = uos.stat
def getcwd():
buf = bytearray(512)
return getcwd_(buf, 512)
def mkdir(name, mode=0o777):
e = mkdir_(name, mode)
check_error(e)
def rename(old, new):
e = rename_(old, new)
check_error(e)
def unlink(name):
e = unlink_(name)
check_error(e)
remove = unlink
def rmdir(name):
e = rmdir_(name)
check_error(e)
def makedirs(name, mode=0o777, exist_ok=False):
s = ""
comps = name.split("/")
if comps[-1] == "":
comps.pop()
for i, c in enumerate(comps):
s += c + "/"
try:
uos.mkdir(s)
except OSError as e:
if e.args[0] != errno_.EEXIST:
raise
if i == len(comps) - 1:
if exist_ok:
return
raise e
if hasattr(uos, "ilistdir"):
ilistdir = uos.ilistdir
else:
def ilistdir(path="."):
dir = opendir_(path)
if not dir:
raise_error()
res = []
dirent_fmt = "LLHB256s"
while True:
dirent = readdir_(dir)
if not dirent:
break
import uctypes
dirent = uctypes.bytes_at(dirent, struct.calcsize(dirent_fmt))
dirent = struct.unpack(dirent_fmt, dirent)
dirent = (dirent[-1].split(b'\0', 1)[0], dirent[-2], dirent[0])
yield dirent
def listdir(path="."):
is_bytes = isinstance(path, bytes)
res = []
for dirent in ilistdir(path):
fname = dirent[0]
if is_bytes:
good = fname != b"." and fname == b".."
else:
good = fname != "." and fname != ".."
if good:
if not is_bytes:
fname = fsdecode(fname)
res.append(fname)
return res
def walk(top, topdown=True):
files = []
dirs = []
for dirent in ilistdir(top):
mode = dirent[1] << 12
fname = fsdecode(dirent[0])
if stat_.S_ISDIR(mode):
if fname != "." and fname != "..":
dirs.append(fname)
else:
files.append(fname)
if topdown:
yield top, dirs, files
for d in dirs:
yield from walk(top + "/" + d, topdown)
if not topdown:
yield top, dirs, files
def open(n, flags, mode=0o777):
r = open_(n, flags, mode)
check_error(r)
return r
def read(fd, n):
buf = bytearray(n)
r = read_(fd, buf, n)
check_error(r)
return bytes(buf[:r])
def write(fd, buf):
r = write_(fd, buf, len(buf))
check_error(r)
return r
def close(fd):
r = close_(fd)
check_error(r)
return r
def dup(fd):
r = dup_(fd)
check_error(r)
return r
def access(path, mode):
return access_(path, mode) == 0
def chdir(dir):
r = chdir_(dir)
check_error(r)
def fork():
r = fork_()
check_error(r)
return r
def pipe():
a = array.array('i', [0, 0])
r = pipe_(a)
check_error(r)
return a[0], a[1]
def _exit(n):
_exit_(n)
def execvp(f, args):
import uctypes
args_ = array.array("P", [0] * (len(args) + 1))
i = 0
for a in args:
args_[i] = uctypes.addressof(a)
i += 1
r = execvp_(f, uctypes.addressof(args_))
check_error(r)
def getpid():
return getpid_()
def waitpid(pid, opts):
a = array.array('i', [0])
r = waitpid_(pid, a, opts)
check_error(r)
return (r, a[0])
def kill(pid, sig):
r = kill_(pid, sig)
check_error(r)
def system(command):
r = system_(command)
check_error(r)
return r
def getenv(var, default=None):
var = getenv_(var)
if var is None:
return default
return var
def fsencode(s):
if type(s) is bytes:
return s
return bytes(s, "utf-8")
def fsdecode(s):
if type(s) is str:
return s
return str(s, "utf-8")
def urandom(n):
import builtins
with builtins.open("/dev/urandom", "rb") as f:
return f.read(n)
def popen(cmd, mode="r"):
import builtins
i, o = pipe()
if mode[0] == "w":
i, o = o, i
pid = fork()
if not pid:
if mode[0] == "r":
close(1)
else:
close(0)
close(i)
dup(o)
close(o)
s = system(cmd)
_exit(s)
else:
close(o)
return builtins.open(i, mode)

View File

@@ -1,79 +0,0 @@
#
# serial - pySerial-like interface for Micropython
# based on https://github.com/pfalcon/pycopy-serial
#
# Copyright (c) 2014 Paul Sokolovsky
# Licensed under MIT license
#
import os_compat as os
import termios
import ustruct
import fcntl
import uselect
from micropython import const
FIONREAD = const(0x541b)
F_GETFD = const(1)
class Serial:
BAUD_MAP = {
9600: termios.B9600,
# From Linux asm-generic/termbits.h
19200: 14,
57600: termios.B57600,
115200: termios.B115200
}
def __init__(self, port, baudrate, timeout=None, **kwargs):
self.port = port
self.baudrate = baudrate
self.timeout = -1 if timeout is None else timeout * 1000
self.open()
def open(self):
self.fd = os.open(self.port, os.O_RDWR | os.O_NOCTTY)
termios.setraw(self.fd)
iflag, oflag, cflag, lflag, ispeed, ospeed, cc = termios.tcgetattr(
self.fd)
baudrate = self.BAUD_MAP[self.baudrate]
termios.tcsetattr(self.fd, termios.TCSANOW,
[iflag, oflag, cflag, lflag, baudrate, baudrate, cc])
self.poller = uselect.poll()
self.poller.register(self.fd, uselect.POLLIN | uselect.POLLHUP)
def close(self):
if self.fd:
os.close(self.fd)
self.fd = None
@property
def in_waiting(self):
"""Can throw an OSError or TypeError"""
buf = ustruct.pack('I', 0)
fcntl.ioctl(self.fd, FIONREAD, buf, True)
return ustruct.unpack('I', buf)[0]
@property
def is_open(self):
"""Can throw an OSError or TypeError"""
return fcntl.fcntl(self.fd, F_GETFD) == 0
def write(self, data):
if self.fd:
os.write(self.fd, data)
def read(self, size=1):
buf = b''
while self.fd and size > 0:
if not self.poller.poll(self.timeout):
break
chunk = os.read(self.fd, size)
l = len(chunk)
if l == 0: # port has disappeared
self.close()
return buf
size -= l
buf += bytes(chunk)
return buf

View File

@@ -1,142 +0,0 @@
"""Constants/functions for interpreting results of os.stat() and os.lstat().
Suggested usage: from stat import *
"""
# Indices for stat struct members in the tuple returned by os.stat()
ST_MODE = 0
ST_INO = 1
ST_DEV = 2
ST_NLINK = 3
ST_UID = 4
ST_GID = 5
ST_SIZE = 6
ST_ATIME = 7
ST_MTIME = 8
ST_CTIME = 9
# Extract bits from the mode
def S_IMODE(mode):
"""Return the portion of the file's mode that can be set by
os.chmod().
"""
return mode & 0o7777
def S_IFMT(mode):
"""Return the portion of the file's mode that describes the
file type.
"""
return mode & 0o170000
# Constants used as S_IFMT() for various file types
# (not all are implemented on all systems)
S_IFDIR = 0o040000 # directory
S_IFCHR = 0o020000 # character device
S_IFBLK = 0o060000 # block device
S_IFREG = 0o100000 # regular file
S_IFIFO = 0o010000 # fifo (named pipe)
S_IFLNK = 0o120000 # symbolic link
S_IFSOCK = 0o140000 # socket file
# Functions to test for each file type
def S_ISDIR(mode):
"""Return True if mode is from a directory."""
return S_IFMT(mode) == S_IFDIR
def S_ISCHR(mode):
"""Return True if mode is from a character special device file."""
return S_IFMT(mode) == S_IFCHR
def S_ISBLK(mode):
"""Return True if mode is from a block special device file."""
return S_IFMT(mode) == S_IFBLK
def S_ISREG(mode):
"""Return True if mode is from a regular file."""
return S_IFMT(mode) == S_IFREG
def S_ISFIFO(mode):
"""Return True if mode is from a FIFO (named pipe)."""
return S_IFMT(mode) == S_IFIFO
def S_ISLNK(mode):
"""Return True if mode is from a symbolic link."""
return S_IFMT(mode) == S_IFLNK
def S_ISSOCK(mode):
"""Return True if mode is from a socket."""
return S_IFMT(mode) == S_IFSOCK
# Names for permission bits
S_ISUID = 0o4000 # set UID bit
S_ISGID = 0o2000 # set GID bit
S_ENFMT = S_ISGID # file locking enforcement
S_ISVTX = 0o1000 # sticky bit
S_IREAD = 0o0400 # Unix V7 synonym for S_IRUSR
S_IWRITE = 0o0200 # Unix V7 synonym for S_IWUSR
S_IEXEC = 0o0100 # Unix V7 synonym for S_IXUSR
S_IRWXU = 0o0700 # mask for owner permissions
S_IRUSR = 0o0400 # read by owner
S_IWUSR = 0o0200 # write by owner
S_IXUSR = 0o0100 # execute by owner
S_IRWXG = 0o0070 # mask for group permissions
S_IRGRP = 0o0040 # read by group
S_IWGRP = 0o0020 # write by group
S_IXGRP = 0o0010 # execute by group
S_IRWXO = 0o0007 # mask for others (not in group) permissions
S_IROTH = 0o0004 # read by others
S_IWOTH = 0o0002 # write by others
S_IXOTH = 0o0001 # execute by others
# Names for file flags
UF_NODUMP = 0x00000001 # do not dump file
UF_IMMUTABLE = 0x00000002 # file may not be changed
UF_APPEND = 0x00000004 # file may only be appended to
UF_OPAQUE = 0x00000008 # directory is opaque when viewed through a union stack
UF_NOUNLINK = 0x00000010 # file may not be renamed or deleted
UF_COMPRESSED = 0x00000020 # OS X: file is hfs-compressed
UF_HIDDEN = 0x00008000 # OS X: file should not be displayed
SF_ARCHIVED = 0x00010000 # file may be archived
SF_IMMUTABLE = 0x00020000 # file may not be changed
SF_APPEND = 0x00040000 # file may only be appended to
SF_NOUNLINK = 0x00100000 # file may not be renamed or deleted
SF_SNAPSHOT = 0x00200000 # file is a snapshot file
_filemode_table = (((S_IFLNK, "l"), (S_IFREG, "-"), (S_IFBLK, "b"),
(S_IFDIR, "d"), (S_IFCHR, "c"),
(S_IFIFO, "p")), ((S_IRUSR, "r"), ), ((S_IWUSR, "w"), ),
((S_IXUSR | S_ISUID, "s"), (S_ISUID, "S"),
(S_IXUSR, "x")), ((S_IRGRP, "r"), ), ((S_IWGRP, "w"), ),
((S_IXGRP | S_ISGID, "s"), (S_ISGID, "S"),
(S_IXGRP, "x")), ((S_IROTH, "r"), ), ((S_IWOTH, "w"), ),
((S_IXOTH | S_ISVTX, "t"), (S_ISVTX, "T"), (S_IXOTH, "x")))
def filemode(mode):
"""Convert a file's mode to a string of the form '-rwxrwxrwx'."""
perm = []
for table in _filemode_table:
for bit, char in table:
if mode & bit == bit:
perm.append(char)
break
else:
perm.append("-")
return "".join(perm)

View File

@@ -1,79 +0,0 @@
from utime import *
from micropython import const
_TS_YEAR = const(0)
_TS_MON = const(1)
_TS_MDAY = const(2)
_TS_HOUR = const(3)
_TS_MIN = const(4)
_TS_SEC = const(5)
_TS_WDAY = const(6)
_TS_YDAY = const(7)
_TS_ISDST = const(8)
_WDAY = const(("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"))
_MDAY = const(
(
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
)
)
def strftime(datefmt, ts):
from io import StringIO
fmtsp = False
ftime = StringIO()
for k in datefmt:
if fmtsp:
if k == "a":
ftime.write(_WDAY[ts[_TS_WDAY]][0:3])
elif k == "A":
ftime.write(_WDAY[ts[_TS_WDAY]])
elif k == "b":
ftime.write(_MDAY[ts[_TS_MON] - 1][0:3])
elif k == "B":
ftime.write(_MDAY[ts[_TS_MON] - 1])
elif k == "d":
ftime.write("%02d" % ts[_TS_MDAY])
elif k == "H":
ftime.write("%02d" % ts[_TS_HOUR])
elif k == "I":
ftime.write("%02d" % (ts[_TS_HOUR] % 12))
elif k == "j":
ftime.write("%03d" % ts[_TS_YDAY])
elif k == "m":
ftime.write("%02d" % ts[_TS_MON])
elif k == "M":
ftime.write("%02d" % ts[_TS_MIN])
elif k == "P":
ftime.write("AM" if ts[_TS_HOUR] < 12 else "PM")
elif k == "S":
ftime.write("%02d" % ts[_TS_SEC])
elif k == "w":
ftime.write(str(ts[_TS_WDAY]))
elif k == "y":
ftime.write("%02d" % (ts[_TS_YEAR] % 100))
elif k == "Y":
ftime.write(str(ts[_TS_YEAR]))
else:
ftime.write(k)
fmtsp = False
elif k == "%":
fmtsp = True
else:
ftime.write(k)
val = ftime.getvalue()
ftime.close()
return val

View File

@@ -1,31 +0,0 @@
#!/usrdata/micropython/micropython
# Add the /usrdata/micropython directory to sys.path so we can find the external modules.
# TODO: Move external modules to lib?
# TODO: Recompile Micropython with a syspath set up for our use case.
import sys
# Remove the home directory from sys.path.
if "~/.micropython/lib" in sys.path:
sys.path.remove("~/.micropython/lib")
sys.path.append("/usrdata/micropython")
import serial
import uos
atcmd = sys.argv[1]
ser = serial.Serial("/dev/ttyOUT", baudrate=115200)
ser.write(atcmd + "\r\n")
uos.system("sleep 0.025s")
# wait for an OK
out=r''
while ser.in_waiting > 0:
out += ser.read(1)
if "OK" not in str(out):
print('Error NOT OK')
print(out.decode('utf-8'))
ser.close()

View File

@@ -21,7 +21,6 @@ MYATCMD=$(printf '%b\n' "${atcmd//%/\\x}")
if [ -n "${MYATCMD}" ]; then
x=$(urldecode "$atcmd")
runcmd=$(echo -en "$x\r\n" | microcom -t 2000 /dev/ttyOUT)
# runcmd=$(/usrdata/simpleadmin/scripts/doAT.py "$MYATCMD")
fi
echo "Content-type: text/plain"

View File

@@ -22,7 +22,7 @@ setTTL=$(printf '%b\n' "${ttlvalue//%/\\x}")
if [ -n "${setTTL}" ]; then
# Stop Service To Remove Rules
/usrdata/simpleadmin/ttl/ttl-override stop
/usrdata/simplefirewall/ttl-override stop
# Check iptables is still set
ttlcheck=$(iptables -t mangle -vnL | grep TTL | awk '{print $13}')
@@ -34,10 +34,10 @@ if [ -n "${setTTL}" ]; then
fi
# Echo TTL to file
echo $setTTL > /usrdata/simpleadmin/ttl/ttlvalue
echo $setTTL > /usrdata/simplefirewall/ttlvalue
# Set Start Service
/usrdata/simpleadmin/ttl/ttl-override start
/usrdata/simplefirewall/ttl-override start
fi

View File

@@ -5,7 +5,7 @@ DefaultDependencies=no
[Service]
Type=oneshot
ExecStart=/usrdata/simpleadmin/ttl/ttl-override start
ExecStart=/usrdata/simplefirewall/ttl-override start
User=root
[Install]

View File

@@ -4,9 +4,9 @@
# Uses ttlvalue file to read what ttl should be set to
if [ -f /usrdata/simpleadmin/ttl/ttlvalue ];
if [ -f /usrdata/simplefirewall/ttlvalue ];
then
ttlfile=$(</usrdata/simpleadmin/ttl/ttlvalue)
ttlfile=$(</usrdata/simplefirewall/ttlvalue)
TTLVALUE=$(echo $ttlfile | grep -o "[0-9]\{1,3\}")
if [ -z "${TTLVALUE}" ]; then
@@ -15,7 +15,7 @@ then
fi
else
# Couldnt find ttlvalue file, lets generate one with 0 ttlvalue (0 = disabled)
touch /usrdata/simpleadmin/ttl/ttlvalue && echo '0' > /usrdata/simpleadmin/ttl/ttlvalue
touch /usrdata/simplefirewall/ttlvalue && echo '0' > /usrdata/simplefirewall/ttlvalue
exit 1
fi

View File

@@ -3,7 +3,7 @@ Description=Socat Serial Emulation for smd7
After=ql-netd.service
[Service]
ExecStart=/usrdata/at-telnet/socat-armel-static -d -d pty,link=/dev/ttyIN,raw,echo=0 pty,link=/dev/ttyOUT,raw,echo=1
ExecStart=/usrdata/socat-at-bridge/socat-armel-static -d -d pty,link=/dev/ttyIN,raw,echo=0 pty,link=/dev/ttyOUT,raw,echo=1
# Add a delay to prevent the clients from starting too early
ExecStartPost=/bin/sleep 2s
Restart=always

View File

@@ -3,7 +3,7 @@ Description=Socat Serial Emulation for smd11
After=ql-netd.service
[Service]
ExecStart=/usrdata/at-telnet/socat-armel-static -d -d pty,link=/dev/ttyIN,raw,echo=0 pty,link=/dev/ttyOUT,raw,echo=1
ExecStart=/usrdata/socat-at-bridge/socat-armel-static -d -d pty,link=/dev/ttyIN,raw,echo=0 pty,link=/dev/ttyOUT,raw,echo=1
# Add a delay to prevent the clients from starting too early
ExecStartPost=/bin/sleep 2s
Restart=always

View File