I have a python script that works on Windows but am having a hard time running it on Ubuntu Desktop. Not a lot of wisdom here any tips greatly appreciated…
test.py
#!/usr/bin/env python3
"""
https://pymodbustcp.readthedocs.io/en/latest/examples/server_allow.html
An example of Modbus/TCP server which allow modbus read and/or write only from
specific IPs.
Run this as root to listen on TCP privileged ports (<= 1024).
"""
import argparse
from pyModbusTCP.server import ModbusServer, DataHandler
from pyModbusTCP.constants import EXP_ILLEGAL_FUNCTION
# some const
ALLOW_R_L = ['127.0.0.1', '192.168.0.104']
ALLOW_W_L = ['127.0.0.1']
# a custom data handler with IPs filter
class MyDataHandler(DataHandler):
def read_coils(self, address, count, srv_info):
if srv_info.client.address in ALLOW_R_L:
return super().read_coils(address, count, srv_info)
else:
return DataHandler.Return(exp_code=EXP_ILLEGAL_FUNCTION)
def read_d_inputs(self, address, count, srv_info):
if srv_info.client.address in ALLOW_R_L:
return super().read_d_inputs(address, count, srv_info)
else:
return DataHandler.Return(exp_code=EXP_ILLEGAL_FUNCTION)
def read_h_regs(self, address, count, srv_info):
if srv_info.client.address in ALLOW_R_L:
return super().read_h_regs(address, count, srv_info)
else:
return DataHandler.Return(exp_code=EXP_ILLEGAL_FUNCTION)
def read_i_regs(self, address, count, srv_info):
if srv_info.client.address in ALLOW_R_L:
return super().read_i_regs(address, count, srv_info)
else:
return DataHandler.Return(exp_code=EXP_ILLEGAL_FUNCTION)
def write_coils(self, address, bits_l, srv_info):
if srv_info.client.address in ALLOW_W_L:
return super().write_coils(address, bits_l, srv_info)
else:
return DataHandler.Return(exp_code=EXP_ILLEGAL_FUNCTION)
def write_h_regs(self, address, words_l, srv_info):
if srv_info.client.address in ALLOW_W_L:
return super().write_h_regs(address, words_l, srv_info)
else:
return DataHandler.Return(exp_code=EXP_ILLEGAL_FUNCTION)
if __name__ == '__main__':
# parse args
parser = argparse.ArgumentParser()
parser.add_argument('-H', '--host', type=str, default='localhost', help='Host (default: localhost)')
parser.add_argument('-p', '--port', type=int, default=502, help='TCP port (default: 502)')
args = parser.parse_args()
# init modbus server and start it
server = ModbusServer(host=args.host, port=args.port, data_hdl=MyDataHandler())
server.start()
When I run this from SSH I get:
Traceback (most recent call last):
File "/home/ben/anaconda3/lib/python3.7/site-packages/pyModbusTCP/server.py", line 989, in start
self._service.server_bind()
File "/home/ben/anaconda3/lib/python3.7/socketserver.py", line 466, in server_bind
self.socket.bind(self.server_address)
PermissionError: [Errno 13] Permission denied
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "test.py", line 69, in <module>
server.start()
File "/home/ben/anaconda3/lib/python3.7/site-packages/pyModbusTCP/server.py", line 992, in start
raise ModbusServer.NetworkError(e)
pyModbusTCP.server.NetworkError: [Errno 13] Permission denied
If I do a ls -la
on the directory:
total 12
drwxrwxr-x 2 ben ben 4096 Aug 7 10:57 .
drwxr-xr-x 5 ben ben 4096 Aug 7 10:50 ..
-rw-rw-r-- 1 ben ben 2522 Aug 7 10:54 test.py
2
Answers
In Unix based environments (Linux, etc.) TCP ports of 1024 and below are considered privileged. There are historical reasons for this as internal services run on low ports (i.e. sshd on port 22).
You have two ways to fix this:
bind
to port 502 which appears to be the default.-p
or--port
command line option to specify a port of 1025 or more. These ports do not require root privileges.
What user are you running the script with?
In order not to change the user to root, you could test by typing sudo at the beginning of the command to run the script. For example: