2 * SPDX-License-Identifier: ISC
4 * Copyright (c) 2011-2015, 2017 Todd C. Miller <Todd.Miller@sudo.ws>
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 * This is an open source non-commercial project. Dear PVS-Studio, please check it.
21 * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
26 #include <sys/types.h>
27 #include <sys/ioctl.h>
32 #endif /* HAVE_STRING_H */
35 #endif /* HAVE_STRINGS_H */
41 #include "sudo_compat.h"
42 #include "sudo_debug.h"
43 #include "sudo_util.h"
45 /* TCSASOFT is a BSD extension that ignores control flags and speed. */
50 /* Non-standard termios input flags */
61 /* Non-standard termios output flags */
78 /* Non-standard termios local flags */
95 #ifndef _POSIX_VDISABLE
97 # define _POSIX_VDISABLE VDISABLE
99 # define _POSIX_VDISABLE 0
103 static struct termios term, oterm;
106 /* tgetpass() needs to know the erase and kill chars for cbreak mode. */
107 __dso_public int sudo_term_eof;
108 __dso_public int sudo_term_erase;
109 __dso_public int sudo_term_kill;
111 static volatile sig_atomic_t got_sigttou;
114 * SIGTTOU signal handler for term_restore that just sets a flag.
123 * Like tcsetattr() but restarts on EINTR _except_ for SIGTTOU.
124 * Returns 0 on success or -1 on failure, setting errno.
125 * Sets got_sigttou on failure if interrupted by SIGTTOU.
128 tcsetattr_nobg(int fd, int flags, struct termios *tp)
130 struct sigaction sa, osa;
134 * If we receive SIGTTOU from tcsetattr() it means we are
135 * not in the foreground process group.
136 * This should be less racy than using tcgetpgrp().
138 memset(&sa, 0, sizeof(sa));
139 sigemptyset(&sa.sa_mask);
140 sa.sa_handler = sigttou;
142 sigaction(SIGTTOU, &sa, &osa);
144 rc = tcsetattr(fd, flags, tp);
145 } while (rc != 0 && errno == EINTR && !got_sigttou);
146 sigaction(SIGTTOU, &osa, NULL);
152 * Restore saved terminal settings if we are in the foreground process group.
153 * Returns true on success or false on failure.
156 sudo_term_restore_v1(int fd, bool flush)
158 debug_decl(sudo_term_restore, SUDO_DEBUG_UTIL)
161 const int flags = flush ? (TCSASOFT|TCSAFLUSH) : (TCSASOFT|TCSADRAIN);
162 if (tcsetattr_nobg(fd, flags, &oterm) != 0)
163 debug_return_bool(false);
166 debug_return_bool(true);
170 * Disable terminal echo.
171 * Returns true on success or false on failure.
174 sudo_term_noecho_v1(int fd)
176 debug_decl(sudo_term_noecho, SUDO_DEBUG_UTIL)
178 if (!changed && tcgetattr(fd, &oterm) != 0)
179 debug_return_bool(false);
180 (void) memcpy(&term, &oterm, sizeof(term));
181 CLR(term.c_lflag, ECHO|ECHONL);
183 term.c_cc[VSTATUS] = _POSIX_VDISABLE;
185 if (tcsetattr_nobg(fd, TCSASOFT|TCSADRAIN, &term) == 0) {
187 debug_return_bool(true);
189 debug_return_bool(false);
193 * Set terminal to raw mode.
194 * Returns true on success or false on failure.
197 sudo_term_raw_v1(int fd, int isig)
200 debug_decl(sudo_term_raw, SUDO_DEBUG_UTIL)
202 if (!changed && tcgetattr(fd, &oterm) != 0)
203 debug_return_bool(false);
204 (void) memcpy(&term, &oterm, sizeof(term));
205 /* Set terminal to raw mode */
207 term.c_cc[VTIME] = 0;
208 CLR(term.c_iflag, ICRNL | IGNCR | INLCR | IUCLC | IXON);
209 CLR(term.c_oflag, OPOST);
210 CLR(term.c_lflag, ECHO | ICANON | ISIG | IEXTEN);
212 SET(term.c_lflag, ISIG);
213 if (tcsetattr_nobg(fd, TCSASOFT|TCSADRAIN, &term) == 0) {
215 debug_return_bool(true);
217 debug_return_bool(false);
221 * Set terminal to cbreak mode.
222 * Returns true on success or false on failure.
225 sudo_term_cbreak_v1(int fd)
227 debug_decl(sudo_term_cbreak, SUDO_DEBUG_UTIL)
229 if (!changed && tcgetattr(fd, &oterm) != 0)
230 debug_return_bool(false);
231 (void) memcpy(&term, &oterm, sizeof(term));
232 /* Set terminal to half-cooked mode */
234 term.c_cc[VTIME] = 0;
235 /* cppcheck-suppress redundantAssignment */
236 CLR(term.c_lflag, ECHO | ECHONL | ICANON | IEXTEN);
237 /* cppcheck-suppress redundantAssignment */
238 SET(term.c_lflag, ISIG);
240 term.c_cc[VSTATUS] = _POSIX_VDISABLE;
242 if (tcsetattr_nobg(fd, TCSASOFT|TCSADRAIN, &term) == 0) {
243 sudo_term_eof = term.c_cc[VEOF];
244 sudo_term_erase = term.c_cc[VERASE];
245 sudo_term_kill = term.c_cc[VKILL];
247 debug_return_bool(true);
249 debug_return_bool(false);
252 /* Termios flags to copy between terminals. */
253 #define INPUT_FLAGS (IGNPAR|PARMRK|INPCK|ISTRIP|INLCR|IGNCR|ICRNL|IUCLC|IXON|IXANY|IXOFF|IMAXBEL|IUTF8)
254 #define OUTPUT_FLAGS (OPOST|OLCUC|ONLCR|OCRNL|ONOCR|ONLRET)
255 #define CONTROL_FLAGS (CS7|CS8|PARENB|PARODD)
256 #define LOCAL_FLAGS (ISIG|ICANON|XCASE|ECHO|ECHOE|ECHOK|ECHONL|NOFLSH|TOSTOP|IEXTEN|ECHOCTL|ECHOKE|PENDIN)
259 * Copy terminal settings from one descriptor to another.
260 * We cannot simply copy the struct termios as src and dst may be
261 * different terminal types (pseudo-tty vs. console or glass tty).
262 * Returns true on success or false on failure.
265 sudo_term_copy_v1(int src, int dst)
267 struct termios tt_src, tt_dst;
268 struct winsize wsize;
271 debug_decl(sudo_term_copy, SUDO_DEBUG_UTIL)
273 if (tcgetattr(src, &tt_src) != 0 || tcgetattr(dst, &tt_dst) != 0)
274 debug_return_bool(false);
276 /* Clear select input, output, control and local flags. */
277 CLR(tt_dst.c_iflag, INPUT_FLAGS);
278 CLR(tt_dst.c_oflag, OUTPUT_FLAGS);
279 CLR(tt_dst.c_cflag, CONTROL_FLAGS);
280 CLR(tt_dst.c_lflag, LOCAL_FLAGS);
282 /* Copy select input, output, control and local flags. */
283 SET(tt_dst.c_iflag, (tt_src.c_iflag & INPUT_FLAGS));
284 SET(tt_dst.c_oflag, (tt_src.c_oflag & OUTPUT_FLAGS));
285 SET(tt_dst.c_cflag, (tt_src.c_cflag & CONTROL_FLAGS));
286 SET(tt_dst.c_lflag, (tt_src.c_lflag & LOCAL_FLAGS));
288 /* Copy special chars from src verbatim. */
289 for (i = 0; i < NCCS; i++)
290 tt_dst.c_cc[i] = tt_src.c_cc[i];
292 /* Copy speed from src (zero output speed closes the connection). */
293 if ((speed = cfgetospeed(&tt_src)) == B0)
295 cfsetospeed(&tt_dst, speed);
296 speed = cfgetispeed(&tt_src);
297 cfsetispeed(&tt_dst, speed);
299 if (tcsetattr_nobg(dst, TCSASOFT|TCSAFLUSH, &tt_dst) == -1)
300 debug_return_bool(false);
302 if (ioctl(src, TIOCGWINSZ, &wsize) == 0)
303 (void)ioctl(dst, TIOCSWINSZ, &wsize);
305 debug_return_bool(true);