How to stop ffmpeg remotely? - linux

I'm running ffmpeg on another machine for screen capture. I'd like to be able to stop it recording remotely. FFMPEG requires that q is pressed to stop encoding as it has to do some finalization to finish the file cleanly. I know I could kill it with kill/killall however this can lead to corrupt videos.
Press [q] to stop encoding
I can't find anything on google specifically for this, but some there is suggestion that echoing into /proc//fd/0 will work.
I've tried this but it does not stop ffmpeg. The q is however shown in the terminal in which ffmpeg is running.
echo -n q > /proc/16837/fd/0
So how can I send a character to another existing process in such a way it is as if it were typed locally? Or is there another way of remotely stopping ffmpeg cleanly.

Here's a neat trick I discovered when I was faced with this problem: Make an empty file (it doesn't have to be a named pipe or anything), then write 'q' to it when it's time to stop recording.
$ touch stop
$ <./stop ffmpeg -i ... output.ext >/dev/null 2>>Capture.log &
$ wait for stopping time
$ echo 'q' > stop
FFmpeg stops as though it got 'q' from the terminal STDIN.

Newer versions of ffmpeg don't use 'q' anymore, at least on Ubuntu Oneiric, instead they say to press Ctrl+C to stop them. So with a newer version you can simply use 'killall -INT' to send them SIGINT instead of SIGTERM, and they should exit cleanly.

Elaborating on the answer from sashoalm, i have tested both scenarios, and here are the results:
My experiments shows that doing
killall --user $USER --ignore-case --signal INT ffmpeg
Produces the following on the console where ffmpeg was running
Exiting normally, received signal 2.
While doing
killall --user $USER --ignore-case --signal SIGTERM ffmpeg
Produces
Exiting normally, received signal 15.
So it looks that ffmpeg is fine with both signals.
System: Debian GNU/Linux 9 (stretch), 2020-02-28

You can also try to use "expect" to automate the execution and stop of the program. You would have to start it using some virtual shell like screen, tmux or byobu and then start the ffmpeg inside of it. This way you would be able to get again the virtual shell screen and give the "q" option.
Locally or remotely start a virtual shell session, lets say with "screen". Name the session with -S option, like screen -S recvideo Then you can start the ffmpeg as you like. You can, optionally, detach from this session with a Ctrl+a + d.
Connect to the machine where the ffmpeg is running inside the screen (or tmux or whatever) and reconnect to it: screen -d -RR recvideo and then send the "q"
To do that from inside a script you can then use expect, like:
prompt="> "
expect << EOF
set timeout 20
spawn screen -S recvideo
expect "$prompt"
send -- "ffmpeg xxxxx\r"
set timeout 1
expect eof
EOF
Then, in another moment or script point or in another script you recover it:
expect << EOF
set timeout 30
spawn screen -d -RR recvideo
expect "$prompt"
send -- "q"
expect "$prompt"
send -- "exit\r"
expect eof
EOF
You can also automate the whole ssh session with expect, passing a sequence of commands and "expects" to do what you want.

