[v2] termios: Add tcgetwinsize and tcsetwinsize [BZ #32074]

Message ID 8ef691311db7b5178c61a297962dc2ac8eced85d.1789069182.git.ashamil435@gmail.com (mailing list archive)
State New
Headers
Series [v2] termios: Add tcgetwinsize and tcsetwinsize [BZ #32074] |

Checks

Context Check Description
redhat-pt-bot/TryBot-apply_patch success Patch applied to master at the time it was sent
linaro-tcwg-bot/tcwg_glibc_build--master-arm success Build passed
redhat-pt-bot/TryBot-32bit success Build for i686
linaro-tcwg-bot/tcwg_glibc_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_glibc_check--master-aarch64 fail Test failed
linaro-tcwg-bot/tcwg_glibc_check--master-arm success Test passed

Commit Message

Shamil Abdulaev Sept. 10, 2026, 7:40 p.m. UTC
  POSIX.1-2024 adds tcgetwinsize and tcsetwinsize to <termios.h>, together
with the struct winsize type they operate on, which glibc so far
provided only through <sys/ioctl.h>.

Move the definition of struct winsize into a new
bits/types/struct_winsize.h, include it from the four
bits/ioctl-types.h variants that used to define the structure, and
expose it from <termios.h> under __USE_MISC and __USE_XOPEN2K24, as the
BSDs and musl do.  Implement the two functions on top of the TIOCGWINSZ
and TIOCSWINSZ ioctls in sysdeps/unix, with ENOSYS stubs for other
configurations, and export them at GLIBC_2.45.

Add the functions and the structure to the conformance data for
POSIX.1-2024 and X/Open Issue 8, document them in the manual, and add
tests: declaration tests for the default, POSIX.1-2024 and X/Open
Issue 8 modes, both include orders of <termios.h> and <sys/ioctl.h>,
and a functional test on a pseudo-terminal.

Tested on x86_64-linux-gnu.

Signed-off-by: Shamil Abdulaev <ashamil435@gmail.com>
---
 NEWS                                          |  9 +-
 bits/ioctl-types.h                            | 10 +--
 conform/data/termios.h-data                   | 11 +++
 include/bits/types/struct_winsize.h           |  1 +
 manual/terminal.texi                          | 74 ++++++++++++++++
 sysdeps/generic/libc.abilist                  |  2 +
 sysdeps/mach/hurd/i386/libc.abilist           |  2 +
 sysdeps/mach/hurd/x86_64/libc.abilist         |  2 +
 sysdeps/unix/sysv/linux/aarch64/libc.abilist  |  2 +
 sysdeps/unix/sysv/linux/alpha/libc.abilist    |  2 +
 sysdeps/unix/sysv/linux/arc/libc.abilist      |  2 +
 sysdeps/unix/sysv/linux/arm/be/libc.abilist   |  2 +
 sysdeps/unix/sysv/linux/arm/le/libc.abilist   |  2 +
 sysdeps/unix/sysv/linux/bits/ioctl-types.h    |  8 +-
 sysdeps/unix/sysv/linux/csky/libc.abilist     |  2 +
 sysdeps/unix/sysv/linux/hppa/libc.abilist     |  2 +
 sysdeps/unix/sysv/linux/i386/libc.abilist     |  2 +
 .../sysv/linux/loongarch/ilp32/libc.abilist   |  2 +
 .../sysv/linux/loongarch/lp64/libc.abilist    |  2 +
 .../sysv/linux/m68k/coldfire/libc.abilist     |  2 +
 .../unix/sysv/linux/m68k/m680x0/libc.abilist  |  2 +
 .../sysv/linux/microblaze/be/libc.abilist     |  2 +
 .../sysv/linux/microblaze/le/libc.abilist     |  2 +
 .../unix/sysv/linux/mips/bits/ioctl-types.h   |  8 +-
 .../sysv/linux/mips/mips32/fpu/libc.abilist   |  2 +
 .../sysv/linux/mips/mips32/nofpu/libc.abilist |  2 +
 .../sysv/linux/mips/mips64/n32/libc.abilist   |  2 +
 .../sysv/linux/mips/mips64/n64/libc.abilist   |  2 +
 sysdeps/unix/sysv/linux/or1k/libc.abilist     |  2 +
 .../sysv/linux/powerpc/bits/ioctl-types.h     |  8 +-
 .../linux/powerpc/powerpc32/fpu/libc.abilist  |  2 +
 .../powerpc/powerpc32/nofpu/libc.abilist      |  2 +
 .../linux/powerpc/powerpc64/be/libc.abilist   |  2 +
 .../linux/powerpc/powerpc64/le/libc.abilist   |  2 +
 .../unix/sysv/linux/riscv/rv32/libc.abilist   |  2 +
 .../unix/sysv/linux/riscv/rv64/libc.abilist   |  2 +
 sysdeps/unix/sysv/linux/s390/libc.abilist     |  2 +
 sysdeps/unix/sysv/linux/sh/be/libc.abilist    |  2 +
 sysdeps/unix/sysv/linux/sh/le/libc.abilist    |  2 +
 .../sysv/linux/sparc/sparc32/libc.abilist     |  2 +
 .../sysv/linux/sparc/sparc64/libc.abilist     |  2 +
 .../unix/sysv/linux/x86_64/64/libc.abilist    |  2 +
 .../unix/sysv/linux/x86_64/x32/libc.abilist   |  2 +
 sysdeps/unix/tcgetwinsize.c                   | 27 ++++++
 sysdeps/unix/tcsetwinsize.c                   | 27 ++++++
 termios/Makefile                              | 11 +++
 termios/Versions                              |  4 +
 termios/bits/types/struct_winsize.h           | 31 +++++++
 termios/tcgetwinsize.c                        | 35 ++++++++
 termios/tcsetwinsize.c                        | 35 ++++++++
 termios/termios.h                             | 13 +++
 termios/tst-tcwinsize.c                       | 86 +++++++++++++++++++
 termios/tst-winsize-ioctl.c                   |  6 ++
 termios/tst-winsize-main.c                    | 34 ++++++++
 termios/tst-winsize-posix2024.c               |  5 ++
 termios/tst-winsize-xopen2024.c               |  5 ++
 termios/tst-winsize.c                         |  3 +
 57 files changed, 489 insertions(+), 32 deletions(-)
 create mode 100644 include/bits/types/struct_winsize.h
 create mode 100644 sysdeps/unix/tcgetwinsize.c
 create mode 100644 sysdeps/unix/tcsetwinsize.c
 create mode 100644 termios/bits/types/struct_winsize.h
 create mode 100644 termios/tcgetwinsize.c
 create mode 100644 termios/tcsetwinsize.c
 create mode 100644 termios/tst-tcwinsize.c
 create mode 100644 termios/tst-winsize-ioctl.c
 create mode 100644 termios/tst-winsize-main.c
 create mode 100644 termios/tst-winsize-posix2024.c
 create mode 100644 termios/tst-winsize-xopen2024.c
 create mode 100644 termios/tst-winsize.c
  

Patch

diff --git a/NEWS b/NEWS
index 56c6b581df..46de2690d5 100644
--- a/NEWS
+++ b/NEWS
@@ -9,11 +9,16 @@  Version 2.45
 
 Major new features:
 
-  [Add new features here]
+* The functions tcgetwinsize and tcsetwinsize from POSIX.1-2024 have been
+  added to query and change the window size of a terminal.  The struct
+  winsize type they operate on is now also defined by <termios.h>.
 
 Deprecated and removed features, and other changes affecting compatibility:
 
-  [Add deprecations, removals and changes affecting compatibility here]
+* <termios.h> now defines struct winsize and declares tcgetwinsize and
+  tcsetwinsize when _DEFAULT_SOURCE or _GNU_SOURCE is in effect, as the
+  BSDs and musl do.  Programs which include <termios.h> without
+  <sys/ioctl.h> and define their own struct winsize need to be adjusted.
 
 Changes to build and runtime requirements:
 
diff --git a/bits/ioctl-types.h b/bits/ioctl-types.h
index 8cf457b1a9..3d0a8abbae 100644
--- a/bits/ioctl-types.h
+++ b/bits/ioctl-types.h
@@ -67,15 +67,7 @@  struct sgttyb
 
 #if defined TIOCGWINSZ || defined TIOCSWINSZ
 /* Type of ARG for TIOCGWINSZ and TIOCSWINSZ requests.  */
-struct winsize
-{
-  unsigned short int ws_row;	/* Rows, in characters.  */
-  unsigned short int ws_col;	/* Columns, in characters.  */
-
-  /* These are not actually used.  */
-  unsigned short int ws_xpixel;	/* Horizontal pixels.  */
-  unsigned short int ws_ypixel;	/* Vertical pixels.  */
-};
+# include <bits/types/struct_winsize.h>
 
 # define	_IOT_winsize	/* Hurd ioctl type field.  */ \
   _IOT (_IOTS (unsigned short int), 4, 0, 0, 0, 0)
diff --git a/conform/data/termios.h-data b/conform/data/termios.h-data
index ab138835b8..66be1c46df 100644
--- a/conform/data/termios.h-data
+++ b/conform/data/termios.h-data
@@ -9,6 +9,13 @@  type {struct termios}
 type pid_t
 #endif
 
+#if defined XOPEN2K24 || defined POSIX2024
+type {struct winsize}
+
+element {struct winsize} {unsigned short int} ws_row
+element {struct winsize} {unsigned short int} ws_col
+#endif
+
 element {struct termios} tcflag_t c_iflag
 element {struct termios} tcflag_t c_oflag
 element {struct termios} tcflag_t c_cflag
@@ -147,6 +154,10 @@  function int tcgetattr (int, struct termios*)
 #if !defined POSIX && !defined XPG4
 function pid_t tcgetsid (int)
 #endif
+#if defined XOPEN2K24 || defined POSIX2024
+function int tcgetwinsize (int, struct winsize*)
+function int tcsetwinsize (int, const struct winsize*)
+#endif
 function int tcsendbreak (int, int)
 function int tcsetattr (int, int, const struct termios*)
 
diff --git a/include/bits/types/struct_winsize.h b/include/bits/types/struct_winsize.h
new file mode 100644
index 0000000000..c64b2c725a
--- /dev/null
+++ b/include/bits/types/struct_winsize.h
@@ -0,0 +1 @@ 
+#include <termios/bits/types/struct_winsize.h>
diff --git a/manual/terminal.texi b/manual/terminal.texi
index c23fc10b5c..03459b0401 100644
--- a/manual/terminal.texi
+++ b/manual/terminal.texi
@@ -24,6 +24,8 @@  descriptor is and how to open a file descriptor for a terminal device.
 * BSD Terminal Modes::          BSD compatible terminal mode setting
 * Line Control::                Sending break sequences, clearing
                                  terminal buffers @dots{}
+* Window Size::                 Querying and setting the size of a
+                                 terminal window.
 * Noncanon Example::            How to read single characters without echo.
 * getpass::                     Prompting the user for a passphrase.
 * Pseudo-Terminals::            How to open a pseudo-terminal.
@@ -2134,6 +2136,78 @@  A bad value was supplied as the @var{action} argument.
 @end table
 @end deftypefun
 
+@node Window Size
+@section Window Size
+@cindex terminal window size
+@cindex window size of a terminal
+
+These functions query and change the size of the window associated with a
+terminal device.  They are declared in the header file @file{termios.h}.
+@pindex termios.h
+
+@deftp {Data Type} {struct winsize}
+@standards{POSIX.1-2024, termios.h}
+This structure describes the size of a terminal window.  It contains at
+least the following members:
+
+@table @code
+@item unsigned short int ws_row
+The number of rows, in characters.
+
+@item unsigned short int ws_col
+The number of columns, in characters.
+
+@item unsigned short int ws_xpixel
+The width of the window, in pixels.  Many terminals report this as
+@math{0}.
+
+@item unsigned short int ws_ypixel
+The height of the window, in pixels.  Many terminals report this as
+@math{0}.
+@end table
+@end deftp
+
+@deftypefun int tcgetwinsize (int @var{filedes}, struct winsize *@var{winsize-p})
+@standards{POSIX.1-2024, termios.h}
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
+This function stores the current window size of the terminal device with
+file descriptor @var{filedes} in the structure that @var{winsize-p}
+points to.
+
+If successful, @code{tcgetwinsize} returns @math{0}.  A return value of
+@math{-1} indicates an error.  The following @code{errno} error
+conditions are defined for this function:
+
+@table @code
+@item EBADF
+The @var{filedes} argument is not a valid file descriptor.
+
+@item ENOTTY
+The @var{filedes} is not associated with a terminal.
+@end table
+@end deftypefun
+
+@deftypefun int tcsetwinsize (int @var{filedes}, const struct winsize *@var{winsize-p})
+@standards{POSIX.1-2024, termios.h}
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
+This function sets the window size of the terminal device with file
+descriptor @var{filedes} from the structure that @var{winsize-p} points
+to.  If the size changes, the foreground process group of the terminal
+is sent a @code{SIGWINCH} signal.  @xref{Miscellaneous Signals}.
+
+If successful, @code{tcsetwinsize} returns @math{0}.  A return value of
+@math{-1} indicates an error.  The following @code{errno} error
+conditions are defined for this function:
+
+@table @code
+@item EBADF
+The @var{filedes} argument is not a valid file descriptor.
+
+@item ENOTTY
+The @var{filedes} is not associated with a terminal.
+@end table
+@end deftypefun
+
 @node Noncanon Example
 @section Noncanonical Mode Example
 
diff --git a/sysdeps/generic/libc.abilist b/sysdeps/generic/libc.abilist
index baeb89089a..69d9989bed 100644
--- a/sysdeps/generic/libc.abilist
+++ b/sysdeps/generic/libc.abilist
@@ -1 +1,3 @@ 
 GLIBC_2.36 pidfd_getfd F
+GLIBC_2.45 tcgetwinsize F
+GLIBC_2.45 tcsetwinsize F
diff --git a/sysdeps/mach/hurd/i386/libc.abilist b/sysdeps/mach/hurd/i386/libc.abilist
index 0166703bdb..acfb946e5f 100644
--- a/sysdeps/mach/hurd/i386/libc.abilist
+++ b/sysdeps/mach/hurd/i386/libc.abilist
@@ -2835,6 +2835,8 @@  GLIBC_2.44 timer_delete F
 GLIBC_2.44 timer_getoverrun F
 GLIBC_2.44 timer_gettime F
 GLIBC_2.44 timer_settime F
+GLIBC_2.45 tcgetwinsize F
+GLIBC_2.45 tcsetwinsize F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
 GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/mach/hurd/x86_64/libc.abilist b/sysdeps/mach/hurd/x86_64/libc.abilist
index 0262a079aa..8bee520c79 100644
--- a/sysdeps/mach/hurd/x86_64/libc.abilist
+++ b/sysdeps/mach/hurd/x86_64/libc.abilist
@@ -2511,6 +2511,8 @@  GLIBC_2.44 timer_delete F
 GLIBC_2.44 timer_getoverrun F
 GLIBC_2.44 timer_gettime F
 GLIBC_2.44 timer_settime F
+GLIBC_2.45 tcgetwinsize F
+GLIBC_2.45 tcsetwinsize F
 HURD_CTHREADS_0.3 __mutex_init F
 HURD_CTHREADS_0.3 __mutex_lock F
 HURD_CTHREADS_0.3 __mutex_trylock F
diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
index 3156688add..03c054e2e4 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2775,3 +2775,5 @@  GLIBC_2.43 memset_explicit F
 GLIBC_2.43 mseal F
 GLIBC_2.43 openat2 F
 GLIBC_2.43 umaxabs F
+GLIBC_2.45 tcgetwinsize F
+GLIBC_2.45 tcsetwinsize F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index 8af5b0b581..56dd5fb113 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -3122,6 +3122,8 @@  GLIBC_2.43 memset_explicit F
 GLIBC_2.43 mseal F
 GLIBC_2.43 openat2 F
 GLIBC_2.43 umaxabs F
+GLIBC_2.45 tcgetwinsize F
+GLIBC_2.45 tcsetwinsize F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
 GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/unix/sysv/linux/arc/libc.abilist b/sysdeps/unix/sysv/linux/arc/libc.abilist
index 35fcef2cc4..01dd08bc3a 100644
--- a/sysdeps/unix/sysv/linux/arc/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arc/libc.abilist
@@ -2536,3 +2536,5 @@  GLIBC_2.43 memset_explicit F
 GLIBC_2.43 mseal F
 GLIBC_2.43 openat2 F
 GLIBC_2.43 umaxabs F
+GLIBC_2.45 tcgetwinsize F
+GLIBC_2.45 tcsetwinsize F
diff --git a/sysdeps/unix/sysv/linux/arm/be/libc.abilist b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
index a6c6b951bf..469a486482 100644
--- a/sysdeps/unix/sysv/linux/arm/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
@@ -2828,6 +2828,8 @@  GLIBC_2.43 memset_explicit F
 GLIBC_2.43 mseal F
 GLIBC_2.43 openat2 F
 GLIBC_2.43 umaxabs F
+GLIBC_2.45 tcgetwinsize F
+GLIBC_2.45 tcsetwinsize F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
 GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/unix/sysv/linux/arm/le/libc.abilist b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
index e76015fe66..6102a0c40f 100644
--- a/sysdeps/unix/sysv/linux/arm/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
@@ -2825,6 +2825,8 @@  GLIBC_2.43 memset_explicit F
 GLIBC_2.43 mseal F
 GLIBC_2.43 openat2 F
 GLIBC_2.43 umaxabs F
+GLIBC_2.45 tcgetwinsize F
+GLIBC_2.45 tcsetwinsize F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
 GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/unix/sysv/linux/bits/ioctl-types.h b/sysdeps/unix/sysv/linux/bits/ioctl-types.h
index 0278ea0b37..4c32a32f27 100644
--- a/sysdeps/unix/sysv/linux/bits/ioctl-types.h
+++ b/sysdeps/unix/sysv/linux/bits/ioctl-types.h
@@ -24,13 +24,7 @@ 
 #include <asm/ioctls.h>
 
 
-struct winsize
-  {
-    unsigned short int ws_row;
-    unsigned short int ws_col;
-    unsigned short int ws_xpixel;
-    unsigned short int ws_ypixel;
-  };
+#include <bits/types/struct_winsize.h>
 
 /* modem lines */
 #define TIOCM_LE	0x001
diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist
index 1fb7cdcad5..07bb85401b 100644
--- a/sysdeps/unix/sysv/linux/csky/libc.abilist
+++ b/sysdeps/unix/sysv/linux/csky/libc.abilist
@@ -2812,3 +2812,5 @@  GLIBC_2.43 memset_explicit F
 GLIBC_2.43 mseal F
 GLIBC_2.43 openat2 F
 GLIBC_2.43 umaxabs F
+GLIBC_2.45 tcgetwinsize F
+GLIBC_2.45 tcsetwinsize F
diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
index 0710ccecf9..8be927b303 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -2849,6 +2849,8 @@  GLIBC_2.43 memset_explicit F
 GLIBC_2.43 mseal F
 GLIBC_2.43 openat2 F
 GLIBC_2.43 umaxabs F
+GLIBC_2.45 tcgetwinsize F
+GLIBC_2.45 tcsetwinsize F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
 GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index 3afe3a88eb..35ef766bdc 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -3032,6 +3032,8 @@  GLIBC_2.43 memset_explicit F
 GLIBC_2.43 mseal F
 GLIBC_2.43 openat2 F
 GLIBC_2.43 umaxabs F
+GLIBC_2.45 tcgetwinsize F
+GLIBC_2.45 tcsetwinsize F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
 GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/unix/sysv/linux/loongarch/ilp32/libc.abilist b/sysdeps/unix/sysv/linux/loongarch/ilp32/libc.abilist
index 7c6d7055c3..14c5b3012b 100644
--- a/sysdeps/unix/sysv/linux/loongarch/ilp32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/loongarch/ilp32/libc.abilist
@@ -2287,3 +2287,5 @@  GLIBC_2.44 wprintf F
 GLIBC_2.44 write F
 GLIBC_2.44 writev F
 GLIBC_2.44 wscanf F
+GLIBC_2.45 tcgetwinsize F
+GLIBC_2.45 tcsetwinsize F
diff --git a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
index c2b3a66d3a..2fdb602c8e 100644
--- a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
@@ -2296,3 +2296,5 @@  GLIBC_2.43 memset_explicit F
 GLIBC_2.43 mseal F
 GLIBC_2.43 openat2 F
 GLIBC_2.43 umaxabs F
+GLIBC_2.45 tcgetwinsize F
+GLIBC_2.45 tcsetwinsize F
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index d6855131e8..882960b014 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -2808,6 +2808,8 @@  GLIBC_2.43 memset_explicit F
 GLIBC_2.43 mseal F
 GLIBC_2.43 openat2 F
 GLIBC_2.43 umaxabs F
+GLIBC_2.45 tcgetwinsize F
+GLIBC_2.45 tcsetwinsize F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
 GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
index 4e3fe9c42f..935a5d0d5f 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -2975,6 +2975,8 @@  GLIBC_2.43 memset_explicit F
 GLIBC_2.43 mseal F
 GLIBC_2.43 openat2 F
 GLIBC_2.43 umaxabs F
+GLIBC_2.45 tcgetwinsize F
+GLIBC_2.45 tcsetwinsize F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
 GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
index 29f0c5f954..7de76ddfbb 100644
--- a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
@@ -2861,3 +2861,5 @@  GLIBC_2.43 memset_explicit F
 GLIBC_2.43 mseal F
 GLIBC_2.43 openat2 F
 GLIBC_2.43 umaxabs F
+GLIBC_2.45 tcgetwinsize F
+GLIBC_2.45 tcsetwinsize F
diff --git a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
index 2ef62838f7..a2467cfdf5 100644
--- a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
@@ -2858,3 +2858,5 @@  GLIBC_2.43 memset_explicit F
 GLIBC_2.43 mseal F
 GLIBC_2.43 openat2 F
 GLIBC_2.43 umaxabs F
+GLIBC_2.45 tcgetwinsize F
+GLIBC_2.45 tcsetwinsize F
diff --git a/sysdeps/unix/sysv/linux/mips/bits/ioctl-types.h b/sysdeps/unix/sysv/linux/mips/bits/ioctl-types.h
index 7e98525391..1be4346357 100644
--- a/sysdeps/unix/sysv/linux/mips/bits/ioctl-types.h
+++ b/sysdeps/unix/sysv/linux/mips/bits/ioctl-types.h
@@ -23,13 +23,7 @@ 
 /* Get definition of constants for use with `ioctl'.  */
 #include <asm/ioctls.h>
 
-struct winsize
-  {
-    unsigned short int ws_row;
-    unsigned short int ws_col;
-    unsigned short int ws_xpixel;
-    unsigned short int ws_ypixel;
-  };
+#include <bits/types/struct_winsize.h>
 
 /* modem lines */
 #define TIOCM_LE	0x001		/* line enable */
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
index 031e8961ac..cbd7ee76b2 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -2938,6 +2938,8 @@  GLIBC_2.43 memset_explicit F
 GLIBC_2.43 mseal F
 GLIBC_2.43 openat2 F
 GLIBC_2.43 umaxabs F
+GLIBC_2.45 tcgetwinsize F
+GLIBC_2.45 tcsetwinsize F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
 GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
index 8dc99d81b4..5a3cdb0bcb 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -2936,6 +2936,8 @@  GLIBC_2.43 memset_explicit F
 GLIBC_2.43 mseal F
 GLIBC_2.43 openat2 F
 GLIBC_2.43 umaxabs F
+GLIBC_2.45 tcgetwinsize F
+GLIBC_2.45 tcsetwinsize F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
 GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
index 054c5b6391..68c1d1b349 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -2944,6 +2944,8 @@  GLIBC_2.43 memset_explicit F
 GLIBC_2.43 mseal F
 GLIBC_2.43 openat2 F
 GLIBC_2.43 umaxabs F
+GLIBC_2.45 tcgetwinsize F
+GLIBC_2.45 tcsetwinsize F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
 GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
index 13f0148bc0..810f99d58e 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -2846,6 +2846,8 @@  GLIBC_2.43 memset_explicit F
 GLIBC_2.43 mseal F
 GLIBC_2.43 openat2 F
 GLIBC_2.43 umaxabs F
+GLIBC_2.45 tcgetwinsize F
+GLIBC_2.45 tcsetwinsize F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
 GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/unix/sysv/linux/or1k/libc.abilist b/sysdeps/unix/sysv/linux/or1k/libc.abilist
index e7ffe07dd8..39a9d45814 100644
--- a/sysdeps/unix/sysv/linux/or1k/libc.abilist
+++ b/sysdeps/unix/sysv/linux/or1k/libc.abilist
@@ -2286,3 +2286,5 @@  GLIBC_2.43 memset_explicit F
 GLIBC_2.43 mseal F
 GLIBC_2.43 openat2 F
 GLIBC_2.43 umaxabs F
+GLIBC_2.45 tcgetwinsize F
+GLIBC_2.45 tcsetwinsize F
diff --git a/sysdeps/unix/sysv/linux/powerpc/bits/ioctl-types.h b/sysdeps/unix/sysv/linux/powerpc/bits/ioctl-types.h
index d6f33e03e0..7f46bf92ed 100644
--- a/sysdeps/unix/sysv/linux/powerpc/bits/ioctl-types.h
+++ b/sysdeps/unix/sysv/linux/powerpc/bits/ioctl-types.h
@@ -24,13 +24,7 @@ 
 #include <asm/ioctls.h>
 
 
-struct winsize
-  {
-    unsigned short int ws_row;
-    unsigned short int ws_col;
-    unsigned short int ws_xpixel;
-    unsigned short int ws_ypixel;
-  };
+#include <bits/types/struct_winsize.h>
 
 /* modem lines */
 #define TIOCM_LE	0x001
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
index dea4b20f05..5f9a2e3f38 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -3165,6 +3165,8 @@  GLIBC_2.43 memset_explicit F
 GLIBC_2.43 mseal F
 GLIBC_2.43 openat2 F
 GLIBC_2.43 umaxabs F
+GLIBC_2.45 tcgetwinsize F
+GLIBC_2.45 tcsetwinsize F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
 GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
index b45e127463..b85c4d8cc0 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -3210,6 +3210,8 @@  GLIBC_2.43 memset_explicit F
 GLIBC_2.43 mseal F
 GLIBC_2.43 openat2 F
 GLIBC_2.43 umaxabs F
+GLIBC_2.45 tcgetwinsize F
+GLIBC_2.45 tcsetwinsize F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
 GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
index 942cf6a027..ddb6f43837 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
@@ -2919,6 +2919,8 @@  GLIBC_2.43 memset_explicit F
 GLIBC_2.43 mseal F
 GLIBC_2.43 openat2 F
 GLIBC_2.43 umaxabs F
+GLIBC_2.45 tcgetwinsize F
+GLIBC_2.45 tcsetwinsize F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
 GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
index 65d78e5076..070b947ead 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
@@ -2995,3 +2995,5 @@  GLIBC_2.43 memset_explicit F
 GLIBC_2.43 mseal F
 GLIBC_2.43 openat2 F
 GLIBC_2.43 umaxabs F
+GLIBC_2.45 tcgetwinsize F
+GLIBC_2.45 tcsetwinsize F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
index dcab30d72e..6b228fcec5 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
@@ -2539,3 +2539,5 @@  GLIBC_2.43 memset_explicit F
 GLIBC_2.43 mseal F
 GLIBC_2.43 openat2 F
 GLIBC_2.43 umaxabs F
+GLIBC_2.45 tcgetwinsize F
+GLIBC_2.45 tcsetwinsize F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
index 796ef35e26..1fcea246f8 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
@@ -2739,3 +2739,5 @@  GLIBC_2.43 memset_explicit F
 GLIBC_2.43 mseal F
 GLIBC_2.43 openat2 F
 GLIBC_2.43 umaxabs F
+GLIBC_2.45 tcgetwinsize F
+GLIBC_2.45 tcsetwinsize F
diff --git a/sysdeps/unix/sysv/linux/s390/libc.abilist b/sysdeps/unix/sysv/linux/s390/libc.abilist
index 8f2350ee0b..dcc60f2178 100644
--- a/sysdeps/unix/sysv/linux/s390/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/libc.abilist
@@ -2956,6 +2956,8 @@  GLIBC_2.43 memset_explicit F
 GLIBC_2.43 mseal F
 GLIBC_2.43 openat2 F
 GLIBC_2.43 umaxabs F
+GLIBC_2.45 tcgetwinsize F
+GLIBC_2.45 tcsetwinsize F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
 GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/unix/sysv/linux/sh/be/libc.abilist b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
index 7aa98c5aed..106d9ceafa 100644
--- a/sysdeps/unix/sysv/linux/sh/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
@@ -2855,6 +2855,8 @@  GLIBC_2.43 memset_explicit F
 GLIBC_2.43 mseal F
 GLIBC_2.43 openat2 F
 GLIBC_2.43 umaxabs F
+GLIBC_2.45 tcgetwinsize F
+GLIBC_2.45 tcsetwinsize F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
 GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/unix/sysv/linux/sh/le/libc.abilist b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
index 6bd4f8f63a..bee849dfc1 100644
--- a/sysdeps/unix/sysv/linux/sh/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
@@ -2852,6 +2852,8 @@  GLIBC_2.43 memset_explicit F
 GLIBC_2.43 mseal F
 GLIBC_2.43 openat2 F
 GLIBC_2.43 umaxabs F
+GLIBC_2.45 tcgetwinsize F
+GLIBC_2.45 tcsetwinsize F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
 GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
index b52cab2a35..697dadeb06 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -3186,6 +3186,8 @@  GLIBC_2.43 memset_explicit F
 GLIBC_2.43 mseal F
 GLIBC_2.43 openat2 F
 GLIBC_2.43 umaxabs F
+GLIBC_2.45 tcgetwinsize F
+GLIBC_2.45 tcsetwinsize F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
 GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
index ff99cd4f21..8a39d9f6f2 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -2822,6 +2822,8 @@  GLIBC_2.43 memset_explicit F
 GLIBC_2.43 mseal F
 GLIBC_2.43 openat2 F
 GLIBC_2.43 umaxabs F
+GLIBC_2.45 tcgetwinsize F
+GLIBC_2.45 tcsetwinsize F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
 GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index 306cd627fd..c9658cd473 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -2771,6 +2771,8 @@  GLIBC_2.43 memset_explicit F
 GLIBC_2.43 mseal F
 GLIBC_2.43 openat2 F
 GLIBC_2.43 umaxabs F
+GLIBC_2.45 tcgetwinsize F
+GLIBC_2.45 tcsetwinsize F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
 GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index 8b9c448742..0458177a88 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2790,3 +2790,5 @@  GLIBC_2.43 memset_explicit F
 GLIBC_2.43 mseal F
 GLIBC_2.43 openat2 F
 GLIBC_2.43 umaxabs F
+GLIBC_2.45 tcgetwinsize F
+GLIBC_2.45 tcsetwinsize F
diff --git a/sysdeps/unix/tcgetwinsize.c b/sysdeps/unix/tcgetwinsize.c
new file mode 100644
index 0000000000..1aea64b02c
--- /dev/null
+++ b/sysdeps/unix/tcgetwinsize.c
@@ -0,0 +1,27 @@ 
+/* Get the size of a terminal window.  Unix version.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <sys/ioctl.h>
+#include <termios.h>
+
+/* Store the window size of the terminal FD in *WINP.  */
+int
+tcgetwinsize (int fd, struct winsize *winp)
+{
+  return __ioctl (fd, TIOCGWINSZ, winp);
+}
diff --git a/sysdeps/unix/tcsetwinsize.c b/sysdeps/unix/tcsetwinsize.c
new file mode 100644
index 0000000000..31940031d4
--- /dev/null
+++ b/sysdeps/unix/tcsetwinsize.c
@@ -0,0 +1,27 @@ 
+/* Set the size of a terminal window.  Unix version.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <sys/ioctl.h>
+#include <termios.h>
+
+/* Set the window size of the terminal FD from *WINP.  */
+int
+tcsetwinsize (int fd, const struct winsize *winp)
+{
+  return __ioctl (fd, TIOCSWINSZ, winp);
+}
diff --git a/termios/Makefile b/termios/Makefile
index 6dd125576c..df780447c2 100644
--- a/termios/Makefile
+++ b/termios/Makefile
@@ -25,6 +25,7 @@  include ../Makeconfig
 headers	:= \
   bits/termios-baud.h \
   bits/termios.h \
+  bits/types/struct_winsize.h \
   sys/termios.h \
   sys/ttychars.h \
   sys/ttydefaults.h \
@@ -45,11 +46,21 @@  routines := \
   tcgetattr \
   tcgetpgrp \
   tcgetsid \
+  tcgetwinsize \
   tcsendbrk \
   tcsetattr \
   tcsetpgrp \
+  tcsetwinsize \
   # routines
 
+tests := \
+  tst-tcwinsize \
+  tst-winsize \
+  tst-winsize-ioctl \
+  tst-winsize-posix2024 \
+  tst-winsize-xopen2024 \
+  # tests
+
 include ../Rules
 
 CFLAGS-tcdrain.c += -fexceptions -fasynchronous-unwind-tables
diff --git a/termios/Versions b/termios/Versions
index 03e2063b1f..f0741cfbc8 100644
--- a/termios/Versions
+++ b/termios/Versions
@@ -18,4 +18,8 @@  libc {
     # cf*baud
     cfgetibaud; cfgetobaud; cfsetibaud; cfsetobaud; cfsetbaud;
   }
+  GLIBC_2.45 {
+    # t*
+    tcgetwinsize; tcsetwinsize;
+  }
 }
diff --git a/termios/bits/types/struct_winsize.h b/termios/bits/types/struct_winsize.h
new file mode 100644
index 0000000000..5791bc1c50
--- /dev/null
+++ b/termios/bits/types/struct_winsize.h
@@ -0,0 +1,31 @@ 
+/* Define struct winsize.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#ifndef __winsize_defined
+#define __winsize_defined 1
+
+/* Size of a terminal window.  */
+struct winsize
+  {
+    unsigned short int ws_row;		/* Rows, in characters.  */
+    unsigned short int ws_col;		/* Columns, in characters.  */
+    unsigned short int ws_xpixel;	/* Horizontal pixels.  */
+    unsigned short int ws_ypixel;	/* Vertical pixels.  */
+  };
+
+#endif
diff --git a/termios/tcgetwinsize.c b/termios/tcgetwinsize.c
new file mode 100644
index 0000000000..2b52f8fb9b
--- /dev/null
+++ b/termios/tcgetwinsize.c
@@ -0,0 +1,35 @@ 
+/* Get the size of a terminal window.  Generic version.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <termios.h>
+
+/* Store the window size of the terminal FD in *WINP.  */
+int
+tcgetwinsize (int fd, struct winsize *winp)
+{
+  if (fd < 0)
+    {
+      __set_errno (EBADF);
+      return -1;
+    }
+
+  __set_errno (ENOSYS);
+  return -1;
+}
+stub_warning (tcgetwinsize)
diff --git a/termios/tcsetwinsize.c b/termios/tcsetwinsize.c
new file mode 100644
index 0000000000..65a270a3e0
--- /dev/null
+++ b/termios/tcsetwinsize.c
@@ -0,0 +1,35 @@ 
+/* Set the size of a terminal window.  Generic version.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <termios.h>
+
+/* Set the window size of the terminal FD from *WINP.  */
+int
+tcsetwinsize (int fd, const struct winsize *winp)
+{
+  if (fd < 0)
+    {
+      __set_errno (EBADF);
+      return -1;
+    }
+
+  __set_errno (ENOSYS);
+  return -1;
+}
+stub_warning (tcsetwinsize)
diff --git a/termios/termios.h b/termios/termios.h
index fb38534413..dfeeae27dc 100644
--- a/termios/termios.h
+++ b/termios/termios.h
@@ -38,6 +38,11 @@  __BEGIN_DECLS
    `cc_t', `speed_t', and all the macros specifying the flag bits.  */
 #include <bits/termios.h>
 
+#if defined __USE_MISC || defined __USE_XOPEN2K24
+/* Get the definition of `struct winsize'.  */
+# include <bits/types/struct_winsize.h>
+#endif
+
 #ifdef __USE_MISC
 /* Compare a character C to a value VAL from the `c_cc' array in a
    `struct termios'.  If VAL is _POSIX_VDISABLE, no character can match it.  */
@@ -119,6 +124,14 @@  extern int tcflow (int __fd, int __action) __THROW;
 extern __pid_t tcgetsid (int __fd) __THROW;
 #endif
 
+#if defined __USE_MISC || defined __USE_XOPEN2K24
+/* Store the window size of the terminal FD in *WINP.  */
+extern int tcgetwinsize (int __fd, struct winsize *__winp) __THROW;
+
+/* Set the window size of the terminal FD from *WINP.  */
+extern int tcsetwinsize (int __fd, const struct winsize *__winp) __THROW;
+#endif
+
 
 #ifdef __USE_MISC
 # include <sys/ttydefaults.h>
diff --git a/termios/tst-tcwinsize.c b/termios/tst-tcwinsize.c
new file mode 100644
index 0000000000..e0be180f84
--- /dev/null
+++ b/termios/tst-tcwinsize.c
@@ -0,0 +1,86 @@ 
+/* Check tcgetwinsize and tcsetwinsize on a pseudo-terminal (bug 32074).
+   Copyright (C) 2026 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+/* <termios.h> comes first so that this test also covers including
+   <sys/ioctl.h> after it; tst-winsize-ioctl.c covers the other order.  */
+#include <termios.h>
+#include <sys/ioctl.h>
+
+#include <errno.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/tty.h>
+#include <support/xunistd.h>
+
+static int
+do_test (void)
+{
+  struct winsize initial = { .ws_row = 24, .ws_col = 80 };
+  int outer, inner;
+  support_openpty (&outer, &inner, NULL, NULL, &initial);
+
+  struct winsize ws;
+  memset (&ws, 0xff, sizeof ws);
+  TEST_COMPARE (tcgetwinsize (inner, &ws), 0);
+  TEST_COMPARE (ws.ws_row, 24);
+  TEST_COMPARE (ws.ws_col, 80);
+
+  struct winsize updated
+    = { .ws_row = 50, .ws_col = 132, .ws_xpixel = 640, .ws_ypixel = 480 };
+  TEST_COMPARE (tcsetwinsize (inner, &updated), 0);
+
+  /* The new size must be visible through the ioctl interface and
+     through the other end of the pseudo-terminal.  */
+  memset (&ws, 0, sizeof ws);
+  TEST_COMPARE (ioctl (inner, TIOCGWINSZ, &ws), 0);
+  TEST_COMPARE (ws.ws_row, 50);
+  TEST_COMPARE (ws.ws_col, 132);
+  TEST_COMPARE (ws.ws_xpixel, 640);
+  TEST_COMPARE (ws.ws_ypixel, 480);
+
+  memset (&ws, 0, sizeof ws);
+  TEST_COMPARE (tcgetwinsize (outer, &ws), 0);
+  TEST_COMPARE (ws.ws_row, 50);
+  TEST_COMPARE (ws.ws_col, 132);
+
+  /* A descriptor which is not a terminal.  */
+  int fds[2];
+  xpipe (fds);
+  errno = 0;
+  TEST_COMPARE (tcgetwinsize (fds[0], &ws), -1);
+  TEST_COMPARE (errno, ENOTTY);
+  errno = 0;
+  TEST_COMPARE (tcsetwinsize (fds[0], &updated), -1);
+  TEST_COMPARE (errno, ENOTTY);
+
+  /* An invalid descriptor.  */
+  errno = 0;
+  TEST_COMPARE (tcgetwinsize (-1, &ws), -1);
+  TEST_COMPARE (errno, EBADF);
+  errno = 0;
+  TEST_COMPARE (tcsetwinsize (-1, &updated), -1);
+  TEST_COMPARE (errno, EBADF);
+
+  xclose (fds[0]);
+  xclose (fds[1]);
+  xclose (inner);
+  xclose (outer);
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/termios/tst-winsize-ioctl.c b/termios/tst-winsize-ioctl.c
new file mode 100644
index 0000000000..c653bf903a
--- /dev/null
+++ b/termios/tst-winsize-ioctl.c
@@ -0,0 +1,6 @@ 
+/* Process tst-winsize-main.c in the default mode, with <sys/ioctl.h>
+   included first to check that both headers can be combined in this
+   order; tst-tcwinsize.c covers the other order.  */
+#include <sys/ioctl.h>
+
+#include "tst-winsize-main.c"
diff --git a/termios/tst-winsize-main.c b/termios/tst-winsize-main.c
new file mode 100644
index 0000000000..66f2c1b48e
--- /dev/null
+++ b/termios/tst-winsize-main.c
@@ -0,0 +1,34 @@ 
+/* Check that <termios.h> defines struct winsize (bug 32074).
+   Copyright (C) 2026 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <termios.h>
+
+/* This fails to compile if <termios.h> leaves struct winsize
+   incomplete.  */
+static struct winsize ws;
+
+static int
+do_test (void)
+{
+  ws.ws_row = 24;
+  ws.ws_col = 80;
+
+  return ws.ws_row != 24 || ws.ws_col != 80;
+}
+
+#include <support/test-driver.c>
diff --git a/termios/tst-winsize-posix2024.c b/termios/tst-winsize-posix2024.c
new file mode 100644
index 0000000000..71d61ad5be
--- /dev/null
+++ b/termios/tst-winsize-posix2024.c
@@ -0,0 +1,5 @@ 
+/* Process tst-winsize-main.c in POSIX.1-2024 mode.  */
+#undef _GNU_SOURCE
+#define _POSIX_C_SOURCE 202405L
+
+#include "tst-winsize-main.c"
diff --git a/termios/tst-winsize-xopen2024.c b/termios/tst-winsize-xopen2024.c
new file mode 100644
index 0000000000..d72fc723fc
--- /dev/null
+++ b/termios/tst-winsize-xopen2024.c
@@ -0,0 +1,5 @@ 
+/* Process tst-winsize-main.c in X/Open Issue 8 mode.  */
+#undef _GNU_SOURCE
+#define _XOPEN_SOURCE 800
+
+#include "tst-winsize-main.c"
diff --git a/termios/tst-winsize.c b/termios/tst-winsize.c
new file mode 100644
index 0000000000..287653ffa3
--- /dev/null
+++ b/termios/tst-winsize.c
@@ -0,0 +1,3 @@ 
+/* Process tst-winsize-main.c in the default mode with <termios.h>
+   alone.  */
+#include "tst-winsize-main.c"