How to set a script to execute when a port receives a message
Solution 1
Should be possible with socat
.
Write such a script "getmsg.sh" to receive one message via stdin:
#!/bin/bash
read MESSAGE
echo "PID: $$"
echo "$MESSAGE"
Then run this socat
command to invoke our script for each tcp connection on port 7777:
socat -u tcp-l:7777,fork system:./getmsg.sh
Send a test message from another shell:
echo "message 1" | netcat localhost 7777
Solution 2
The UCSPI-TCP way
There are toolsets other than netcat. Here are how to use a few of them. They all presume the existence of a service
script that runs your func
, whatever that may be:
#!/bin/sh while read -r MESSAGE do echo 1>&2 "${TCPREMOTEIP}" "${TCPREMOTEPORT}" rx "${MESSAGE}" func done
The TCPREMOTEIP
and TCPREMOTEPORT
environment variables are defined by the UCSPI-TCP protocol.
The script is spawned as an individual process per TCP connection using the various toolsets. In what follows, the tools are shown as used within a short script. Such a script, conventionally named run
, is how one would run them under a daemontools-family service manager. They can of course be invoked directly.
Bernstein ucspi-tcp
With Daniel J. Bernstein's ucspi-tcp, tcpserver
spawns the service
script:
#!/bin/sh -e exec tcpserver -v -P -R -H -l 0 0.0.0.0 7777 ./service
There are IPv6-capable enhanced versions of Bernstein ucspi-tcp. With Erwin Hoffman's, tcpserver
attempts to handle both IPv4 and IPv6 in one (if the operating system supports this, a few do not) and spawns the service
script:
#!/bin/sh -e exec tcpserver -v -P -R -H -l 0 ::0 7777 ./service
Bercot s6-networking, s6, and execline
With Laurent Bercot's s6-networking, s6-tcpserver4
and s6-tcpserver6
handle IPv4 and IPv6 separately, and spawn the service
script:
#!/command/execlineb s6-tcpserver4 -v 0.0.0.0 7777 ./service
#!/command/execlineb s6-tcpserver6 -v ::0 7777 ./service
One can build up more complex servers by interposing tools such as s6-tcpserver-access
and s6-applyuidgid
in the chain immediately before ./service
.
nosh UCSPI tools
With the nosh toolset, tcp-socket-listen
listens on the TCP socket, again handling IPv4 and IPv6 simulataneously if the operating system supports doing so, and chains to tcp-socket-accept
which in turn spawns the service
script:
#!/bin/nosh tcp-socket-listen --combine4and6 :: 7777 tcp-socket-accept --verbose --localname 0 ./service
Or one runs two separate processes, on operating systems such as OpenBSD:
#!/bin/nosh tcp-socket-listen 0.0.0.0 7777 tcp-socket-accept --verbose --localname 0 ./service
#!/bin/nosh tcp-socket-listen :: 7777 tcp-socket-accept --verbose --localname :: ./service
One can build up more complex servers by interposing tools such as ucspi-socket-rules-check
and setuidgid
in the chain.
#!/bin/nosh tcp-socket-listen --combine4and6 :: 7777 setuidgid unprivileged-user tcp-socket-accept --verbose --localname 0 ucspi-socket-rules-check --verbose ./service
Pape ipsvd
With Gerrit Pape's ipsvd, tcpsvd
spawns the service
script:
#!/bin/sh -e exec tcpsvd -v 0.0.0.0 7777 ./service
UCSPI-UDP
The common service
script can handle when standard input is a stream socket. But you didn't specify TCP explicitly.
Although some of the aforementioned toolkits can be used to build UDP servers in similar fashion to how one can use them to build TCP servers (c.f. udp-socket-listen
in nosh), it's tricky to build the actual service program with shell script, as the shell's builtins do not necessarily cope well when standard input is a datagram socket.
Further reading
- Protocol:
- Jonathan de Boyne Pollard (2016). The gen on the UNIX Client-Server Program Interface. Frequently Given Answers.
- Daniel J. Bernstein (1996). UNIX Client-Server Program Interface. cr.yp.to.
- toolsets:
- Daniel J. Bernstein. ucspi-tcp. cr.yp.to.
- s6-networking. Laurent Bercot. skarnet.org.
- s6. Laurent Bercot. skarnet.org.
- Jonathan de Boyne Pollard (2016). nosh. Softwares.
- ipsvd. Gerrit Pape. smarden.org.
- reference manuals:
- Daniel J. Bernstein. The
tcpserver
program. ucspi-tcp. - Erwin Hoffmann.
tcpserver
. ucspi-tcp6. fehcom.de. -
s6-tcpserver4
. Laurent Bercot. s6-networking. skarnet.org. -
s6-tcpserver6
. Laurent Bercot. s6-networking. skarnet.org. -
s6-tcpserver-access
. Laurent Bercot. s6-networking. skarnet.org. -
s6-applyuidgid
. Laurent Bercot. s6. skarnet.org. - Jonathan de Boyne Pollard (2016).
tcpserver
. nosh Guide. Softwares. - Jonathan de Boyne Pollard (2016).
tcp-socket-listen
. nosh Guide. Softwares. - Jonathan de Boyne Pollard (2016).
tcp-socket-accept
. nosh Guide. Softwares. - Jonathan de Boyne Pollard (2016).
ucspi-socket-rules-check
. nosh Guide. Softwares. - Jonathan de Boyne Pollard (2016).
setuidgid
. nosh Guide. Softwares. - Jonathan de Boyne Pollard (2016).
udp-socket-listen
. nosh Guide. Softwares. -
tcpsvd
. ipsvd. Gerrit Pape. smarden.org.
- Daniel J. Bernstein. The
Related videos on Youtube
![Daniel](https://lh4.googleusercontent.com/-dx2kDe-KqcU/AAAAAAAAAAI/AAAAAAAAAFE/TmKUwqA1-zQ/photo.jpg?sz=256)
Daniel
Updated on September 18, 2022Comments
-
Daniel almost 2 years
I'm wondering how to get a shell script to listen in on a certain port (maybe using netcat?). Hopefully so that when a message is sent to that port, the script records the message and then runs a function.
Example:
Computer 1 has the script running in the background, the script opened port 1234 to incoming traffic
Computer 2 sends message "hello world" to port 1234 of computer 1
Script on Computer 1 records the message "hello world" to a variable $MESSAGE
Script runs function now that variable $MESSAGE has been set
How do I go about doning this?
-
Akseli Palén over 7 yearsHave you tested it?
-
drHogan over 7 yearsRewritten and tested now ;)
-
Daniel over 7 yearsI was inspired by your solution and found a way that works with netcat: nc -l 7777 | ./getmsg.sh
-
drHogan over 7 yearsGlad to hear that. But
netcat
exists after one connection.socat
would do the same if you remove ",fork" from my command line.