Bug Summary

File:/rootdir/_build/../src/pty.cc
Warning:line 189, column 22
Value stored to 'errsv' during its initialization is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name pty.cc -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -mframe-pointer=none -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/rootdir/_build -resource-dir /usr/lib/llvm-14/lib/clang/14.0.6 -I src/libbte-2.91.so.0.6500.0.p -I src -I ../src -I . -I .. -I src/bte -I ../src/bte -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/pango-1.0 -I /usr/include/fribidi -I /usr/include/cairo -I /usr/include/pixman-1 -I /usr/include/uuid -I /usr/include/harfbuzz -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/p11-kit-1 -I /usr/include/ctk-3.0 -I /usr/include/at-spi2-atk/2.0 -I /usr/include/at-spi-2.0 -I /usr/include/dbus-1.0 -I /usr/lib/x86_64-linux-gnu/dbus-1.0/include -I /usr/include/gio-unix-2.0 -I /usr/include/atk-1.0 -I /usr/include/gdk-pixbuf-2.0 -I /usr/include/x86_64-linux-gnu -D _FILE_OFFSET_BITS=64 -D G_LOG_DOMAIN="BTE" -D LOCALEDIR="/usr/local/share/locale" -D BTE_DISABLE_DEPRECATION_WARNINGS -D BTE_COMPILATION -U PARSER_INCLUDE_NOP -D CDK_VERSION_MIN_REQUIRED=(G_ENCODE_VERSION(3,18)) -D CDK_VERSION_MAX_ALLOWED=(G_ENCODE_VERSION(3,20)) -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/12/../../../../include/x86_64-linux-gnu/c++/12 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/backward -internal-isystem /usr/lib/llvm-14/lib/clang/14.0.6/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O3 -Wno-address-of-packed-member -Wno-missing-field-initializers -Wno-packed -Wno-switch-enum -Wno-unused-parameter -Wwrite-strings -std=gnu++17 -fdeprecated-macro -fdebug-compilation-dir=/rootdir/_build -ferror-limit 19 -fvisibility hidden -fvisibility-inlines-hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcxx-exceptions -fexceptions -fcolor-diagnostics -vectorize-loops -vectorize-slp -analyzer-checker deadcode.DeadStores -analyzer-checker alpha.deadcode.UnreachableCode -analyzer-checker alpha.core.CastSize -analyzer-checker alpha.core.CastToStruct -analyzer-checker alpha.core.IdenticalExpr -analyzer-checker alpha.core.SizeofPtr -analyzer-checker alpha.security.ArrayBoundV2 -analyzer-checker alpha.security.MallocOverflow -analyzer-checker alpha.security.ReturnPtrRange -analyzer-checker alpha.unix.SimpleStream -analyzer-checker alpha.unix.cstring.BufferOverlap -analyzer-checker alpha.unix.cstring.NotNullTerminated -analyzer-checker alpha.unix.cstring.OutOfBounds -analyzer-checker alpha.core.FixedAddr -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /rootdir/html-report/2022-08-13-111909-9867-1 -x c++ ../src/pty.cc
1/*
2 * Copyright (C) 2001,2002 Red Hat, Inc.
3 * Copyright © 2009, 2010, 2019 Christian Persch
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 3 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20/**
21 * SECTION: bte-pty
22 * @short_description: Functions for starting a new process on a new pseudo-terminal and for
23 * manipulating pseudo-terminals
24 *
25 * The terminal widget uses these functions to start commands with new controlling
26 * pseudo-terminals and to resize pseudo-terminals.
27 */
28
29#include "config.h"
30
31#include "pty.hh"
32
33#include "libc-glue.hh"
34
35#include <bte/bte.h>
36#include "bteptyinternal.hh"
37
38#include <assert.h>
39#include <sys/types.h>
40#include <sys/ioctl.h>
41#include <sys/socket.h>
42#ifdef HAVE_SYS_TERMIOS_H
43#include <sys/termios.h>
44#endif
45#include <errno(*__errno_location ()).h>
46#include <fcntl.h>
47#include <limits.h>
48#ifdef HAVE_SYS_SYSLIMITS_H
49#include <sys/syslimits.h>
50#endif
51#include <signal.h>
52#include <pthread.h>
53#include <stdio.h>
54#include <stdlib.h>
55#include <string.h>
56#ifdef HAVE_TERMIOS_H
57#include <termios.h>
58#endif
59#include <unistd.h>
60#ifdef HAVE_UTIL_H
61#include <util.h>
62#endif
63#ifdef HAVE_PTY_H
64#include <pty.h>
65#endif
66#if defined(__sun) && defined(HAVE_STROPTS_H)
67#include <stropts.h>
68#endif
69#include <glib.h>
70#include <gio/gio.h>
71#include "debug.h"
72
73#include <glib/gi18n-lib.h>
74
75#include "glib-glue.hh"
76
77#include "btedefines.hh"
78
79#include "missing.hh"
80
81namespace bte::base {
82
83Pty*
84Pty::ref() noexcept
85{
86 g_atomic_int_inc(&m_refcount)(__extension__ ({ static_assert (sizeof *(&m_refcount) ==
sizeof (gint), "Expression evaluates to false"); (void) (0 ?
*(&m_refcount) ^ *(&m_refcount) : 1); (void) __atomic_fetch_add
((&m_refcount), 1, 5); }))
;
87 return this;
88}
89
90void
91Pty::unref() noexcept
92{
93 if (g_atomic_int_dec_and_test(&m_refcount)(__extension__ ({ static_assert (sizeof *(&m_refcount) ==
sizeof (gint), "Expression evaluates to false"); (void) (0 ?
*(&m_refcount) ^ *(&m_refcount) : 1); __atomic_fetch_sub
((&m_refcount), 1, 5) == 1; }))
)
94 delete this;
95}
96
97int
98Pty::get_peer(bool cloexec) const noexcept
99{
100 if (!m_pty_fd)
101 return -1;
102
103 /* FIXME? else if (m_flags & BTE_PTY_NO_CTTTY)
104 * No session and no controlling TTY wanted, do we need to lose our controlling TTY,
105 * perhaps by open("/dev/tty") + ioctl(TIOCNOTTY) ?
106 */
107
108 /* Now open the PTY peer. Note that this also makes the PTY our controlling TTY. */
109 auto const fd_flags = int{O_RDWR02 |
110 ((m_flags & BTE_PTY_NO_CTTY) ? O_NOCTTY0400 : 0) |
111 (cloexec ? O_CLOEXEC02000000 : 0)};
112
113 auto peer_fd = bte::libc::FD{};
114
115#ifdef __linux__1
116 peer_fd = ioctl(m_pty_fd.get(), TIOCGPTPEER(((0U) << (((0 +8)+8)+14)) | ((('T')) << (0 +8)) |
(((0x41)) << 0) | ((0) << ((0 +8)+8)))
, fd_flags);
117 /* Note: According to the kernel's own tests (tools/testing/selftests/filesystems/devpts_pts.c),
118 * the error returned when the running kernel does not support this ioctl should be EINVAL.
119 * However it appears that the actual error returned is ENOTTY. So we check for both of them.
120 * See issue#182.
121 */
122 if (!peer_fd &&
123 errno(*__errno_location ()) != EINVAL22 &&
124 errno(*__errno_location ()) != ENOTTY25) {
125 auto errsv = bte::libc::ErrnoSaver{};
126 _bte_debug_print(BTE_DEBUG_PTY, "%s failed: %s\n",do { } while(0)
127 "ioctl(TIOCGPTPEER)", g_strerror(errsv))do { } while(0);
128 return -1;
129 }
130
131 /* Fall back to ptsname + open */
132#endif
133
134 if (!peer_fd) {
135 auto const name = ptsname(m_pty_fd.get());
136 if (name == nullptr) {
137 auto errsv = bte::libc::ErrnoSaver{};
138 _bte_debug_print(BTE_DEBUG_PTY, "%s failed: %s\n",do { } while(0)
139 "ptsname", g_strerror(errsv))do { } while(0);
140 return -1;
141 }
142
143 _bte_debug_print (BTE_DEBUG_PTY,do { } while(0)
144 "Setting up child pty: master FD = %d name = %s\n",do { } while(0)
145 m_pty_fd.get(), name)do { } while(0);
146
147 peer_fd = ::open(name, fd_flags);
148 if (!peer_fd) {
149 auto errsv = bte::libc::ErrnoSaver{};
150 _bte_debug_print (BTE_DEBUG_PTY, "Failed to open PTY: %s\n",do { } while(0)
151 g_strerror(errsv))do { } while(0);
152 return -1;
153 }
154 }
155
156 assert(bool(peer_fd))(static_cast <bool> (bool(peer_fd)) ? void (0) : __assert_fail
("bool(peer_fd)", "../src/pty.cc", 156, __extension__ __PRETTY_FUNCTION__
))
;
157
158#if defined(__sun) && defined(HAVE_STROPTS_H)
159 /* See https://illumos.org/man/7i/streamio */
160 if (isastream (peer_fd.get()) == 1) {
161 /* https://illumos.org/man/7m/ptem */
162 if ((ioctl(peer_fd.get(), I_FIND, "ptem") == 0) &&
163 (ioctl(peer_fd.get(), I_PUSH, "ptem") == -1)) {
164 return -1;
165 }
166 /* https://illumos.org/man/7m/ldterm */
167 if ((ioctl(peer_fd.get(), I_FIND, "ldterm") == 0) &&
168 (ioctl(peer_fd.get(), I_PUSH, "ldterm") == -1)) {
169 return -1;
170 }
171 /* https://illumos.org/man/7m/ttcompat */
172 if ((ioctl(peer_fd.get(), I_FIND, "ttcompat") == 0) &&
173 (ioctl(peer_fd.get(), I_PUSH, "ttcompat") == -1)) {
174 return -1;
175 }
176 }
177#endif
178
179 return peer_fd.release();
180}
181
182void
183Pty::child_setup() const noexcept
184{
185 /* Unblock all signals */
186 sigset_t set;
187 sigemptyset(&set);
188 if (pthread_sigmask(SIG_SETMASK2, &set, nullptr) == -1) {
189 auto errsv = bte::libc::ErrnoSaver{};
Value stored to 'errsv' during its initialization is never read
190 _bte_debug_print(BTE_DEBUG_PTY, "%s failed: %s\n",do { } while(0)
191 "pthread_sigmask", g_strerror(errsv))do { } while(0);
192 _exit(127);
193 }
194
195 /* Reset the handlers for all signals to their defaults. The parent
196 * (or one of the libraries it links to) may have changed one to be ignored.
197 */
198 for (int n = 1; n < NSIG(64 + 1); n++) {
199 if (n == SIGSTOP19 || n == SIGKILL9)
200 continue;
201
202 signal(n, SIG_DFL((__sighandler_t) 0));
203 }
204
205 if (!(m_flags & BTE_PTY_NO_SESSION)) {
206 /* This starts a new session; we become its process-group leader,
207 * and lose our controlling TTY.
208 */
209 _bte_debug_print (BTE_DEBUG_PTY, "Starting new session\n")do { } while(0);
210 if (setsid() == -1) {
211 auto errsv = bte::libc::ErrnoSaver{};
212 _bte_debug_print(BTE_DEBUG_PTY, "%s failed: %s\n",do { } while(0)
213 "setsid", g_strerror(errsv))do { } while(0);
214 _exit(127);
215 }
216 }
217
218 auto peer_fd = get_peer();
219 if (peer_fd == -1)
220 _exit(127);
221
222#ifdef TIOCSCTTY0x540E
223 /* On linux, opening the PTY peer above already made it our controlling TTY (since
224 * previously there was none, after the setsid() call). However, it appears that e.g.
225 * on *BSD, that doesn't happen, so we need this explicit ioctl here.
226 */
227 if (!(m_flags & BTE_PTY_NO_CTTY)) {
228 if (ioctl(peer_fd, TIOCSCTTY0x540E, peer_fd) != 0) {
229 auto errsv = bte::libc::ErrnoSaver{};
230 _bte_debug_print(BTE_DEBUG_PTY, "%s failed: %s\n",do { } while(0)
231 "ioctl(TIOCSCTTY)", g_strerror(errsv))do { } while(0);
232 _exit(127);
233 }
234 }
235#endif
236
237 /* now setup child I/O through the tty */
238 if (peer_fd != STDIN_FILENO0) {
239 if (dup2(peer_fd, STDIN_FILENO0) != STDIN_FILENO0){
240 _exit (127);
241 }
242 }
243 if (peer_fd != STDOUT_FILENO1) {
244 if (dup2(peer_fd, STDOUT_FILENO1) != STDOUT_FILENO1){
245 _exit (127);
246 }
247 }
248 if (peer_fd != STDERR_FILENO2) {
249 if (dup2(peer_fd, STDERR_FILENO2) != STDERR_FILENO2){
250 _exit (127);
251 }
252 }
253
254 /* If the peer FD has not been consumed above as one of the stdio descriptors,
255 * need to close it now so that it doesn't leak to the child.
256 */
257 if (peer_fd != STDIN_FILENO0 &&
258 peer_fd != STDOUT_FILENO1 &&
259 peer_fd != STDERR_FILENO2) {
260 close(peer_fd);
261 }
262
263 /* Now set the TERM environment variable */
264 /* FIXME: Setting environment here seems to have no effect, the merged envp2 will override on exec.
265 * By the way, we'd need to set the one from there, if any. */
266 g_setenv("TERM", BTE_TERMINFO_NAME"xterm-256color", TRUE(!(0)));
267
268 char version[7];
269 g_snprintf (version, sizeof (version), "%u", BTE_VERSION_NUMERIC(((0)) * 10000 + ((65)) * 100 + ((0))));
270 g_setenv ("BTE_VERSION", version, TRUE(!(0)));
271}
272
273/*
274 * Pty::set_size:
275 * @rows: the desired number of rows
276 * @columns: the desired number of columns
277 * @cell_height_px: the height of a cell in px, or 0 for undetermined
278 * @cell_width_px: the width of a cell in px, or 0 for undetermined
279 *
280 * Attempts to resize the pseudo terminal's window size. If successful, the
281 * OS kernel will send #SIGWINCH to the child process group.
282 *
283 * Returns: %true on success, or %false on error with errno set
284 */
285bool
286Pty::set_size(int rows,
287 int columns,
288 int cell_height_px,
289 int cell_width_px) const noexcept
290{
291 auto master = fd();
292
293 struct winsize size;
294 memset(&size, 0, sizeof(size));
295 size.ws_row = rows > 0 ? rows : 24;
296 size.ws_col = columns > 0 ? columns : 80;
297 _bte_debug_print(BTE_DEBUG_PTY,do { } while(0)
298 "Setting size on fd %d to (%d,%d).\n",do { } while(0)
299 master, columns, rows)do { } while(0);
300 auto ret = ioctl(master, TIOCSWINSZ0x5414, &size);
301
302 if (ret != 0) {
303 auto errsv = bte::libc::ErrnoSaver{};
304 _bte_debug_print(BTE_DEBUG_PTY,do { } while(0)
305 "Failed to set size on %d: %s\n",do { } while(0)
306 master, g_strerror(errsv))do { } while(0);
307 }
308
309 return ret == 0;
310}
311
312/*
313 * Pty::get_size:
314 * @rows: (out) (allow-none): a location to store the number of rows, or %NULL
315 * @columns: (out) (allow-none): a location to store the number of columns, or %NULL
316 *
317 * Reads the pseudo terminal's window size.
318 *
319 * If getting the window size failed, @error will be set to a #GIOError.
320 *
321 * Returns: %true on success, or %false on error with errno set
322 */
323bool
324Pty::get_size(int* rows,
325 int* columns) const noexcept
326{
327 auto master = fd();
328
329 struct winsize size;
330 memset(&size, 0, sizeof(size));
331 auto ret = ioctl(master, TIOCGWINSZ0x5413, &size);
332 if (ret == 0) {
333 if (columns != nullptr) {
334 *columns = size.ws_col;
335 }
336 if (rows != nullptr) {
337 *rows = size.ws_row;
338 }
339 _bte_debug_print(BTE_DEBUG_PTY,do { } while(0)
340 "Size on fd %d is (%d,%d).\n",do { } while(0)
341 master, size.ws_col, size.ws_row)do { } while(0);
342 return true;
343 }
344
345 auto errsv = bte::libc::ErrnoSaver{};
346 _bte_debug_print(BTE_DEBUG_PTY,do { } while(0)
347 "Failed to read size from fd %d: %s\n",do { } while(0)
348 master, g_strerror(errsv))do { } while(0);
349
350 return false;
351}
352
353static int
354fd_set_cpkt(bte::libc::FD& fd)
355{
356 /* tty_ioctl(4) -> every read() gives an extra byte at the beginning
357 * notifying us of stop/start (^S/^Q) events. */
358 int one = 1;
359 return ioctl(fd.get(), TIOCPKT0x5420, &one);
360}
361
362static int
363fd_setup(bte::libc::FD& fd)
364{
365 if (grantpt(fd.get()) != 0) {
366 auto errsv = bte::libc::ErrnoSaver{};
367 _bte_debug_print(BTE_DEBUG_PTY, "%s failed: %s\n",do { } while(0)
368 "grantpt", g_strerror(errsv))do { } while(0);
369 return -1;
370 }
371
372 if (unlockpt(fd.get()) != 0) {
373 auto errsv = bte::libc::ErrnoSaver{};
374 _bte_debug_print(BTE_DEBUG_PTY, "%s failed: %s\n",do { } while(0)
375 "unlockpt", g_strerror(errsv))do { } while(0);
376 return -1;
377 }
378
379 if (bte::libc::fd_set_cloexec(fd.get()) < 0) {
380 auto errsv = bte::libc::ErrnoSaver{};
381 _bte_debug_print(BTE_DEBUG_PTY,do { } while(0)
382 "%s failed: %s",do { } while(0)
383 "Setting CLOEXEC flag", g_strerror(errsv))do { } while(0);
384 return -1;
385 }
386
387 if (bte::libc::fd_set_nonblock(fd.get()) < 0) {
388 auto errsv = bte::libc::ErrnoSaver{};
389 _bte_debug_print(BTE_DEBUG_PTY,do { } while(0)
390 "%s failed: %s",do { } while(0)
391 "Setting O_NONBLOCK flag", g_strerror(errsv))do { } while(0);
392 return -1;
393 }
394
395 if (fd_set_cpkt(fd) < 0) {
396 auto errsv = bte::libc::ErrnoSaver{};
397 _bte_debug_print(BTE_DEBUG_PTY,do { } while(0)
398 "%s failed: %s",do { } while(0)
399 "ioctl(TIOCPKT)", g_strerror(errsv))do { } while(0);
400 return -1;
401 }
402
403 return 0;
404}
405
406/*
407 * _bte_pty_open_posix:
408 * @pty: a #BtePty
409 * @error: a location to store a #GError, or %NULL
410 *
411 * Opens a new file descriptor to a new PTY master.
412 *
413 * Returns: the new PTY's master FD, or -1
414 */
415static bte::libc::FD
416_bte_pty_open_posix(void)
417{
418 /* Attempt to open the master. */
419 auto fd = bte::libc::FD{posix_openpt(O_RDWR02 | O_NOCTTY0400 | O_NONBLOCK04000 | O_CLOEXEC02000000)};
420#ifndef __linux__1
421 /* Other kernels may not support CLOEXEC or NONBLOCK above, so try to fall back */
422 bool need_cloexec = false, need_nonblocking = false;
423 if (!fd && errno(*__errno_location ()) == EINVAL22) {
424 /* Try without NONBLOCK and apply the flag afterward */
425 need_nonblocking = true;
426 fd = posix_openpt(O_RDWR02 | O_NOCTTY0400 | O_CLOEXEC02000000);
427 if (!fd && errno(*__errno_location ()) == EINVAL22) {
428 /* Try without CLOEXEC and apply the flag afterwards */
429 need_cloexec = true;
430 fd = posix_openpt(O_RDWR02 | O_NOCTTY0400);
431 }
432 }
433#endif /* !linux */
434
435 if (!fd) {
436 auto errsv = bte::libc::ErrnoSaver{};
437 _bte_debug_print(BTE_DEBUG_PTY,do { } while(0)
438 "%s failed: %s",do { } while(0)
439 "posix_openpt", g_strerror(errsv))do { } while(0);
440 return {};
441 }
442
443#ifndef __linux__1
444 if (need_cloexec && bte::libc::fd_set_cloexec(fd.get()) < 0) {
445 auto errsv = bte::libc::ErrnoSaver{};
446 _bte_debug_print(BTE_DEBUG_PTY,do { } while(0)
447 "%s failed: %s",do { } while(0)
448 "Setting CLOEXEC flag", g_strerror(errsv))do { } while(0);
449 return {};
450 }
451
452 if (need_nonblocking && bte::libc::fd_set_nonblock(fd.get()) < 0) {
453 auto errsv = bte::libc::ErrnoSaver{};
454 _bte_debug_print(BTE_DEBUG_PTY,do { } while(0)
455 "%s failed: %s",do { } while(0)
456 "Setting NONBLOCK flag", g_strerror(errsv))do { } while(0);
457 return {};
458 }
459#endif /* !linux */
460
461 if (fd_set_cpkt(fd) < 0) {
462 auto errsv = bte::libc::ErrnoSaver{};
463 _bte_debug_print(BTE_DEBUG_PTY,do { } while(0)
464 "%s failed: %s",do { } while(0)
465 "ioctl(TIOCPKT)", g_strerror(errsv))do { } while(0);
466 return {};
467 }
468
469 if (grantpt(fd.get()) != 0) {
470 auto errsv = bte::libc::ErrnoSaver{};
471 _bte_debug_print(BTE_DEBUG_PTY, "%s failed: %s\n",do { } while(0)
472 "grantpt", g_strerror(errsv))do { } while(0);
473 return {};
474 }
475
476 if (unlockpt(fd.get()) != 0) {
477 auto errsv = bte::libc::ErrnoSaver{};
478 _bte_debug_print(BTE_DEBUG_PTY, "%s failed: %s\n",do { } while(0)
479 "unlockpt", g_strerror(errsv))do { } while(0);
480 return {};
481 }
482
483 _bte_debug_print(BTE_DEBUG_PTY, "Allocated pty on fd %d.\n", fd.get())do { } while(0);
484
485 return fd;
486}
487
488static bte::libc::FD
489_bte_pty_open_foreign(int masterfd /* consumed */)
490{
491 auto fd = bte::libc::FD{masterfd};
492 if (!fd) {
493 errno(*__errno_location ()) = EBADF9;
494 return {};
495 }
496
497 if (fd_setup(fd) < 0)
498 return {};
499
500 return fd;
501}
502
503/*
504 * Pty::set_utf8:
505 * @utf8: whether or not the pty is in UTF-8 mode
506 *
507 * Tells the kernel whether the terminal is UTF-8 or not, in case it can make
508 * use of the info. Linux 2.6.5 or so defines IUTF8 to make the line
509 * discipline do multibyte backspace correctly.
510 *
511 * Returns: %true on success, or %false on error with errno set
512 */
513bool
514Pty::set_utf8(bool utf8) const noexcept
515{
516#ifdef IUTF80040000
517 struct termios tio;
518 if (tcgetattr(fd(), &tio) == -1) {
519 auto errsv = bte::libc::ErrnoSaver{};
520 _bte_debug_print(BTE_DEBUG_PTY, "%s failed: %s",do { } while(0)
521 "tcgetattr", g_strerror(errsv))do { } while(0);
522 return false;
523 }
524
525 auto saved_cflag = tio.c_iflag;
526 if (utf8) {
527 tio.c_iflag |= IUTF80040000;
528 } else {
529 tio.c_iflag &= ~IUTF80040000;
530 }
531
532 /* Only set the flag if it changes */
533 if (saved_cflag != tio.c_iflag &&
534 tcsetattr(fd(), TCSANOW0, &tio) == -1) {
535 auto errsv = bte::libc::ErrnoSaver{};
536 _bte_debug_print(BTE_DEBUG_PTY, "%s failed: %s",do { } while(0)
537 "tcsetattr", g_strerror(errsv))do { } while(0);
538 return false;
539 }
540#endif
541
542 return true;
543}
544
545Pty*
546Pty::create(BtePtyFlags flags)
547{
548 auto fd = _bte_pty_open_posix();
549 if (!fd)
550 return nullptr;
551
552 return new Pty{std::move(fd), flags};
553}
554
555Pty*
556Pty::create_foreign(int foreign_fd,
557 BtePtyFlags flags)
558{
559 auto fd = _bte_pty_open_foreign(foreign_fd);
560 if (!fd)
561 return nullptr;
562
563 return new Pty{std::move(fd), flags};
564}
565
566} // namespace bte::base