Terminal size inside snap returns 0 length and 0 width

Hi all, I am running a cli java program inside snap that asks for the terminal length and width. Everything was great until lately(1-2 months), now when I’m building a new version of the snap and the program ask for the terminal length and width it always returns 0 for both values…

The only change in the snap was adding “base: core18” to the snapcraft.yaml because snapcraft now requires it, and building using --use-lxd or --provider=host --destructive-mode because my vm cpu doesn’t support kvm.

Someone has a clue why this is happening? @ogra @ijohnson @Igor

Thanks

Do you get any system journal denials? journalctl --no-pager -b0 --no-hostname | grep audit

1 Like

Yes,

Nov 03 08:31:36 audit[10506]: AVC apparmor="DENIED" operation="open" profile="snap.my-snap-cli.my-snap-cli" name="/proc/10506/mountinfo" pid=10506 comm="java" requested_mask="r" denied_mask="r" fsuid=0 ouid=0
Nov 03 08:31:36 audit[10506]: AVC apparmor="DENIED" operation="open" profile="snap.my-snap-cli.my-snap-cli" name="/proc/10506/coredump_filter" pid=10506 comm="java" requested_mask="wr" denied_mask="wr" fsuid=0 ouid=0
Nov 03 08:31:36 audit[10506]: AVC apparmor="DENIED" operation="open" profile="snap.my-snap-cli.my-snap-cli" name="/proc/10506/coredump_filter" pid=10506 comm="java" requested_mask="wr" denied_mask="wr" fsuid=0 ouid=0
Nov 03 08:31:36 audit[10506]: AVC apparmor="DENIED" operation="open" profile="snap.my-snap-cli.my-snap-cli" name="/home/user/" pid=10506 comm="java" requested_mask="r" denied_mask="r" fsuid=0 ouid=1000
Nov 03 08:31:36 kernel: audit: type=1400 audit(1635928296.629:281): apparmor="DENIED" operation="open" profile="snap.my-snap-cli.my-snap-cli" name="/proc/10506/mountinfo" pid=10506 comm="java" requested_mask="r" denied_mask="r" fsuid=0 ouid=0
Nov 03 08:31:36 kernel: audit: type=1400 audit(1635928296.629:282): apparmor="DENIED" operation="open" profile="snap.my-snap-cli.my-snap-cli" name="/proc/10506/coredump_filter" pid=10506 comm="java" requested_mask="wr" denied_mask="wr" fsuid=0 ouid=0
Nov 03 08:31:36 kernel: audit: type=1400 audit(1635928296.629:283): apparmor="DENIED" operation="open" profile="snap.my-snap-cli.my-snap-cli" name="/proc/10506/coredump_filter" pid=10506 comm="java" requested_mask="wr" denied_mask="wr" fsuid=0 ouid=0
Nov 03 08:31:36 kernel: audit: type=1400 audit(1635928296.629:284): apparmor="DENIED" operation="open" profile="snap.my-snap-cli.my-snap-cli" name="/home/user/" pid=10506 comm="java" requested_mask="r" denied_mask="r" fsuid=0 ouid=1000
Nov 03 08:31:37 audit[10506]: AVC apparmor="DENIED" operation="open" profile="snap.my-snap-cli.my-snap-cli" name="/proc/10506/net/if_inet6" pid=10506 comm="java" requested_mask="r" denied_mask="r" fsuid=0 ouid=0
Nov 03 08:31:37 audit[10506]: AVC apparmor="DENIED" operation="open" profile="snap.my-snap-cli.my-snap-cli" name="/proc/10506/net/ipv6_route" pid=10506 comm="java" requested_mask="r" denied_mask="r" fsuid=0 ouid=0
Nov 03 08:31:37 audit[10506]: AVC apparmor="DENIED" operation="open" profile="snap.my-snap-cli.my-snap-cli" name="/proc/10506/net/if_inet6" pid=10506 comm="java" requested_mask="r" denied_mask="r" fsuid=0 ouid=0
Nov 03 08:31:37 kernel: audit: type=1400 audit(1635928297.757:285): apparmor="DENIED" operation="open" profile="snap.my-snap-cli.my-snap-cli" name="/proc/10506/net/if_inet6" pid=10506 comm="java" requested_mask="r" denied_mask="r" fsuid=0 ouid=0
Nov 03 08:31:37 kernel: audit: type=1400 audit(1635928297.757:286): apparmor="DENIED" operation="open" profile="snap.my-snap-cli.my-snap-cli" name="/proc/10506/net/ipv6_route" pid=10506 comm="java" requested_mask="r" denied_mask="r" fsuid=0 ouid=0
Nov 03 08:31:37 kernel: audit: type=1400 audit(1635928297.757:287): apparmor="DENIED" operation="open" profile="snap.my-snap-cli.my-snap-cli" name="/proc/10506/net/if_inet6" pid=10506 comm="java" requested_mask="r" denied_mask="r" fsuid=0 ouid=0

