How to set a script to execute when a port receives a message

36,342

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

Share:
36,342

Related videos on Youtube

Daniel
Author by

Daniel

Updated on September 18, 2022

Comments

  • Daniel
    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:

    1. Computer 1 has the script running in the background, the script opened port 1234 to incoming traffic

    2. Computer 2 sends message "hello world" to port 1234 of computer 1

    3. Script on Computer 1 records the message "hello world" to a variable $MESSAGE

    4. Script runs function now that variable $MESSAGE has been set

    How do I go about doning this?

  • Akseli Palén
    Akseli Palén over 7 years
    Have you tested it?
  • drHogan
    drHogan over 7 years
    Rewritten and tested now ;)
  • Daniel
    Daniel over 7 years
    I was inspired by your solution and found a way that works with netcat: nc -l 7777 | ./getmsg.sh
  • drHogan
    drHogan over 7 years
    Glad to hear that. But netcat exists after one connection. socat would do the same if you remove ",fork" from my command line.