Forked init.d process becomes unresponsive - bash

I have a script to start and fork a netcat process. After a while, the netcat process stops logging output. Remote computers are supposed to connect to the socket and send a message every few hours, but it seems like the netcat process dies/halts after a while, because there's usually only one message from the remotes on the hour after the daemon starts, and then no more follow. I ensured it wasn't a problem with the remotes not sending their information to the socket; so it seems like something to do with the netcat process dying out. When I run atop, the process is still alive, but if I try to connect to the socket manually and send something, it doesn't log it to the output file.
dstart(){
if [ -f /run/mynetcat.pid ]; then
echo "Netcat instance running on "$(cat /run/mynetcat.pid)
exit 1
else
echo "Starting Netcat instance"
mkdir -p /var/log/mynetcat/
(setsid nc -l -k -p 25001 >> /var/log/mynetcat/mynetcat.log 2> /var/log/mynetcat/mynetcat.err & echo $! > /run/mynetcat.pid)&
return 0
fi
}
###later on in the script
case "$1" in
start)
dstart
;;

Related

How to close netcat connection after receive a server response?

I need to sendo a lot of messages via netcat or something similar. The problem is that when I run echo "something" | netcat ip port the connection continues opened after I received the response. Actually the connection continues opened waiting for a new input. However, what I need is that the connection closed after I receive the response. Look, my script is basically this:
#!/bin/bash
i=1
while [ $i -ne 10000 ];do
sed -n $[i]p wordlist | netcat localhost 30002 >> result
i=$[$i+1]
done
If I can close the connection after print the response in result, everything will work fine. I know that there is an option -w "x" that closes the connection after "x" seconds, but the minimum value for "x" is 1 and 1 is bigger than I can wait, I need close the connection as soon as possible.
Unfortunately, the -q flag didn't work for me.
I'm using "OpenBSD netcat (Debian patchlevel 1.187-1ubuntu0.1)" and, even though the -q flag shows up in the manual, it didn't work as mentioned in cnicutar's answer.
Therefore, my workaround was:
#!/bin/sh
# POSIX COMPLIANT
HOST="localhost"
PORT=30002
scan () {
# Ensuring there is no file named msg
rm msg
# While msg file doesn't exist or is empty, do
while [ ! -s msg ]; do
# Remove instruction from within the loop
rm msg
# Append the received messages to msg file, and put the process in the background
echo "$HOST $PORT" | xargs nc >> msg &
# If the file exists and is not empty, return, we received the message
[ -s msg ] && return;
# A small timeout.. doing some tests I noticed that a timeout of zero sometimes didn't work to catch the message
# Maybe nc needs a small time to receive everything. You might want to test and increase or decrease this timeout if needed.
sleep 0.1
# This script will be spawning a lot of nc process, to kill it before the loop runs again
pkill -x nc
done
} 2> /dev/null
scan
# The function returned, so cat the file
cat msg
# make sure nc is killed
pkill -x nc > /dev/null 2>&1
rm msg
What you're looking for is the -q switch. If you specify:
netcat -q 0 localhost 30002
netcat will exit immediately.

Socket problem on Debian 9 - How to use bash script to check

I have a service on Debian 9. I also have a bash restarter so if the service shuts down unexpectedly, the restarter will make it run again.
The restarter basically does:
if (this service is not running); then
run service
fi
The problem I have is that sometimes, after the service shuts down unexpectedly, the restarter makes it run again but I get the error:
failed to bind socket acceptor
The service is running but not really working.
What can I add on my restarter so it checks the socket is available first and then, run the service? I am trying a lot of things posted here but nothing works so far.
Your problem is probably caused by a connection in TIME_WAIT state (see e.g. here).
You can avoid this problem by using SO_REUSEADDR in the application.
If you can not alter the application you would have to check for this condition in the restarter and delay service start until the TIME_WAIT connection vanishes.
This check can be done by analyzing the output of netstat, e.g.:
while netstat -n | egrep ':5678.*:.*TIME_WAIT' >/dev/null ; do sleep 0.1 ; done
Where 5678 is your service port number.
Good luck!
EDIT> Restarter snippet with a check:
pidof service >/dev/null
PID1=$?
if [ $PID1 -eq 1 ] ; then
while netstat -n | egrep ':5678.*:.*TIME_WAIT' >/dev/null ; do sleep 1 ; done
screen -A -dmS service1 gdb --batch -x /home/server/crashreport.gdb /home/server/bin/service
fi
Note that a shorter form is possible:
if ! pidof service >/dev/null ; then
while netstat -n | egrep ':5678.*:.*TIME_WAIT' >/dev/null ; do sleep 1 ; done
screen -A -dmS service1 gdb --batch -x /home/server/crashreport.gdb /home/server/bin/service
fi
If your restarter runs this check in a loop you should give screen and gdb its time to start the service (otherwise the service could be run two times which might cause a similar error message as the one in your question...)