None of the denials seem related. I briefly suspected seccomp, but I’ve checked a simple stty size command in shell of various snaps on core/core18/core20, all seem to work correctly. AFAIK stty uses TCGETSZ and TIOCGWINSZ on stdout/stderr, and the calls are not blocked. I don’t know what mechanism JRE may use, perhaps it’s some thing Java specific.

Can you run snap run --shell <yoursnap>.<yourapp> and then inside the shell run stty size ?

1 Like

If you can share what Java API you’re using to determine the terminal size, that might also help in determining the cause.

2 Likes

Running snap run --shell <yoursnap>.<yourapp> and then inside the shell run stty size returns the real values, also stty -a

The Java library is org.jline.terminal.getSize() which seems its running stty -a and parse it

When I’m running the java cli from the snap directory /snap/my_snap/current everything works fine

Any ideas?

Anything? @jamesh @mborzecki @ijohnson

AFAIU the code ends up here: https://github.com/jline/jline3/blob/b8c26ce881a641a5e25f96fbe58cb06a9a900b73/terminal-jna/src/main/java/org/jline/terminal/impl/jna/linux/LinuxNativePty.java#L93-L97 so perhaps you can run your app under strace (snap run --strace='-e ioctl -s 256 -vf' <snap>.<app>) and look for ioctl() in the log to see if that fails or the structure gets populate with reasonable data.

1 Like

Thank you. This is the result after I’m running the command:

login
login[pid  2534] --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_ACCERR, si_addr=0x7f48d9e5e008} ---
Terminal too small, please resize (current width 0 (minimal 100), current height 0 (minimal 51))
cli> [pid  2555] +++ exited with 0 +++

I discovered that the problem is much more wider… It seems that a lot of “terminal” features like change color of text, auto complete, etc has problems now… only under Snap.

Did you changed something lately in that area? @Igor @ogra @ijohnson @mborzecki @jamesh Please help here

Thanks again!

Did you try the snap run --strace=... command @mborzecki suggested? Did it show any TIOCGWINSZ ioctl calls?

1 Like

Yes, I even posted here the result… nothing relevant…

@Talzzz can you share what interfaces you have declared for your snap? Is there a simple working example I could try to recreate the problem?

1 Like

I have created a simple example as @Igor suggested and I payed attention that the infra(jline-terminal) I’ve been using throw a warning only when it’s running under snap.

WARNING: Unable to create a system terminal, creating a dumb terminal (enable debug logging for more information)

This warning can explain why I experience lack of special features, such as bold and blinking characters.

But why it “Unable to create a system terminal” under Snap? see: https://github.com/jline/jline3/blob/a01fe264d43ecdf7ab969d4a431cec3f8f840986/terminal/src/main/java/org/jline/terminal/TerminalBuilder.java

Thanks!

1 Like

Can you share your yaml, I would like to build and try here.

1 Like

Sure,

name: terminal-test-cli
version: '99999'
base: core18
summary: TEST.
description: |
  TEST
 
grade: stable
confinement: strict

parts:
  jre:
    source-type: tar
    source: ./jre-source/zulu11.33.10-sa-jre11.0.4-linux_x64.tar.gz
    plugin: dump
  test-cli:
    source-type: local
    source: ./test-cli-source
    plugin: dump
 
apps:
  terminal-test-cli:
    command: ./bin/java -DBASE_LOG_PATH="$SNAP_USER_COMMON/logs/" -jar $SNAP/test-cli.jar
    plugs: [network]

java app code:

Terminal terminal = TerminalBuilder.builder().build();
AttributedStringBuilder ansi = new AttributedStringBuilder(100);
ansi.append("\t").append(new AttributedString("RED BLINK"), AttributedStyle.DEFAULT.foreground(AttributedStyle.RED).blink());
terminal.writer().println(ansi.toAnsi(terminal));
terminal.flush();

