Skip to content

Commit 7dd6b76

Browse files
gnodetstbischof
authored andcommitted
Fix interruption handling in nested shells
This change modifies the Shell.sh method to properly handle interruption signals in nested shells by clearing and restoring the current pipe. When a user starts a shell session, then runs the 'sh' command to create a nested shell, and then runs a command like 'ttop' in that nested shell, pressing Ctrl+C now properly interrupts the command. This complements PR #411 which made the Pipe.setCurrentPipe method public to enable this fix. Together, these changes resolve the issue reported in jline/jline3#1143 where interruption exceptions were not working for child sessions. The fix follows the same pattern implemented in the Posix.runShell method, ensuring consistent behavior across different shell creation methods.
1 parent 69b7335 commit 7dd6b76

3 files changed

Lines changed: 42 additions & 3 deletions

File tree

gogo/jline/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858
<dependency>
5959
<groupId>org.apache.felix</groupId>
6060
<artifactId>org.apache.felix.gogo.runtime</artifactId>
61-
<version>1.1.0</version>
61+
<version>1.1.7-SNAPSHOT</version>
6262
</dependency>
6363
<dependency>
6464
<groupId>org.apache.felix</groupId>

gogo/jline/src/main/java/org/apache/felix/gogo/jline/Posix.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
import java.util.stream.Collectors;
7272
import java.util.stream.Stream;
7373

74+
import org.apache.felix.gogo.runtime.Pipe;
7475
import org.apache.felix.service.command.Process;
7576
import org.apache.felix.gogo.jline.Shell.Context;
7677
import org.apache.felix.service.command.CommandProcessor;
@@ -811,6 +812,10 @@ private void startShell(CommandSession session, Terminal terminal) {
811812
private void runShell(CommandSession session, Terminal terminal) {
812813
InputStream in = terminal.input();
813814
OutputStream out = terminal.output();
815+
816+
// Save the current pipe and clear it before creating a child shell
817+
Pipe currentPipe = Pipe.setCurrentPipe(null);
818+
814819
CommandSession newSession = processor.createSession(in, out, out);
815820
newSession.put(Shell.VAR_TERMINAL, terminal);
816821
newSession.put(".tmux", session.get(".tmux"));
@@ -822,11 +827,26 @@ public void exit() throws Exception {
822827
terminal.close();
823828
}
824829
};
830+
831+
// Register a signal handler for INT signal to properly propagate interruption
832+
Terminal.SignalHandler prevIntHandler = terminal.handle(Terminal.Signal.INT, signal -> {
833+
// Propagate the interrupt to the current thread
834+
Thread.currentThread().interrupt();
835+
});
836+
825837
try {
826-
new Shell(context, processor).gosh(newSession, new String[]{"--login"});
838+
new Shell(context, processor).gosh(newSession, new String[] {"--login"});
827839
} catch (Exception e) {
828840
e.printStackTrace();
829841
} finally {
842+
// Restore the previous signal handler
843+
if (prevIntHandler != null) {
844+
terminal.handle(Terminal.Signal.INT, prevIntHandler);
845+
}
846+
847+
// Restore the previous pipe
848+
Pipe.setCurrentPipe(currentPipe);
849+
830850
try {
831851
terminal.close();
832852
} catch (IOException e) {

gogo/jline/src/main/java/org/apache/felix/gogo/jline/Shell.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import org.apache.felix.gogo.runtime.Closure;
4747
import org.apache.felix.gogo.runtime.CommandProxy;
4848
import org.apache.felix.gogo.runtime.CommandSessionImpl;
49+
import org.apache.felix.gogo.runtime.Pipe;
4950
import org.apache.felix.service.command.Job;
5051
import org.apache.felix.service.command.Job.Status;
5152
import org.apache.felix.gogo.runtime.Reflective;
@@ -440,6 +441,16 @@ private CommandSession createChildSession(CommandSession parent) {
440441

441442
private Object runShell(final CommandSession session, Terminal terminal,
442443
LineReader reader) throws InterruptedException {
444+
Pipe currentPipe = Pipe.setCurrentPipe(null);
445+
try {
446+
return doRunShell(session, terminal, reader);
447+
} finally {
448+
Pipe.setCurrentPipe(currentPipe);
449+
}
450+
}
451+
452+
private Object doRunShell(final CommandSession session, Terminal terminal,
453+
LineReader reader) throws InterruptedException {
443454
AtomicBoolean reading = new AtomicBoolean();
444455
session.setJobListener((job, previous, current) -> {
445456
if (previous == Status.Background || current == Status.Background
@@ -555,7 +566,15 @@ private String getStatusLine(Job job, int width, String status) {
555566

556567
@Descriptor("start a new shell")
557568
public Object sh(final CommandSession session, String[] argv) throws Exception {
558-
return gosh(session, argv);
569+
// Save the current pipe and clear it before creating a child shell
570+
// This ensures that interruption signals are properly propagated to the child shell
571+
Pipe currentPipe = Pipe.setCurrentPipe(null);
572+
try {
573+
return gosh(session, argv);
574+
} finally {
575+
// Restore the previous pipe
576+
Pipe.setCurrentPipe(currentPipe);
577+
}
559578
}
560579

561580
private void shutdown() throws Exception {

0 commit comments

Comments
 (0)