My program has a process::Command spawned and waited. In that process is a bash command running a command that will also wait for an exit signal to stop the process. For example,
let shell = "bash";
Command::new(shell)
.arg("-c")
.arg("watch command") // watch command won't stop until it gets a Ctrl+C
.spawn()
.unwrap();
.wait()
.unwrap();
During the process is running, if I type Ctrl+C I get two questions about whether to terminate the process or not (Y/N) consequently. If I just type Ctrl+C for just a few more times, the process will exit. But when I chose to answer the question with Y or N, the terminal will break. I can't type commands, Enter keys won't work, cannot even exit from the terminal. The new commands I type will replace the current texts on the terminal, and I can even delete the terminal texts like a text editor.
I don't know what was causing the error. Am I doing something wrong? How do I add an event listener that will listen for Ctrl+C command and exit the process completely on the event?
Related
I'm trying to achieve the following:
from a fish script, open a PDF reader as a background job. Once it is opened, spawn another fish process (that runs an infinite while loop), also as a background job.
Next, open an editor (neovim) and allow it to take control of the running terminal. Once neovim terminates, also suspend the previous 2 background jobs (mupdf and the other fish process).
My current attempt looks something along the lines of:
mupdf $pdfpath &
set pid_mupdf $last_pid
fish -c "while inotifywait ...; [logic to rebuild the pdf file..]; end" &
set pid_sub $last_pid
nvim $mdpath && kill -2 $pid_mudf $pid_sub
First I open mupdf as a background job and save its PID in a variable. Next I spawn the other fish process, also as a background job, and I save its PID as well.
Next I run nvim (but not as a background job, as I intend to actually control it), and after it is terminated by the user, I gracefully kill the previous 2 background jobs.
However this doesn't work as intended.
mupdf and the second fish process open successfully, and so does nvim, but it quickly closes after around half a second, after which I get the following in the controlling terminal window: image (bote is just the filename of the script from which the lines above originate)
The 2 background processes stay running after that and I have to kill them manually.
I understand that the script is sent a SIGHUP because the controlling terminal now executes another application (neovim), but why does neovim close after that?
I also tried disowning the background processes after they're spawned but that didn't help.
How would I solve this issue?
The problem is that $last_pid, in fish 3, and %last, in fish 2, doesn't work by default in scripts. See https://github.com/fish-shell/fish-shell/issues/5036. You can "fix" this by putting status job-control full at the top of the script or using the (jobs -lp) hack that Glenn mentioned.
Regarding the background process remaining running... I can't reproduce that. It works for me. However, note that your nvim && kill will only run the kill if nvim exits with a status of zero. If you always want the kill to be run you should just unconditionally execute it. Also, your use of signal two (SIGINT) should produce the desired result but is unusual. You should use kill -15 or just omit the signal in which case it defaults to 15 (SIGTERM).
You're getting the PID incorrectly. The $pid_mudf and $pid_sub variables are empty. You want
set pid_mupdf (jobs -lp)
When I attempt to exit ruby processes, namely, specs, rails console, and binding.pry calls, there are two options: ctrl+c, ctrl+z, or if things are really stuck, open a separate tab and killall ruby. However, when I ctrl+c the first time, the terminal outputs Exiting... Interrupt again to exit immediately. but hangs permanently. If I ctrl+c again to force exit, it successfully exits. However, from that point on, I can no longer see what I'm typing into the shell. So if I type ls, the line will still appear blank, but if I hit enter, it will successfully execute the ls command.
When I ctrl+z, it manages to stop the process successfully. However, after doing this a few times, I wind up with a bunch of ruby processes running, which seem to block running new ruby processes. In this scenario, killall ruby does nothing (nor does any derivative such as looking up by pid). I have to open activity monitor (mac) and force quit each proc individually.
Any ideas how I managed to get myself into this/how to resolve it?
Killing all your Ruby instances is a shotgun approach; you might hit targets you didn't intend, so I suggest to avoid it.
When your shell doesn't show what you're typing any more you need to (blindly) enter reset to reset the terminal.
Ctrl+Z doesn't kill your process, it just get's suspended. You should get an output that tells you a job number, like:
[1] + 95295 suspended man reset
Here, 1 is the job number. You can then resume the command by typing fg %<jobnumber>, in this example fg %1. Or you can kill it with kill -9 %<jobnumber>, like kill -9 %1.
I'm trying to use a shell script to start a command. I don't care if/when/how/why it finishes. I want the process to start and run, but I want to be able to get back to my shell immediately...
You can just run the script in the background:
$ myscript &
Note that this is different from putting the & inside your script, which probably won't do what you want.
Everyone just forgot disown. So here is a summary:
& puts the job in the background.
Makes it block on attempting to read input, and
Makes the shell not wait for its completion.
disown removes the process from the shell's job control, but it still leaves it connected to the terminal.
One of the results is that the shell won't send it a SIGHUP(If the shell receives a SIGHUP, it also sends a SIGHUP to the process, which normally causes the process to terminate).
And obviously, it can only be applied to background jobs(because you cannot enter it when a foreground job is running).
nohup disconnects the process from the terminal, redirects its output to nohup.out and shields it from SIGHUP.
The process won't receive any sent SIGHUP.
Its completely independent from job control and could in principle be used also for foreground jobs(although that's not very useful).
Usually used with &(as a background job).
nohup cmd
doesn't hangup when you close the terminal. output by default goes to nohup.out
You can combine this with backgrounding,
nohup cmd &
and get rid of the output,
nohup cmd > /dev/null 2>&1 &
you can also disown a command. type cmd, Ctrl-Z, bg, disown
Alternatively, after you got the program running, you can hit Ctrl-Z which stops your program and then type
bg
which puts your last stopped program in the background. (Useful if your started something without '&' and still want it in the backgroung without restarting it)
screen -m -d $command$ starts the command in a detached session. You can use screen -r to attach to the started session. It is a wonderful tool, extremely useful also for remote sessions. Read more at man screen.
My console app traps SIGINT so it can exit gracefully.
Pressing CTRL+C inside XCode while the program is being debugged, though, has no effect.
I can find the process and use a terminal window to send a SIGINT to my process, however I'm hoping there's a simpler solution that I can do within XCode.
The pause button of the debugger console actually sends a SIGINT to your app.
If you want to make the debugger pass the signal to your app you can do the following:
Press the pause button of the debugger and wait for the debug console to gain focus
Type handle SIGINT pass and press ENTER
Press the Continue button
Now pressing again the Pause button of the debugger console of Xcode will make the SIGINT hit your app.
If you don't want the debugger to stop as soon as the SIGINT is caught you might also want to add handle SIGINT nostop to the previous list.
Update for 2017 / Xcode 8: the proper command(s) for informing lldb of your intention for handling interrupts:
process handle SIGINT -s false
process handle SIGINT -p true
In my experience, even with the above commands, the pause feature of the debugger still will interrupt the app and yield control to the debugger's stack pointer, however a pkill -2 appname at the terminal will trigger your interrupt function without any interaction with the debugger, for example:
void on_signal(int sig) {
is_interrupted = 1;
}
int main(int argc, const char * argv[]) {
signal(SIGINT, on_signal);
// ... do stuff
}
In Xcode 5+ (llvm):
Pause the process
At the (llvm) prompt, enter process signal SIGINT
Resume execution
One solution is to use the UNIX kill or killall commands.
If you know the Process ID you can open the Terminal application and type:
kill -SIGINT 415
(where 415 is the PID for this process)
Or, perhaps easier, you can type
killall -SIGINT my_console_app
(where my_console_app is the name of your app, i.e. the name of the binary created — not the bundle if it's in one)
This will send the signal to all instances of the application, as the name killall suggests, so it may not be appropriate. It might be worth checking if there are other processes with the same name running before you do a killall (;
One of the work-arounds to the hassel of working with interrupts is making a MainMenu with a CTRL-C short-cut that executes your requested routine, or sends itself a SIGINT, if you really need the sig.
Update for XCode 12+
In the modern versions of XCode it is hard to intercept SIGINT. For debug purposes it is better to use SIGINFO in place of SIGINT. You can generate that in Terminal using ^T (CTRL+T).
So during debugging temporary replace SIGINT with SIGINFO in your code. To intercept SIGINFO try next steps all together:
Go to Product > Scheme > Edit Scheme > Run > Options and change Console from Use Xcode to Use Terminal.
Run your program
Pause the program
In the Xcode console, enter this:
(lldb) process handle -p true -s false -n false SIGINFO
You will get:
NAME PASS STOP NOTIFY
=========== ===== ===== ======
SIGINFO true false false
Resume the program
Switch to Terminal and press CTRL+T.
Your signal handler will fire.
Don't forget to return SIGINT back after finishing debugging!
When calling shell scripts from Erlang, I generally need their exit status (0 or something else), so I run them using this function:
%% in module util
os_cmd_exitstatus(Action, Cmd) ->
?debug("~ts starting... Shell command: ~ts", [Action, Cmd]),
try erlang:open_port({spawn, Cmd}, [exit_status, stderr_to_stdout]) of
Port ->
os_cmd_exitstatus_loop(Action, Port)
catch
_:Reason ->
case Reason of
badarg ->
Message = "Bad input arguments";
system_limit ->
Message = "All available ports in the Erlang emulator are in use";
_ ->
Message = file:format_error(Reason)
end,
?error("~ts: shell command error: ~ts", [Action, Message]),
error
end.
os_cmd_exitstatus_loop(Action, Port) ->
receive
{Port, {data, Data}} ->
?debug("~ts... Shell output: ~ts", [Action, Data]),
os_cmd_exitstatus_loop(Action, Port);
{Port, {exit_status, 0}} ->
?info("~ts finished successfully", [Action]),
ok;
{Port, {exit_status, Status}} ->
?error("~ts failed with exit status ~p", [Action, Status]),
error;
{'EXIT', Port, Reason} ->
?error("~ts failed with port exit: reason ~ts",
[Action, file:format_error(Reason)]),
error
end.
This worked fine, until I used this to start a script which forks off a program and exits:
#!/bin/sh
FILENAME=$1
eog $FILENAME &
exit 0
(In the actual usecase, there are quite a few more arguments, and some massaging before they are passed to the program). When run from the terminal, it shows the image and exits immediately, as expected.
But running from Erlang, it doesn't. In the log file I see that it starts fine:
22/Mar/2011 13:38:30.518 Debug: Starting player starting... Shell command: /home/aromanov/workspace/gmcontroller/scripts.dummy/image/show-image.sh /home/aromanov/workspace/media/images/9e89471e-eb0b-43f8-8c12-97bbe598e7f7.png
and the eog window appears. But I don't get
22/Mar/2011 13:47:14.709 Info: Starting player finished successfully
until killing the eog process (with kill or just closing the window), which isn't suitable for my requirements. Why the difference in behavior? Is there a way to fix it?
Normally if you run a command in background with & in a shell script and the shell script terminates before the command, then the command gets orphaned. It might be that erlang trys to prevent orphaned processes in open_port and waits for eog to terminate. Normally if you want to run something in background during a shell script you should put in a wait at the end of the script to wait for your background processes to terminate. But this is exactly what youd don't want to do.
You might try the following in your shell script:
#!/bin/sh
FILENAME=$1
daemon eog $FILENAME
# exit 0 not needed: daemon returns 0 if everything is ok
If your operating system has a daemon command. I checked in FreeBSD and it has one: daemon(8)
This is not a command available on all Unix alike systems, however there might be a different command doing the same thing in your operating system.
The daemon utility detaches itself from the controlling terminal and executes the program specified by its arguments.
I'm not sure if this solves your problem, but I suspect that eog somehow stays attached to stdin/stdou as a kind of controling terminal. Worth a try anyway.
This should also solve the possible problem that job control is on erroneously which could also cause the problem. Since daemon does exit normally your shell can't try to wait for the background job on exit because there is none in the shells view.
Having said all this: why not just keep the port open in Erlang while eog runs?
Start it with:
#!/bin/sh
FILENAME=$1
exec eog $FILENAME
Calling it with exec doesn't fork it bu replaces the shell process with eog. The exit status you'll see in Erlang will then be the status of eog when it terminates. Also you have the possibility to close the port and terminate eog from Erlang if you want to do so.
Perhaps your /bin/sh doesn't support job control when it isn't run interactively? At least the /bin/sh (actually dash(1)!) on my Ubuntu system mentions:
-m monitor Turn on job control (set automatically
when interactive).
When you run the script from a terminal, the shell probably recognizes that it is being run interactively and supports job control. When you run the shell script as a port, the shell probably runs without job control.