Can you try using the standard java (openjre) to see what happens? And if you add desktop, x11 plugs?

How did you build the jar btw? I can’t fully recreate what you have as you use local sources, so if you have a fully reproducible code I could locally compile (also any special instructions you may have), that would help.

1 Like

After some debugging, I got the specific error:

WARNING: Creating a dumb terminal
java.lang.IllegalStateException: Unable to create a system terminal
        at org.jline.terminal.TerminalBuilder.doBuild(TerminalBuilder.java:219)
        at org.jline.terminal.TerminalBuilder.build(TerminalBuilder.java:172)
            at App.main(App.java:29)
        Suppressed: java.lang.NoClassDefFoundError: com/sun/jna/Platform
                at org.jline.terminal.impl.jna.JnaNativePty.current(JnaNativePty.java:39)
                at org.jline.terminal.impl.jna.JnaSupportImpl.current(JnaSupportImpl.java:15)
                at org.jline.terminal.TerminalBuilder.doBuild(TerminalBuilder.java:260)
                ... 2 more
        Caused by: java.lang.ClassNotFoundException: com.sun.jna.Platform
                at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(Unknown Source)
                at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(Unknown Source)
                at java.base/java.lang.ClassLoader.loadClass(Unknown Source)
                ... 5 more
        Suppressed: java.lang.UnsupportedOperationException
                at org.jline.terminal.impl.jansi.JansiSupportImpl.current(JansiSupportImpl.java:70)
                at org.jline.terminal.TerminalBuilder.doBuild(TerminalBuilder.java:270)
                ... 2 more
        Suppressed: java.io.IOException: Not a tty
                at org.jline.terminal.impl.ExecPty.current(ExecPty.java:44)
                at org.jline.terminal.TerminalBuilder.doBuild(TerminalBuilder.java:279)
                ... 2 more
        Caused by: java.io.IOException: Error executing 'tty': not a tty
                at org.jline.utils.ExecHelper.exec(ExecHelper.java:42)
                at org.jline.terminal.impl.ExecPty.current(ExecPty.java:41)
                ... 3 more

Also, when I’m running snap run --shell <SnapName> and run tty command I’m getting “not a tty”.

What is the reason? does it changed lately? can it be related to the --use-lxd? @jamesh @ijohnson @mborzecki @Igor

Thanks!

Solved by adding to the command < /dev/tty e.g java -jar myjar.jar < /dev/tty

Thanks everyone!

1 Like

Okay. Looking at a strace of that command under snap confinement, I see:

ioctl(0, TCGETS, {B38400 opost isig icanon echo ...}) = 0
fstat(0, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x3), ...}) = 0
readlink("/proc/self/fd/0", "/dev/pts/3", 4095) = 10
stat("/dev/pts/3", 0x7fff86cb9d20)      = -1 ENOENT (No such file or directory)
stat("/dev/pts", {st_mode=S_IFDIR|0755, st_size=0, ...}) = 0
openat(AT_FDCWD, "/dev/pts", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 3
fstat(3, {st_mode=S_IFDIR|0755, st_size=0, ...}) = 0
getdents64(3, 0x55d762e8f070 /* 3 entries */, 32768) = 72
getdents64(3, 0x55d762e8f070 /* 0 entries */, 32768) = 0
close(3)                                = 0

… and then it tries to list and stat everything in /dev. From the look of it, it is trying to find a device file that has matching stat information to the stdin file descriptor (i.e. fd number 0). It first tries to read the /proc/self/fd/0 symlink and look for a file by the matching name, and then falls back to checking everything in /dev/pts and then /dev.

It fails because the snap runs under its own pseudo-terminal namespace, so the /dev/pts/3 file can’t be found. It then reports “not a tty” even though the TCGETS ioctl succeeded (indicating that the file descriptor is in fact a terminal). The irony is that the ExecPty class doesn’t seem to care what the device file name is: it’s just interested in the error message.

From the look of it, this org.jline.terminal.impl.ExecPty class is part of a fallback code path if it can’t load one of the in-process native code implementations (seems to be “jna” or “jansi”?). Maybe it’d be worth investigating why neither of those are loading? I don’t have enough recent Java experience to be any more specific than that though.