Test if netcat listener got a connection and run a command locally

I need a way to fire a netcat listener from a shell script and if a connection received I need to run a command on the same local listener machine and without interrupting the netcat process / connection
it's like the -e option but I need to run a command locally while keeping the netcat connection running
I don't really know if it can be done I mean after the shell process forked the netcat child can it interact with nc's output for example and run other command before netcat exit?
Edit: I figured it's even easier to do it on the client C code side by checking the return value of an initial send() message to determine if the client connected successfully if we got the sent message length
sret = send(sock, message, strlen(message), 0);
if (sret == strlen(message)) // We're Connected
do something
Thanks
This will check if the initial nc process has started listening, and it will echo every line of input it receives and will then send back a Received response:
rm -f input.txt
touch input.txt
tail -f input.txt | nc -l 5555 > output.txt &
if ! ps -p $! >/dev/null; then
echo "Netcat didn't start. Exiting..."
exit 1
fi
tail -f output.txt | while read -r LINE; do
echo "Received input: $LINE"
echo "Received" >> input.txt
done
See if you can adapt this to meet your needs.

Bash script send enter key or prevent cat hang

I am currently running a Minecraft server in a screen session with this command:
(tail -f /path/to/fifo & cat) | java -Xmx2048M -jar minecraft_server.jar nogui
You can shutdown a minecraft server by sending 'stop' in the server console. I am using the fifo to send commands from other bash scripts, and cat to allow input from the actual Minecraft server console in the screen session.
What happens though, is that if you put the command 'stop' in the actual minecraft console, the server ends up hanging right before it should exit because of the 'cat' command. The only way to get past this, is to press enter again after sending the stop command.
How can I get 'cat' to not cause this to hang?
Edit: The full script.
#!/bin/bash
serverDirectory=/opt/games/minecraft
pidFile=$serverDirectory/server.pid
fifoFile=$serverDirectory/server.fifo
cleanup() {
rm -f $pidFile
rm -f $fifoFile
}
if [ ! -p $fifoFile ]; then
mkfifo $fifoFile && chmod 0777 $fifoFile
fi
echo $$ > $pidFile
# restart server if it stops
while true
do
# how minecraft server should handle an interruption
trap "{ echo 'stop' > $fifoFile ; }" SIGINT
(tail -f $fifoFile & cat) | java -Xmx2048M -jar minecraft_server.jar nogui
echo "Restarting server...."
# if interruption occurs before we restart, stop trying to restart and clean up
trap "{ cleanup ; exit 0 ; }" SIGINT SIGTERM
sleep 5
done
I haven't used a minecraft server, so I don't know if I'm on the right track here, but would this work?
#!/bin/sh
fifo="/path/to/fifo"
mkfifo $fifo
trap "rm -f $fifo" 0 1 2 3 6 15
/path/to/java -Xmx2048M -jar minecraft_server.jar nogui < $fifo &
echo $? > /path/to/minecraft.pid
cat > $fifo
This still doesn't kill off the cat once the server quits, but at least it doesn't block the server. You might want to launch the minecraft server in a function that kills the cat when it exits. I suggest keeping the .pid file for possible future use. :-)

Starting a process over ssh using bash and then killing it on sigint

