[RndTbl] interesting unix pipe problem (stop whole pipe chain on first match)

Trevor Cordes trevor at tecnopolis.ca
Sun May 31 01:26:29 CDT 2020

For the punters out there... think you can find a better (shorter command
line? no signals?) way to solve this interesting problem?

I want to run an nmap on a network range, but have it quit (with output)
the instant it finds the first "up" host.  Nmap doesn't seem to have any
way to achieve this.  So I thought why not use unix pipes to do this?

Oh ya, let's throw on a maximum run time too.  So the goal is to run as
quickly as we can to find one host on the network (the normal case), with
the total time capped at something lower than nmap seems to want to take.

And I need a way to still tell if the whole enchilada found something or
not, and I want to wait for that result to come in.  I need to take
different action on whether we found a host or not.

Note, nmap timeout and rtt options seem to apply per-host so even with
sane-ish numbers, nmap will still often take 40-60s to run this command.

Here's what I've come up with (view in monospace):

echo 192.168.101 | \
timeout 30 bash -c \
 " \
  sint=\$(cat); \
  (nmap -ddd -vvv --send-ip \
  --host-timeout 5s --max-rtt-timeout 1s --initial-rtt-timeout 500ms \
  --max-retries 1 -S \$sint.1 -sn -PE \$sint.2 \$sint.254-254 2>&1 & \
  echo \$! >&3) \
  3>pidnmap \
  | grep --line-buffered -P 'RCVD.{0,30}?ICMP.{0,60}?Echo reply' \
  | (head -1 && kill \$(<pidnmap) 2>&1) \
" \
|grep RCVD || echo "no hosts found"

I initially thought head would sigpipe the nmap once it got it's one
line... and maybe it is, but nmap doesn't quit.  So I have to explicitly
kill the nmap later to make sure it truly dies rather than continue its
60s run.  Hence the convoluted pid/kill stuff.  I tried pkill -g0 at
first, but it ate all the output and made it impossible to tell if we got
output or not.  And using return values (ideal) seems completely hopeless
as soon as signals are involved.

And no, setting /bin/timeout to the very minimum time I'm willing to
tolerate (say 10s?) is not a good solution either, as I really want this
command to return in the < 100ms it takes in the normal case.

So, other than hacking nmap to immediately respond to sigpipe or adding a
"quit after finding X up hosts" option, does anyone see a better UNIXy way
to do this?  The general problem isn't nmap specific... it's really a
problem of shortcircuiting out of any long-running program when we see a
certain line of its output.  Heck, maybe there's already a coreutil or
moreutil that does this generally somehow, but I couldn't find it faster
than I could write the above!

My gut says it's just got to be easier than this!!  I couldn't believe how
convoluted the solution quickly became.

More information about the Roundtable mailing list