The question has already been answered for Linux, but it came up when I was looking for the windows equivalent, so I'm gonna add that to the answers:
On powershell, you start the process like this:
$((Start-Process ffmpeg -passthru -argument "FFMPEG_ARGS").ID)
This sends back the PID of the FFMPEG process that you can store in a variable, or echo, and then you send the windows equivalent of sigint (Ctrl + C) using taskkill
taskkill /pid FFMPEG_PID
I tried with Stop-Process (which is what comes up when looking how to do this on Google) but it actually kills the process. (And yes, taskkill doesn't kill it, it gently asks the process to stop... good naming :D)

Related

Run a command from a script directly to another process

I'm currently working on a script which interact with another process.
If it is relevant, the said process is a simdebug console. What I want is exiting it properly because when I kill the process itself, it creates a lock file .lck.
The Simdebug console is waiting for inputs and closes on receiving quit, then q and n, both sperated by an enter keypress to validate the command.
I managed to send some commands to the Simdebug using
echo quit > /proc/< PID >/fd/1
But it only print the results of the echo and I can't find how to send a enter keypress, only new lines '\n' .
I can't aswell manage to send a quit command which would execute directly in the Simdebug and not the terminal where it is sent from.
My question would be resolved if one of those two points is answered:
Is it possible to simulate a validate keypress as in :
Term 1 : echo ifconfig ; echo < enter keypress>
Which would then execute what's in the read buffer of the Term 2
Is there a way to already execute a commande in another process as in
Term 1 : < unknown syntax > pwd
Term 2 < shows pwd of term2 not term1>
Which would not be working only from terminal to terminal but with an already opened process in read mode.
This is actually a hard thing to do. If you send characters to the /proc/self/fd/0 device or similar stdin device link from a different master terminal then it will just output the characters to the output side of the master terminal of the other process.
With tools like expect or pdip or screen you can send anything you want to a process encapsulated in their pseudoterminals as if it comes from their master terminal. But if a process is running then it will already have it's own terminal.
You can be in luck if your console can be persuaded for a controlling terminal transfer with reptyr.
For example if your console has process id 999999 (and you have screen and reptyr installed and maybe did something to appease selinux or apparmor/yama protections):
screen -dmS automateconsole
screen -S automateconsole -p 0 -X stuff 'reptyr 999999^M'
screen -S automateconsole -p 0 -X stuff 'quit^M'
sleep 1s
screen -S automateconsole -p 0 -X stuff 'q^M'
sleep 1s
screen -S automateconsole -p 0 -X stuff 'n^M'
sleep 1s
screen -S automateconsole -p 0 -X stuff 'exit^M'
But note:
You probably should cleanup the program that init'ed the console.
On Ubuntu at least I could not reptyr processes from other SSH sessions.
https://github.com/nelhage/reptyr
http://theterminallife.com/sending-commands-into-a-screen-session/

make redis server ignore Ctrl+C when launched from shell script

I want to use shell script to launch Redis server and then monitor a log file:
#!/bin/bash
/path/to/redis/src/redis-server &
tail -f /path/to/log/logfile.log
If I run this script and press Ctrl+C from the terminal, the tail -f terminated, which is what I want, however the Redis also detected SIGINT and exited.
I tried to write the script like this:
#!/bin/bash
trap '' INT TSTP
~/redis/src/redis-server &
tail -f ./script1
This time things go even worse, the tail -f refused to terminate while Redis still detected SIGINT and exited.
It seems that there is some problems specific to Redis regarding ignoring signals.
My goal is to make tail -f responds to Ctrl+C while making Redis ignore this signal.
Please anyone tell me whether this can be achieved and if so, give me some advice?
redis-server catches SIGINT (Ctrl+C), even if SIGINT was being ignored. This is an unusual choice; most software will check and won't catch SIGINT if it's already being ignored.
When it receives SIGINT, it saves the database and shuts down.
If you start it as a service, it won't be associated with any terminal at all, and won't see any Ctrl+C you type.
If you start it as a background job in an interactive shell:
$ /path/to/redis/src/redis-server &
your shell will put it into a process group that is different from the terminal's process group, and typing Ctrl+C won't affect it. (If you bring it to the foreground with fg, Ctrl+C will send SIGINT to the program).
But, when you run a script like this:
#!/bin/bash
/path/to/redis/src/redis-server &
tail -f /path/to/log/logfile.log
the shell that runs the script will be non-interactive, and any program that it starts in the background (with &) will be in the same process group as the shell. So if you run that shell script in the foreground, typing Ctrl+C will send SIGINT to the shell, to redis-server, and to tail.
To prevent Ctrl+C from sending SIGINT to redis-server in a case like this, you need to either put redis-server in its own process group or disassociate it from your terminal. You can do this with setsid, which does both:
#!/bin/bash
setsid /path/to/redis/src/redis-server &
tail -f /path/to/log/logfile.log

How to end a process in another linux terminal

Background Info:
I'm trying to set up a remote display using a raspberry pi.
Currently I'm using fbi (frame buffer image viewer) to display the image.
The device is going to be controlled via ssh or web interface - not sure which, but definitely not from the actual device.
The Problem is I cant seem to find an easy way to "clean" quit the process remotely, clear the screen & generate no errors. The fbi program will exit if the q button is pressed but that seems to do no good over ssh. Ideally I'd prefer a less messy solution then having to look up the pid each time before killing it. I am open to the idea of using another program, however I can't run it in Xorg.
I've tried:
Grep-ing the pid and sending kill -sigterm but it either doesn't quit or doesnt clear the screen
echo "q" > /proc/[pid]/fd/0 - all the iterations i try I either get an access denied or nothing happens
Any Ideas?
How to kill a process without a message?
In one terminal, I started a process:
# sleep 100
Now to kill the process, without a message and without knowing the pid:
# kill -13 (pgrep sleep)
How to remotely clear the screen of a terminal?
First, get the tty # of the terminal you want to clear:
# tty
/dev/pts/1
Now from a different terminal:
# printf '\033c' > /dev/pts/1
Without seeing your code, a solution could be to use fbi to just display an image on fb0 from another terminal:
fbi -T 1 -noverbose -d /dev/fb0 image.png
Then just clear the entire framebuffer (fb0):
dd if=/dev/zero of=/dev/fb0
Or better yet, just write a "blank" image to fb0 to "clear" it.

How can I keep my Linux program running after I exit ssh of my non-root user?

I've searched, googled, sat in IRC for a week and even talked to a friend who is devoutly aligned with linux but I haven't yet received a solid answer.
I have written a shell script that runs as soon as I log into my non-root user and runs basically just does "./myprogram &" without quotation. When I exit shh my program times out and I am unable to connect to it until I log back in. How can I keep my program running after I exit SSH of my non-root user?
I am curious if this has to be done on the program level or what? My apologizes if this does not belong here, I am not sure where it goes to be perfectly honest.
Beside using nohup, you can run your program in terminal multiplexer like screen or tmux. With them, you can reattach to sessions, which is for example quite helpful if you need to run terminal-based interactive programs or long time running scripts over a unstable ssh connections.
boybu is a nice enhancement of screen.
Try nohup: http://linux.die.net/man/1/nohup
Likely your program receives a SIGHUP signal when you exit your ssh session.
There's two signals that can cause your program to die after your ssh session ends: SIGHUP and SIGPIPE.
SIGHUP will be sent to your program because the parent process (ssh) has died. You can get around this either by using the program nohup (i.e. nohup ./myprogram &) or by using the shell builtin disown (./myprogram& disown)
SIGPIPE will be sent to your program if it tries to write to stdout or stderr after the ssh session has been disconnected. To get around this, redirect them to a file or /dev/null, i.e. nohup ./myprogram >/dev/null 2>/dev/null &
You might also want to use the batch (or at) command, in addition to the other answers (nohup, screen, ...). And ssh has a -f option which might interest you.

How to know from a bash script if the user abruptly closes ssh session

I have a bash script that acts as the default shell for a user loging in trough ssh.
It provides a menu with several options one of wich is sending a file using netcat.
The netcat of the embedded linux I'm using lacks the -w option, so if the user closes the ssh connection without ever sending the file, the netcat command waits forever.
I need to know if the user abruptly closes the connection so the script can kill the netcat command and exit gracefully.
Things I've tried so far:
Trapping the SIGHUP: it is not issued. The only signal issued i could find is SIGCONT, but I don't think it's reliable and portable.
Playing with the -t option of the read command to detect a closed stdin: this would work if not for a silly bug in the embedded read command (only times out on the first invocation)
Edit:
I'll try to answer the questions in the comments and explain the situation further.
The code I have is:
nc -l -p 7576 > /dev/null 2>> $LOGFILE < $TMP_DIR/$BACKUP_FILE &
wait
I'm ignoring SIGINT and SIGTSTP, but I've tried to trap all the signals and the only one received is SIGCONT.
Reading the bash man page I've found out that the SIGHUP should be sent to both script and netcat and that the SIGCONT is sent to stopped jobs to ensure they receive the SIGHUP.
I guess the wait makes the script count as stopped and so it receives the SIGCONT but at the same time the wait somehow eats up the SIGHUP.
So I've tried changing the wait for a sleep and then both SIGHUP and SIGCONT are received.
The question is: why is the wait blocking the SIGHUP?
Edit 2: Solved
I solved it polling for a closed stdin with the read builtin using the -t option. To work around the bug in the read builtin I spawn it in a new bash (bash -c "read -t 3 dummy").
Does the Parent PiD change? If so you could look up the parent in the process list and make sure the process name is correct.
I have written similar applications. It would be helpful to have more of the code in your shell. I think there may be a way of writing your overall program differently which would address this issue.

Resources