I want to start a couple of jobs on different machines using ssh. If the user then interrupts the main script I want to shut down all the jobs gracefully.
Here is a short example of what I'm trying to do:
#!/bin/bash
trap "aborted" SIGINT SIGTERM
aborted() {
kill -SIGTERM $bash2_pid
exit
}
ssh -t remote_machine /foo/bar.sh &
bash2_pid=$!
wait
However the bar.sh process is still running the remote machine. If I do the same commands in a terminal window it shuts down the process on the remote host.
Is there an easy way to make this happen when I run the bash script? Or do I need to make it log on to the remote machine, find the right process and kill it that way?
edit:
Seems like I have to go with option B, killing the remotescript through another ssh connection
So no I want to know how do I get the remotepid?
I've tried a something along the lines of :
remote_pid=$(ssh remote_machine '{ /foo/bar.sh & } ; echo $!')
This doesn't work since it blocks.
How do I wait for a variable to print and then "release" a subprocess?
It would definitely be preferable to keep your cleanup managed by the ssh that starts the process rather than moving in for the kill with a second ssh session later on.
When ssh is attached to your terminal; it behaves quite well. However, detach it from your terminal and it becomes (as you've noticed) a pain to signal or manage remote processes. You can shut down the link, but not the remote processes.
That leaves you with one option: Use the link as a way for the remote process to get notified that it needs to shut down. The cleanest way to do this is by using blocking I/O. Make the remote read input from ssh and when you want the process to shut down; send it some data so that the remote's reading operation unblocks and it can proceed with the cleanup:
command & read; kill $!
This is what we would want to run on the remote. We invoke our command that we want to run remotely; we read a line of text (blocks until we receive one) and when we're done, signal the command to terminate.
To send the signal from our local script to the remote, all we need to do now is send it a line of text. Unfortunately, Bash does not give you a lot of good options, here. At least, not if you want to be compatible with bash < 4.0.
With bash 4 we can use co-processes:
coproc ssh user#host 'command & read; kill $!'
trap 'echo >&"${COPROC[1]}"' EXIT
...
Now, when the local script exits (don't trap on INT, TERM, etc. Just EXIT) it sends a new line to the file in the second element of the COPROC array. That file is a pipe which is connected to ssh's stdin, effectively routing our line to ssh. The remote command reads the line, ends the read and kills the command.
Before bash 4 things get a bit harder since we don't have co-processes. In that case, we need to do the piping ourselves:
mkfifo /tmp/mysshcommand
ssh user#host 'command & read; kill $!' < /tmp/mysshcommand &
trap 'echo > /tmp/mysshcommand; rm /tmp/mysshcommand' EXIT
This should work in pretty much any bash version.
Try this:
ssh -tt host command </dev/null &
When you kill the local ssh process, the remote pty will close and SIGHUP will be sent to the remote process.
Referencing the answer by lhunath and https://unix.stackexchange.com/questions/71205/background-process-pipe-input I came up with this script
run.sh:
#/bin/bash
log="log"
eval "$#" \&
PID=$!
echo "running" "$#" "in PID $PID"> $log
{ (cat <&3 3<&- >/dev/null; kill $PID; echo "killed" >> $log) & } 3<&0
trap "echo EXIT >> $log" EXIT
wait $PID
The difference being that this version kills the process when the connection is closed, but also returns the exit code of the command when it runs to completion.
$ ssh localhost ./run.sh true; echo $?; cat log
0
running true in PID 19247
EXIT
$ ssh localhost ./run.sh false; echo $?; cat log
1
running false in PID 19298
EXIT
$ ssh localhost ./run.sh sleep 99; echo $?; cat log
^C130
running sleep 99 in PID 20499
killed
EXIT
$ ssh localhost ./run.sh sleep 2; echo $?; cat log
0
running sleep 2 in PID 20556
EXIT
For a one-liner:
ssh localhost "sleep 99 & PID=\$!; { (cat <&3 3<&- >/dev/null; kill \$PID) & } 3<&0; wait \$PID"
For convenience:
HUP_KILL="& PID=\$!; { (cat <&3 3<&- >/dev/null; kill \$PID) & } 3<&0; wait \$PID"
ssh localhost "sleep 99 $HUP_KILL"
Note: kill 0 may be preferred to kill $PID depending on the behavior needed with regard to spawned child processes. You can also kill -HUP or kill -INT if you desire.
Update:
A secondary job control channel is better than reading from stdin.
ssh -n -R9002:localhost:8001 -L8001:localhost:9001 localhost ./test.sh sleep 2
Set job control mode and monitor the job control channel:
set -m
trap "kill %1 %2 %3" EXIT
(sleep infinity | netcat -l 127.0.0.1 9001) &
(netcat -d 127.0.0.1 9002; kill -INT $$) &
"$#" &
wait %3
Finally, here's another approach and a reference to a bug filed on openssh:
https://bugzilla.mindrot.org/show_bug.cgi?id=396#c14
This is the best way I have found to do this. You want something on the server side that attempts to read stdin and then kills the process group when that fails, but you also want a stdin on the client side that blocks until the server side process is done and will not leave lingering processes like <(sleep infinity) might.
ssh localhost "sleep 99 < <(cat; kill -INT 0)" <&1
It doesn't actually seem to redirect stdout anywhere but it does function as a blocking input and avoids capturing keystrokes.
The solution for bash 3.2:
mkfifo /tmp/mysshcommand
ssh user#host 'command & read; kill $!' < /tmp/mysshcommand &
trap 'echo > /tmp/mysshcommand; rm /tmp/mysshcommand' EXIT
doesn't work. The ssh command is not on the ps list on the "client" machine. Only after I echo something into the pipe will it appear in the process list of the client machine. The process that appears on the "server" machine would just be the command itself, not the read/kill part.
Writing again into the pipe does not terminate the process.
So summarizing, I need to write into the pipe for the command to start up, and if I write again, it does not kill the remote command, as expected.
You may want to consider mounting the remote file system and run the script from the master box. For instance, if your kernel is compiled with fuse (can check with the following):
/sbin/lsmod | grep -i fuse
You can then mount the remote file system with the following command:
sshfs user#remote_system: mount_point
Now just run your script on the file located in mount_point.

Resources