0

This topic is mentioned multiple times everywhere but none of the solutions work for me.

I have multiple Linux host machines, they are actually docker containers, based on alpine image. I would like to open a port on host-a permanently in a background process once the host (container) is up and ready. This signal informs other machines that the host-a is ready to do something.

Parallelly host-b and host-c are checking in bash loops that remote port and waiting for this signal, then continuing.

I use nc command to open the port permanently in the background and I use nc to check whether the remote port is opened or not. But unfortunately, my solution does not work and if it works then it is unstable, and exists after the first check.

host-a: open the port permanently in the background

nc -lk -v -p "$UP_SIGNAL_PORT" &
next-command-in-my-script...

This is how host-b and host-c check the port status:

while ! nc -w 5 -z "$domain" "$UP_SIGNAL_PORT" 2>/dev/null; do
  sleep 0.5
done
continue...

It seems that the command that opens the port exists after the first "ping".

I have also tried the following command but it behaves strangely:

[host-a]# nc -lk -p 12345 &
[host-a]# ps axu
1114 root      0:00 nc -lk -p 12345

[host-b]# nc -zv hello.com 12345
hello.com (172.19.0.2:12345) open

try to check the port again
[host-b]# nc -zv hello.com 12345
NO result


[host-a]# ps axu
1114 root      0:00 nc -lk -p 12345
the process is still running BUT the port seems to be closed 

after the first remote "ping"

What is wrong here?

The nc version that I have:

# nc
BusyBox v1.35.0 (2022-11-19 10:13:10 UTC) multi-call binary.

--- UPDATE ---

As per your suggestion, I tried this, but unfortunately did not work:

[host-a]# yes hello | nc -lk -v -p 12345 >/dev/null &
[1] 1125
[root@host-a.hello.com]# listening on [::]:12345 ...

[root@host-a.hello.com]# connect to [::ffff:172.19.0.2]:12345 from host-b.hello.com.docker_default:40841 ([::ffff:172.19.0.3]:40841)
nc: too many output retries

[1]+  Exit 1                  yes hello | nc -lk -v -p 12345 > /dev/null

host-b:

[root@host-b.hello.com]# nc -zv host-a.hello.com 12345
host-a.hello.com (172.19.0.2:12345) open
[root@host-b.hello.com]# nc -zv host-a.hello.com 12345
no answer after the 1st ping

Unfortunately, this exists also after the first ping.

zappee
  • 20,148
  • 14
  • 73
  • 129
  • 1
    Try `xinetd` it's better for your task. – Ivan May 24 '23 at 13:14
  • `nc` is the wrong tool for this job (to a greater or lesser degree based on which version you have; the more appropriate ones have the option to fork a subprocess for each new incoming connection). Use a proper built-to-purpose superserver -- xinetd was the up-and-coming standard 25 years ago; today I'd suggest [`tcpsvd`](http://smarden.org/ipsvd/tcpsvd.8.html). (In some cases a systemd socket unit is appropriate, but I'm not sure you're in one of those cases). – Charles Duffy May 24 '23 at 13:25
  • You tagged this with "docker". In a Docker context, you should run `nc` (or the real server process) as a foreground process as the image's `CMD`, as the only thing running in its container; you should not normally need interactive shells in containers, nor should you need to manually launch a server in a container after it's started. – David Maze May 24 '23 at 13:36
  • Relevant: [Minimal webserver using netcat](https://stackoverflow.com/questions/16640054/minimal-web-server-using-netcat), [How to listen for multiple connections using nc](https://stackoverflow.com/questions/29728151/how-to-listen-for-multiple-tcp-connection-using-nc) – Charles Duffy May 24 '23 at 13:37
  • See the `socat` answer there -- in particular, instead of using `-`/stdin on the side opposite the listen socket, you can tell socat to fork off an arbitrary executable, so you can have a script of your choice started for each incoming connection, to listen to the request and send an appropriate response. – Charles Duffy May 24 '23 at 13:40
  • Actually, I do not have/need any response. The only one that "clients" check is whether the port is opened or not. There is no request and response body in my case. I use this as a true/false variable. It the port is opened, then continue, if not, wait and check the port status after 500 ms. – zappee May 24 '23 at 15:42
  • Hm. You may actually be _in_ a place where a systemd socket unit is the right thing, then. (Socket activation -- like the work of any other superserver -- has systemd itself keep a socket open, and then spawn a child process, passing it a pre-opened file descriptor, when there's actually traffic on the port; so you don't have the memory of running a separate process, even a trivial one like `nc`, except when there are actually connecting clients; and systemd keeps the port open no matter whether the program to handle an incoming connection is running or not). – Charles Duffy May 24 '23 at 16:23
  • But then `systemd` inside Docker tends to be somewhere between pesky and impossible. (Or, well, maybe things have improved since last time I looked.) – tripleee May 24 '23 at 16:25
  • `nc`-in-a-loop, though, is _never_ the right thing (as there's no listen socket as it circles back around to the top between invocations), and `nc` without a loop is... hard to predict enough to write good answers around it. There are at least three different commonly-distributed, independently-developed programs under the "netcat" name (one from the GNU project, one from nmap's authors, one from the BSD world), they're all a little different, and none of them are built with the immediate use case in mind. – Charles Duffy May 24 '23 at 16:26
  • @tripleee, agreed, in Docker I'd stick with tcpsvd, or just write a tiny server in Python/Go/Rust/C/whatnot. – Charles Duffy May 24 '23 at 16:26
  • @CharlesDuffy The `socat` is a really cool tool. I was able to open a port permanently with this tool using the `socat - tcp-listen:"$UP_SIGNAL_PORT",fork,reuseaddr &` command. I only neded to modify this part in my code. The port check remained untouched. If you add this as a solution then I will accept it. – zappee May 25 '23 at 22:15

1 Answers1

-2

Wrap it in a function and start it in background:

fun(){
    nc -lk -v -p "$UP_SIGNAL_PORT"
    fun
}

fun &

Or use xinetd

Ivan
  • 6,188
  • 1
  • 16
  • 23