#1121520 /usr/lib/elixir/bin/iex: only understands stdin EOF if stdin is not a teletype, requires ^C/^\ if it is (fails to emulate eof in -icanon mode)

Package:
elixir
Source:
elixir
Description:
functional meta-programming aware language
Submitter:
наб
Date:
2025-11-27 17:45:02 UTC
Severity:
normal
#1121520#5
Date:
2025-11-27 17:42:21 UTC
From:
To:
Dear Maintainer,

  $ iex
  Erlang/OTP 27 [erts-15.2.7.3] [source] [64-bit] [smp:24:24] [ds:24:24:10] [async-threads:1] [jit:ns]

  Interactive Elixir (1.18.3) - press Ctrl+C to exit (type h() ENTER for help)
  iex(1)> [123,124]
  ~c"{|"
  iex(2)>
if I EOF by ^D here, nothing happens, and ^C shows an error;
only ^\ actually quits (but the process doesn't even succumb to it so
the prompt positioning is messed up because it doesn't output a final newline).

This is clearly wrong, especially given:
  $ cat | iex
  Erlang/OTP 27 [erts-15.2.7.3] [source] [64-bit] [smp:24:24] [ds:24:24:10] [async-threads:1] [jit:ns]

  Interactive Elixir (1.18.3) - press Ctrl+C to exit (type h() ENTER for help)
  iex(1)> [123,124]
  ~c"{|"
  iex(2)>
and EOFing here actually registers
(though it doesn't emit a final newline either so the prompt is also messed up),
and
  $ iex < /dev/null
  Erlang/OTP 27 [erts-15.2.7.3] [source] [64-bit] [smp:24:24] [ds:24:24:10] [async-threads:1] [jit:ns]

  Interactive Elixir (1.18.3) - press Ctrl+C to exit (type h() ENTER for help)
  $

strace of iex when getting ^D:
  [pid 2539937] ppoll([{fd=10<pipe:[760177565]>, events=POLLIN|POLLRDNORM}], 1, NULL, NULL, 8 <unfinished ...>
  [pid 2539938] <... epoll_wait resumed>[{events=EPOLLIN, data={u32=0, u64=140179142606848}}], 512, -1) = 1
  [pid 2539938] epoll_wait(4<anon_inode:[eventpoll]>,  <unfinished ...>
  [pid 2539932] read(0</dev/pts/5>, "\4", 1024) = 1
  [pid 2539932] epoll_ctl(4<anon_inode:[eventpoll]>, EPOLL_CTL_MOD, 0</dev/pts/5>, {events=EPOLLIN|EPOLLONESHOT, data={u32=0, u64=0}}) = 0
  [pid 2539932] writev(1</dev/pts/5>, [{iov_base="\7", iov_len=1}], 1) = 1

strace of cat | iex:
  [pid 2550516] ppoll([{fd=10<pipe:[760201449]>, events=POLLIN|POLLRDNORM}], 1, NULL, NULL, 8 <unfinished ...>
  [pid 2550424] <... epoll_wait resumed>[{events=EPOLLIN, data={u32=9, u64=16148037947227111433}}], 512, -1) = 1
  [pid 2550424] timerfd_settime(9<anon_inode:[timerfd]>, 0, {it_interval={tv_sec=0, tv_nsec=0}, it_value={tv_sec=0, tv_nsec=0}}, NULL) = 0
  [pid 2550424] timerfd_settime(9<anon_inode:[timerfd]>, 0, {it_interval={tv_sec=0, tv_nsec=0}, it_value={tv_sec=12, tv_nsec=58194701}}, NULL) = 0
  [pid 2550424] epoll_wait(6<anon_inode:[eventpoll]>,  <unfinished ...>
  [pid 2550517] <... epoll_wait resumed>[{events=EPOLLHUP, data={u32=0, u64=139693811302400}}], 512, -1) = 1
  [pid 2550517] epoll_wait(4<anon_inode:[eventpoll]>,  <unfinished ...>
  [pid 2550510] read(0<pipe:[760194369]>, "", 1024) = 0
  [pid 2550416] epoll_ctl(4<anon_inode:[eventpoll]>, EPOLL_CTL_DEL, 0<pipe:[760194369]>, 0x7f0d07d12474) = 0
  [pid 2550416] epoll_ctl(4<anon_inode:[eventpoll]>, EPOLL_CTL_DEL, 17<pipe:[760192190]>, 0x7f0d07d12474) = 0
  [pid 2550416] close(17<pipe:[760192190]>) = 0
  [pid 2550416] close(18<pipe:[760192190]>) = 0
  [pid 2550416] rt_sigaction(SIGCONT, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f0d543c2df0}, {sa_handler=0x55892f297a30, sa_mask=[], sa_flags=SA_RESTORER|SA_ONSTACK, sa_restorer=0x7f0d543c2df0}, 8) = 0
  [pid 2550416] rt_sigaction(SIGWINCH, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f0d543c2df0}, {sa_handler=0x55892f2979e0, sa_mask=[], sa_flags=SA_RESTORER|SA_ONSTACK, sa_restorer=0x7f0d543c2df0}, 8) = 0
  [pid 2550416] write(8<pipe:[760201448]>, "!", 1) = 1

This implies that iex puts its input teletype into -icanon -echo mode,
which is easy to confirm:
  $ stty -F /dev/pts/5
  speed 38400 baud; line = 0;
  min = 1; time = 0;
  -brkint -icrnl -imaxbel iutf8
  -opost
  -icanon -echo

Makes sense, since iex ships its own readline-style input handler
(and explains why it doesn't work that well).

iex needs to correctly understand eof/c_cc[VEOF] in its icanon emulator.

Best,