1 /*
2  * Copyright (C) 2001,2002 Red Hat, Inc.
3  * Copyright © 2009, 2010 Christian Persch
4  *
5  * This is free software; you can redistribute it and/or modify it under
6  * the terms of the GNU Library General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 
20 /**
21  * SECTION: vte-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  * Since: 0.26
29  */
30 
31 #include <config.h>
32 
33 #include "vtepty.h"
34 #include "vtepty-private.h"
35 #include "vte.h"
36 
37 #include <sys/types.h>
38 #include <sys/ioctl.h>
39 #include <sys/socket.h>
40 #ifdef HAVE_SYS_TERMIOS_H
41 #include <sys/termios.h>
42 #endif
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <limits.h>
46 #ifdef HAVE_SYS_SYSLIMITS_H
47 #include <sys/syslimits.h>
48 #endif
49 #include <signal.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #ifdef HAVE_TERMIOS_H
54 #include <termios.h>
55 #endif
56 #include <unistd.h>
57 #ifdef HAVE_STROPTS_H
58 #include <stropts.h>
59 #endif
60 #include <glib.h>
61 #include <gio/gio.h>
62 #include "debug.h"
63 #include "pty.h"
64 
65 #ifdef MSG_NOSIGNAL
66 #define PTY_RECVMSG_FLAGS MSG_NOSIGNAL
67 #else
68 #define PTY_RECVMSG_FLAGS 0
69 #endif
70 
71 #include <glib/gi18n-lib.h>
72 
73 #ifdef VTE_USE_GNOME_PTY_HELPER
74 #include <sys/uio.h>
75 #ifdef HAVE_SYS_UN_H
76 #include <sys/un.h>
77 #endif
78 #include "../gnome-pty-helper/gnome-pty.h"
79 static gboolean _vte_pty_helper_started = FALSE;
80 static pid_t _vte_pty_helper_pid = -1;
81 static int _vte_pty_helper_tunnel = -1;
82 #endif
83 
84 /* Reset the handlers for all known signals to their defaults.  The parent
85  * (or one of the libraries it links to) may have changed one to be ignored. */
86 static void
_vte_pty_reset_signal_handlers(void)87 _vte_pty_reset_signal_handlers(void)
88 {
89 #ifdef SIGHUP
90 	signal(SIGHUP,  SIG_DFL);
91 #endif
92 	signal(SIGINT,  SIG_DFL);
93 	signal(SIGILL,  SIG_DFL);
94 	signal(SIGABRT, SIG_DFL);
95 	signal(SIGFPE,  SIG_DFL);
96 #ifdef SIGKILL
97 	signal(SIGKILL, SIG_DFL);
98 #endif
99 	signal(SIGSEGV, SIG_DFL);
100 #ifdef SIGPIPE
101 	signal(SIGPIPE, SIG_DFL);
102 #endif
103 #ifdef SIGALRM
104 	signal(SIGALRM, SIG_DFL);
105 #endif
106 	signal(SIGTERM, SIG_DFL);
107 #ifdef SIGCHLD
108 	signal(SIGCHLD, SIG_DFL);
109 #endif
110 #ifdef SIGCONT
111 	signal(SIGCONT, SIG_DFL);
112 #endif
113 #ifdef SIGSTOP
114 	signal(SIGSTOP, SIG_DFL);
115 #endif
116 #ifdef SIGTSTP
117 	signal(SIGTSTP, SIG_DFL);
118 #endif
119 #ifdef SIGTTIN
120 	signal(SIGTTIN, SIG_DFL);
121 #endif
122 #ifdef SIGTTOU
123 	signal(SIGTTOU, SIG_DFL);
124 #endif
125 #ifdef SIGBUS
126 	signal(SIGBUS,  SIG_DFL);
127 #endif
128 #ifdef SIGPOLL
129 	signal(SIGPOLL, SIG_DFL);
130 #endif
131 #ifdef SIGPROF
132 	signal(SIGPROF, SIG_DFL);
133 #endif
134 #ifdef SIGSYS
135 	signal(SIGSYS,  SIG_DFL);
136 #endif
137 #ifdef SIGTRAP
138 	signal(SIGTRAP, SIG_DFL);
139 #endif
140 #ifdef SIGURG
141 	signal(SIGURG,  SIG_DFL);
142 #endif
143 #ifdef SIGVTALARM
144 	signal(SIGVTALARM, SIG_DFL);
145 #endif
146 #ifdef SIGXCPU
147 	signal(SIGXCPU, SIG_DFL);
148 #endif
149 #ifdef SIGXFSZ
150 	signal(SIGXFSZ, SIG_DFL);
151 #endif
152 #ifdef SIGIOT
153 	signal(SIGIOT,  SIG_DFL);
154 #endif
155 #ifdef SIGEMT
156 	signal(SIGEMT,  SIG_DFL);
157 #endif
158 #ifdef SIGSTKFLT
159 	signal(SIGSTKFLT, SIG_DFL);
160 #endif
161 #ifdef SIGIO
162 	signal(SIGIO,   SIG_DFL);
163 #endif
164 #ifdef SIGCLD
165 	signal(SIGCLD,  SIG_DFL);
166 #endif
167 #ifdef SIGPWR
168 	signal(SIGPWR,  SIG_DFL);
169 #endif
170 #ifdef SIGINFO
171 	signal(SIGINFO, SIG_DFL);
172 #endif
173 #ifdef SIGLOST
174 	signal(SIGLOST, SIG_DFL);
175 #endif
176 #ifdef SIGWINCH
177 	signal(SIGWINCH, SIG_DFL);
178 #endif
179 #ifdef SIGUNUSED
180 	signal(SIGUNUSED, SIG_DFL);
181 #endif
182 }
183 
184 typedef struct _VtePtyPrivate VtePtyPrivate;
185 
186 typedef struct {
187 	enum {
188 		TTY_OPEN_BY_NAME,
189 		TTY_OPEN_BY_FD
190 	} mode;
191 	union {
192 		const char *name;
193 		int fd;
194 	} tty;
195 } VtePtyChildSetupData;
196 
197 /**
198  * VtePty:
199  *
200  * Since: 0.26
201  */
202 struct _VtePty {
203         GObject parent_instance;
204 
205         /* <private> */
206         VtePtyPrivate *priv;
207 };
208 
209 struct _VtePtyPrivate {
210         VtePtyFlags flags;
211         int pty_fd;
212 
213         const char *term;
214         VtePtyChildSetupData child_setup_data;
215 
216         gpointer helper_tag; /* only use when using_helper is TRUE */
217 
218         guint utf8 : 1;
219         guint foreign : 1;
220         guint using_helper : 1;
221 };
222 
223 struct _VtePtyClass {
224         GObjectClass parent_class;
225 };
226 
227 /**
228  * vte_pty_child_setup:
229  * @pty: a #VtePty
230  *
231  * FIXMEchpe
232  *
233  * Since: 0.26
234  */
235 void
vte_pty_child_setup(VtePty * pty)236 vte_pty_child_setup (VtePty *pty)
237 {
238         VtePtyPrivate *priv = pty->priv;
239 	VtePtyChildSetupData *data = &priv->child_setup_data;
240 	int fd = -1;
241 	const char *tty = NULL;
242 
243         if (priv->foreign) {
244                 fd = priv->pty_fd;
245         } else {
246                 /* Save the name of the pty -- we'll need it later to acquire
247                 * it as our controlling terminal.
248                 */
249                 switch (data->mode) {
250                         case TTY_OPEN_BY_NAME:
251                                 tty = data->tty.name;
252                                 break;
253                         case TTY_OPEN_BY_FD:
254                                 fd = data->tty.fd;
255                                 tty = ttyname(fd);
256                                 break;
257                 }
258 
259                 _vte_debug_print (VTE_DEBUG_PTY,
260                                 "Setting up child pty: name = %s, fd = %d\n",
261                                         tty ? tty : "(none)", fd);
262 
263 
264                 /* Try to reopen the pty to acquire it as our controlling terminal. */
265                 /* FIXMEchpe: why not just use the passed fd in TTY_OPEN_BY_FD mode? */
266                 if (tty != NULL) {
267                         int i = open(tty, O_RDWR);
268                         if (i != -1) {
269                                 if (fd != -1){
270                                         close(fd);
271                                 }
272                                 fd = i;
273                         }
274                 }
275         }
276 
277 	if (fd == -1)
278 		_exit (127);
279 
280 	/* Start a new session and become process-group leader. */
281 #if defined(HAVE_SETSID) && defined(HAVE_SETPGID)
282 	_vte_debug_print (VTE_DEBUG_PTY, "Starting new session\n");
283 	setsid();
284 	setpgid(0, 0);
285 #endif
286 
287 #ifdef TIOCSCTTY
288 	/* TIOCSCTTY is defined?  Let's try that, too. */
289 	ioctl(fd, TIOCSCTTY, fd);
290 #endif
291 
292 #ifdef HAVE_STROPTS_H
293 	if (isastream (fd) == 1) {
294 		if ((ioctl(fd, I_FIND, "ptem") == 0) &&
295 				(ioctl(fd, I_PUSH, "ptem") == -1)) {
296 			_exit (127);
297 		}
298 		if ((ioctl(fd, I_FIND, "ldterm") == 0) &&
299 				(ioctl(fd, I_PUSH, "ldterm") == -1)) {
300 			_exit (127);
301 		}
302 		if ((ioctl(fd, I_FIND, "ttcompat") == 0) &&
303 				(ioctl(fd, I_PUSH, "ttcompat") == -1)) {
304 			perror ("ioctl (fd, I_PUSH, \"ttcompat\")");
305 			_exit (127);
306 		}
307 	}
308 #endif
309 
310 	/* now setup child I/O through the tty */
311 	if (fd != STDIN_FILENO) {
312 		if (dup2(fd, STDIN_FILENO) != STDIN_FILENO){
313 			_exit (127);
314 		}
315 	}
316 	if (fd != STDOUT_FILENO) {
317 		if (dup2(fd, STDOUT_FILENO) != STDOUT_FILENO){
318 			_exit (127);
319 		}
320 	}
321 	if (fd != STDERR_FILENO) {
322 		if (dup2(fd, STDERR_FILENO) != STDERR_FILENO){
323 			_exit (127);
324 		}
325 	}
326 
327 	/* Close the original slave descriptor, unless it's one of the stdio
328 	 * descriptors. */
329 	if (fd != STDIN_FILENO &&
330 			fd != STDOUT_FILENO &&
331 			fd != STDERR_FILENO) {
332 		close(fd);
333 	}
334 
335 	/* Reset our signals -- our parent may have done any number of
336 	 * weird things to them. */
337 	_vte_pty_reset_signal_handlers();
338 
339         /* Now set the TERM environment variable */
340         if (priv->term != NULL) {
341                 g_setenv("TERM", priv->term, TRUE);
342         }
343 }
344 
345 /* TODO: clean up the spawning
346  * - replace current env rather than adding!
347  */
348 
349 /*
350  * __vte_pty_get_argv:
351  * @command: the command to run
352  * @argv: the argument vector
353  * @flags: (inout) flags from #GSpawnFlags
354  *
355  * Creates an argument vector to pass to g_spawn_async() from @command and
356  * @argv, modifying *@flags if necessary.
357  *
358  * Returns: a newly allocated array of strings. Free with g_strfreev()
359  */
360 char **
__vte_pty_get_argv(const char * command,char ** argv,GSpawnFlags * flags)361 __vte_pty_get_argv (const char *command,
362                     char **argv,
363                     GSpawnFlags *flags /* inout */)
364 {
365 	char **argv2;
366 	int i, argc;
367 
368         g_return_val_if_fail(command != NULL, NULL);
369 
370 	/* push the command into argv[0] */
371 	argc = argv ? g_strv_length (argv) : 0;
372 	argv2 = g_new (char *, argc + 2);
373 
374 	argv2[0] = g_strdup (command);
375 
376 	for (i = 0; i < argc; i++) {
377 		argv2[i+1] = g_strdup (argv[i]);
378 	}
379 	argv2[i+1] = NULL;
380 
381         if (argv) {
382                 *flags |= G_SPAWN_FILE_AND_ARGV_ZERO;
383         }
384 
385 	return argv2;
386 }
387 
388 /*
389  * __vte_pty_merge_environ:
390  * @envp: environment vector
391  *
392  * Merges @envp to the parent environment, and returns a new environment vector.
393  *
394  * Returns: a newly allocated string array. Free using g_strfreev()
395  */
396 static gchar **
__vte_pty_merge_environ(char ** envp,const char * term_value)397 __vte_pty_merge_environ (char **envp, const char *term_value)
398 {
399 	GHashTable *table;
400         GHashTableIter iter;
401         char *name, *value;
402 	gchar **parent_environ;
403 	GPtrArray *array;
404 	gint i;
405 
406 	table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
407 
408 	parent_environ = g_listenv ();
409 	for (i = 0; parent_environ[i] != NULL; i++) {
410 		g_hash_table_replace (table,
411 			              g_strdup (parent_environ[i]),
412 				      g_strdup (g_getenv (parent_environ[i])));
413 	}
414 	g_strfreev (parent_environ);
415 
416 	if (envp != NULL) {
417 		for (i = 0; envp[i] != NULL; i++) {
418 			name = g_strdup (envp[i]);
419 			value = strchr (name, '=');
420 			if (value) {
421 				*value = '\0';
422 				value = g_strdup (value + 1);
423 			}
424 			g_hash_table_replace (table, name, value);
425 		}
426 	}
427 
428         if (term_value != NULL)
429                 g_hash_table_replace (table, g_strdup ("TERM"), g_strdup (term_value));
430 
431 	array = g_ptr_array_sized_new (g_hash_table_size (table) + 1);
432         g_hash_table_iter_init(&iter, table);
433         while (g_hash_table_iter_next(&iter, (gpointer) &name, (gpointer) &value)) {
434                 g_ptr_array_add (array, g_strconcat (name, "=", value, NULL));
435         }
436         g_assert(g_hash_table_size(table) == array->len);
437 	g_hash_table_destroy (table);
438 	g_ptr_array_add (array, NULL);
439 
440 	return (gchar **) g_ptr_array_free (array, FALSE);
441 }
442 
443 /*
444  * __vte_pty_get_pty_flags:
445  * @lastlog: %TRUE if the session should be logged to the lastlog
446  * @utmp: %TRUE if the session should be logged to the utmp/utmpx log
447  * @wtmp: %TRUE if the session should be logged to the wtmp/wtmpx log
448  *
449  * Combines the @lastlog, @utmp, @wtmp arguments into the coresponding
450  * #VtePtyFlags flags.
451  *
452  * Returns: flags from #VtePtyFlags
453  */
454 VtePtyFlags
__vte_pty_get_pty_flags(gboolean lastlog,gboolean utmp,gboolean wtmp)455 __vte_pty_get_pty_flags(gboolean lastlog,
456                         gboolean utmp,
457                         gboolean wtmp)
458 {
459         VtePtyFlags flags = VTE_PTY_DEFAULT;
460 
461         if (!lastlog)
462                 flags |= VTE_PTY_NO_LASTLOG;
463         if (!utmp)
464                 flags |= VTE_PTY_NO_UTMP;
465         if (!wtmp)
466                 flags |= VTE_PTY_NO_WTMP;
467 
468         return flags;
469 }
470 
471 /*
472  * __vte_pty_spawn:
473  * @pty: a #VtePty
474  * @directory: the name of a directory the command should start in, or %NULL
475  *   to use the cwd
476  * @argv: child's argument vector
477  * @envv: a list of environment variables to be added to the environment before
478  *   starting the process, or %NULL
479  * @spawn_flags: flags from #GSpawnFlags
480  * @child_setup: function to run in the child just before exec()
481  * @child_setup_data: user data for @child_setup
482  * @child_pid: a location to store the child PID, or %NULL
483  * @error: return location for a #GError, or %NULL
484  *
485  * Uses g_spawn_async() to spawn the command in @argv. The child's environment will
486  * be the parent environment with the variables in @envv set afterwards.
487  *
488  * Enforces the vte_terminal_watch_child() requirements by adding
489  * %G_SPAWN_DO_NOT_REAP_CHILD to @spawn_flags.
490  *
491  * Note that the %G_SPAWN_LEAVE_DESCRIPTORS_OPEN flag is not supported;
492  * it will be cleared!
493  *
494  * If spawning the command in @working_directory fails because the child
495  * is unable to chdir() to it, falls back trying to spawn the command
496  * in the parent's working directory.
497  *
498  * Returns: %TRUE on success, or %FALSE on failure with @error filled in
499  */
500 gboolean
__vte_pty_spawn(VtePty * pty,const char * directory,char ** argv,char ** envv,GSpawnFlags spawn_flags,GSpawnChildSetupFunc child_setup,gpointer child_setup_data,GPid * child_pid,GError ** error)501 __vte_pty_spawn (VtePty *pty,
502                  const char *directory,
503                  char **argv,
504                  char **envv,
505                  GSpawnFlags spawn_flags,
506                  GSpawnChildSetupFunc child_setup,
507                  gpointer child_setup_data,
508                  GPid *child_pid /* out */,
509                  GError **error)
510 {
511 	gboolean ret = TRUE;
512         char **envp2;
513         gint i;
514         GError *err = NULL;
515 
516         spawn_flags |= G_SPAWN_DO_NOT_REAP_CHILD;
517 
518         /* FIXMEchpe: Enforce this until I've checked our code to make sure
519          * it doesn't leak out internal FDs into the child this way.
520          */
521         spawn_flags &= ~G_SPAWN_LEAVE_DESCRIPTORS_OPEN;
522 
523         /* add the given environment to the childs */
524         envp2 = __vte_pty_merge_environ (envv, pty->priv->term);
525 
526         _VTE_DEBUG_IF (VTE_DEBUG_MISC) {
527                 g_printerr ("Spawing command:\n");
528                 for (i = 0; argv[i] != NULL; i++) {
529                         g_printerr ("    argv[%d] = %s\n", i, argv[i]);
530                 }
531                 for (i = 0; envp2[i] != NULL; i++) {
532                         g_printerr ("    env[%d] = %s\n", i, envp2[i]);
533                 }
534                 g_printerr ("    directory: %s\n",
535                             directory ? directory : "(none)");
536         }
537 
538         ret = g_spawn_async_with_pipes(directory,
539                                        argv, envp2,
540                                        spawn_flags,
541                                        child_setup ? child_setup
542                                                    : (GSpawnChildSetupFunc) vte_pty_child_setup,
543                                        child_setup ? child_setup_data : pty,
544                                        child_pid,
545                                        NULL, NULL, NULL,
546                                        &err);
547         if (!ret &&
548             directory != NULL &&
549             g_error_matches(err, G_SPAWN_ERROR, G_SPAWN_ERROR_CHDIR)) {
550                 /* try spawning in our working directory */
551                 g_clear_error(&err);
552                 ret = g_spawn_async_with_pipes(NULL,
553                                                argv, envp2,
554                                                spawn_flags,
555                                                child_setup ? child_setup
556                                                            : (GSpawnChildSetupFunc) vte_pty_child_setup,
557                                                child_setup ? child_setup_data : pty,
558                                                child_pid,
559                                                NULL, NULL, NULL,
560                                                &err);
561         }
562 
563         g_strfreev (envp2);
564 
565         if (ret)
566                 return TRUE;
567 
568         g_propagate_error (error, err);
569         return FALSE;
570 }
571 
572 /*
573  * __vte_pty_fork:
574  * @pty: a #VtePty
575  * @pid: (out) a location to store a #GPid, or %NULL
576  * @error: a location to store a #GError, or %NULL
577  *
578  * Forks and calls vte_pty_child_setup() in the child.
579  *
580  * Returns: %TRUE on success, or %FALSE on failure with @error filled in
581  */
582 gboolean
__vte_pty_fork(VtePty * pty,GPid * pid,GError ** error)583 __vte_pty_fork(VtePty *pty,
584                GPid *pid,
585                GError **error)
586 {
587 #ifdef HAVE_FORK
588         gboolean ret = TRUE;
589 
590         *pid = fork();
591         switch (*pid) {
592                 case -1:
593                         g_set_error(error,
594                                     G_SPAWN_ERROR,
595                                     G_SPAWN_ERROR_FAILED,
596                                     "Unable to fork: %s",
597                                     g_strerror(errno));
598                         ret = FALSE;
599                 case 0: /* child */
600                         vte_pty_child_setup(pty);
601                         break;
602                 default: /* parent */
603                         break;
604         }
605 
606 	return ret;
607 #else /* !HAVE_FORK */
608         g_set_error_literal(error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
609                             "No fork implementation");
610         return FALSE;
611 #endif /* HAVE_FORK */
612 }
613 
614 /**
615  * vte_pty_set_size:
616  * @pty: a #VtePty
617  * @rows: the desired number of rows
618  * @columns: the desired number of columns
619  * @error: (allow-none); return location to store a #GError, or %NULL
620  *
621  * Attempts to resize the pseudo terminal's window size.  If successful, the
622  * OS kernel will send #SIGWINCH to the child process group.
623  *
624  * If setting the window size failed, @error will be set to a #GIOError.
625  *
626  * Returns: %TRUE on success, %FALSE on failure with @error filled in
627  *
628  * Since: 0.26
629  */
630 gboolean
vte_pty_set_size(VtePty * pty,int rows,int columns,GError ** error)631 vte_pty_set_size(VtePty *pty,
632                  int rows,
633                  int columns,
634                  GError **error)
635 {
636         VtePtyPrivate *priv;
637 	struct winsize size;
638         int master;
639 	int ret;
640 
641         g_return_val_if_fail(VTE_IS_PTY(pty), FALSE);
642 
643         priv = pty->priv;
644 
645         master = vte_pty_get_fd(pty);
646 
647 	memset(&size, 0, sizeof(size));
648 	size.ws_row = rows > 0 ? rows : 24;
649 	size.ws_col = columns > 0 ? columns : 80;
650 	_vte_debug_print(VTE_DEBUG_PTY,
651 			"Setting size on fd %d to (%d,%d).\n",
652 			master, columns, rows);
653 	ret = ioctl(master, TIOCSWINSZ, &size);
654 	if (ret != 0) {
655                 int errsv = errno;
656 
657                 g_set_error(error, G_IO_ERROR,
658                             g_io_error_from_errno(errsv),
659                             "Failed to set window size: %s",
660                             g_strerror(errsv));
661 
662 		_vte_debug_print(VTE_DEBUG_PTY,
663 				"Failed to set size on %d: %s.\n",
664 				master, g_strerror(errsv));
665 
666                 errno = errsv;
667 
668                 return FALSE;
669 	}
670 
671         return TRUE;
672 }
673 
674 /**
675  * vte_pty_get_size:
676  * @pty: a #VtePty
677  * @rows: (out) (allow-none): a location to store the number of rows, or %NULL
678  * @columns: (out) (allow-none): a location to store the number of columns, or %NULL
679  * @error: return location to store a #GError, or %NULL
680  *
681  * Reads the pseudo terminal's window size.
682  *
683  * If getting the window size failed, @error will be set to a #GIOError.
684  *
685  * Returns: %TRUE on success, %FALSE on failure with @error filled in
686  *
687  * Since: 0.26
688  */
689 gboolean
vte_pty_get_size(VtePty * pty,int * rows,int * columns,GError ** error)690 vte_pty_get_size(VtePty *pty,
691                  int *rows,
692                  int *columns,
693                  GError **error)
694 {
695         VtePtyPrivate *priv;
696 	struct winsize size;
697         int master;
698 	int ret;
699 
700         g_return_val_if_fail(VTE_IS_PTY(pty), FALSE);
701 
702         priv = pty->priv;
703 
704         master = vte_pty_get_fd(pty);
705 
706 	memset(&size, 0, sizeof(size));
707 	ret = ioctl(master, TIOCGWINSZ, &size);
708 	if (ret == 0) {
709 		if (columns != NULL) {
710 			*columns = size.ws_col;
711 		}
712 		if (rows != NULL) {
713 			*rows = size.ws_row;
714 		}
715 		_vte_debug_print(VTE_DEBUG_PTY,
716 				"Size on fd %d is (%d,%d).\n",
717 				master, size.ws_col, size.ws_row);
718                 return TRUE;
719 	} else {
720                 int errsv = errno;
721 
722                 g_set_error(error, G_IO_ERROR,
723                             g_io_error_from_errno(errsv),
724                             "Failed to get window size: %s",
725                             g_strerror(errsv));
726 
727 		_vte_debug_print(VTE_DEBUG_PTY,
728 				"Failed to read size from fd %d: %s\n",
729 				master, g_strerror(errsv));
730 
731                 errno = errsv;
732 
733                 return FALSE;
734 	}
735 }
736 
737 /*
738  * _vte_pty_ptsname:
739  * @master: file descriptor to the PTY master
740  * @error: a location to store a #GError, or %NULL
741  *
742  * Returns: a newly allocated string containing the file name of the
743  *   PTY slave device, or %NULL on failure with @error filled in
744  */
745 static char *
_vte_pty_ptsname(int master,GError ** error)746 _vte_pty_ptsname(int master,
747                  GError **error)
748 {
749 #if defined(HAVE_PTSNAME_R)
750 	gsize len = 1024;
751 	char *buf = NULL;
752 	int i, errsv;
753 	do {
754 		buf = g_malloc0(len);
755 		i = ptsname_r(master, buf, len - 1);
756 		switch (i) {
757 		case 0:
758 			/* Return the allocated buffer with the name in it. */
759 			_vte_debug_print(VTE_DEBUG_PTY,
760 					"PTY slave is `%s'.\n", buf);
761 			return buf;
762 			break;
763 		default:
764                         errsv = errno;
765 			g_free(buf);
766                         errno = errsv;
767 			buf = NULL;
768 			break;
769 		}
770 		len *= 2;
771 	} while ((i != 0) && (errno == ERANGE));
772 
773         g_set_error(error, VTE_PTY_ERROR, VTE_PTY_ERROR_PTY98_FAILED,
774                     "%s failed: %s", "ptsname_r", g_strerror(errno));
775         return NULL;
776 #elif defined(HAVE_PTSNAME)
777 	char *p;
778 	if ((p = ptsname(master)) != NULL) {
779 		_vte_debug_print(VTE_DEBUG_PTY, "PTY slave is `%s'.\n", p);
780 		return g_strdup(p);
781 	}
782 
783         g_set_error(error, VTE_PTY_ERROR, VTE_PTY_ERROR_PTY98_FAILED,
784                     "%s failed: %s", "ptsname", g_strerror(errno));
785         return NULL;
786 #elif defined(TIOCGPTN)
787 	int pty = 0;
788 	if (ioctl(master, TIOCGPTN, &pty) == 0) {
789 		_vte_debug_print(VTE_DEBUG_PTY,
790 				"PTY slave is `/dev/pts/%d'.\n", pty);
791 		return g_strdup_printf("/dev/pts/%d", pty);
792 	}
793 
794         g_set_error(error, VTE_PTY_ERROR, VTE_PTY_ERROR_PTY98_FAILED,
795                     "%s failed: %s", "ioctl(TIOCGPTN)", g_strerror(errno));
796         return NULL;
797 #else
798 #error no ptsname implementation for this platform
799 #endif
800 }
801 
802 /*
803  * _vte_pty_getpt:
804  * @error: a location to store a #GError, or %NULL
805  *
806  * Opens a file descriptor for the next available PTY master.
807  * Sets the descriptor to blocking mode!
808  *
809  * Returns: a new file descriptor, or %-1 on failure
810  */
811 static int
_vte_pty_getpt(GError ** error)812 _vte_pty_getpt(GError **error)
813 {
814 	int fd, flags, rv;
815 
816 #ifdef HAVE_POSIX_OPENPT
817 	/* Call the system's function for allocating a pty. */
818 	fd = posix_openpt(O_RDWR | O_NOCTTY);
819 #elif defined(HAVE_GETPT)
820 	/* Call the system's function for allocating a pty. */
821 	fd = getpt();
822 #else
823 	/* Try to allocate a pty by accessing the pty master multiplex. */
824 	fd = open("/dev/ptmx", O_RDWR | O_NOCTTY);
825 	if ((fd == -1) && (errno == ENOENT)) {
826 		fd = open("/dev/ptc", O_RDWR | O_NOCTTY); /* AIX */
827 	}
828 #endif
829         if (fd == -1) {
830                 g_set_error (error, VTE_PTY_ERROR,
831                              VTE_PTY_ERROR_PTY98_FAILED,
832                              "%s failed: %s", "getpt", g_strerror(errno));
833                 return -1;
834         }
835 
836         rv = fcntl(fd, F_GETFL, 0);
837         if (rv < 0) {
838                 int errsv = errno;
839                 g_set_error(error, VTE_PTY_ERROR,
840                             VTE_PTY_ERROR_PTY98_FAILED,
841                             "%s failed: %s", "fcntl(F_GETFL)", g_strerror(errno));
842                 close(fd);
843                 errno = errsv;
844                 return -1;
845         }
846 
847 	/* Set it to blocking. */
848         /* FIXMEchpe: why?? vte_terminal_set_pty does the inverse... */
849         flags = rv & ~(O_NONBLOCK);
850         rv = fcntl(fd, F_SETFL, flags);
851         if (rv < 0) {
852                 int errsv = errno;
853                 g_set_error(error, VTE_PTY_ERROR,
854                             VTE_PTY_ERROR_PTY98_FAILED,
855                             "%s failed: %s", "fcntl(F_SETFL)", g_strerror(errno));
856                 close(fd);
857                 errno = errsv;
858                 return -1;
859         }
860 
861 	return fd;
862 }
863 
864 static gboolean
_vte_pty_grantpt(int master,GError ** error)865 _vte_pty_grantpt(int master,
866                  GError **error)
867 {
868 #ifdef HAVE_GRANTPT
869         int rv;
870 
871         rv = grantpt(master);
872         if (rv != 0) {
873                 int errsv = errno;
874                 g_set_error(error, VTE_PTY_ERROR, VTE_PTY_ERROR_PTY98_FAILED,
875                             "%s failed: %s", "grantpt", g_strerror(errsv));
876                 errno = errsv;
877                 return FALSE;
878         }
879 #endif
880         return TRUE;
881 }
882 
883 static gboolean
_vte_pty_unlockpt(int fd,GError ** error)884 _vte_pty_unlockpt(int fd,
885                   GError **error)
886 {
887         int rv;
888 #ifdef HAVE_UNLOCKPT
889 	rv = unlockpt(fd);
890         if (rv != 0) {
891                 int errsv = errno;
892                 g_set_error(error, VTE_PTY_ERROR, VTE_PTY_ERROR_PTY98_FAILED,
893                             "%s failed: %s", "unlockpt", g_strerror(errsv));
894                 errno = errsv;
895                 return FALSE;
896         }
897         return TRUE;
898 #elif defined(TIOCSPTLCK)
899 	int zero = 0;
900 	rv = ioctl(fd, TIOCSPTLCK, &zero);
901         if (rv != 0) {
902                 int errsv = errno;
903                 g_set_error(error, VTE_PTY_ERROR, VTE_PTY_ERROR_PTY98_FAILED,
904                             "%s failed: %s", "ioctl(TIOCSPTLCK)", g_strerror(errsv));
905                 errno = errsv;
906                 return FALSE;
907         }
908         return TRUE;
909 #else
910 #error no unlockpt implementation for this platform
911 #endif
912 }
913 
914 /*
915  * _vte_pty_open_unix98:
916  * @pty: a #VtePty
917  * @error: a location to store a #GError, or %NULL
918  *
919  * Opens a new file descriptor to a new PTY master.
920  *
921  * Returns: %TRUE on success, %FALSE on failure with @error filled in
922  */
923 static gboolean
_vte_pty_open_unix98(VtePty * pty,GError ** error)924 _vte_pty_open_unix98(VtePty *pty,
925                      GError **error)
926 {
927         VtePtyPrivate *priv = pty->priv;
928 	int fd;
929 	char *buf;
930 
931 	/* Attempt to open the master. */
932 	fd = _vte_pty_getpt(error);
933 	if (fd == -1)
934                 return FALSE;
935 
936 	_vte_debug_print(VTE_DEBUG_PTY, "Allocated pty on fd %d.\n", fd);
937 
938         /* Read the slave number and unlock it. */
939         if ((buf = _vte_pty_ptsname(fd, error)) == NULL ||
940             !_vte_pty_grantpt(fd, error) ||
941             !_vte_pty_unlockpt(fd, error)) {
942                 int errsv = errno;
943                 _vte_debug_print(VTE_DEBUG_PTY,
944                                 "PTY setup failed, bailing.\n");
945                 close(fd);
946                 errno = errsv;
947                 return FALSE;
948         }
949 
950         priv->pty_fd = fd;
951         priv->child_setup_data.mode = TTY_OPEN_BY_NAME;
952         priv->child_setup_data.tty.name = buf;
953         priv->using_helper = FALSE;
954 
955         return TRUE;
956 }
957 
958 #ifdef VTE_USE_GNOME_PTY_HELPER
959 #ifdef HAVE_RECVMSG
960 static void
_vte_pty_read_ptypair(int tunnel,int * parentfd,int * childfd)961 _vte_pty_read_ptypair(int tunnel, int *parentfd, int *childfd)
962 {
963 	int i, ret;
964 	char control[LINE_MAX], iobuf[LINE_MAX];
965 	struct cmsghdr *cmsg;
966 	struct msghdr msg;
967 	struct iovec vec;
968 
969 	for (i = 0; i < 2; i++) {
970 		vec.iov_base = iobuf;
971 		vec.iov_len = sizeof(iobuf);
972 		msg.msg_name = NULL;
973 		msg.msg_namelen = 0;
974 		msg.msg_iov = &vec;
975 		msg.msg_iovlen = 1;
976 		msg.msg_control = control;
977 		msg.msg_controllen = sizeof(control);
978 		ret = recvmsg(tunnel, &msg, PTY_RECVMSG_FLAGS);
979 		if (ret == -1) {
980 			return;
981 		}
982 		for (cmsg = CMSG_FIRSTHDR(&msg);
983 		     cmsg != NULL;
984 		     cmsg = CMSG_NXTHDR(&msg, cmsg)) {
985 			if (cmsg->cmsg_type == SCM_RIGHTS) {
986 				memcpy(&ret, CMSG_DATA(cmsg), sizeof(ret));
987 				switch (i) {
988 					case 0:
989 						*parentfd = ret;
990 						break;
991 					case 1:
992 						*childfd = ret;
993 						break;
994 					default:
995 						g_assert_not_reached();
996 						break;
997 				}
998 			}
999 		}
1000 	}
1001 }
1002 #elif defined (I_RECVFD)
1003 static void
_vte_pty_read_ptypair(int tunnel,int * parentfd,int * childfd)1004 _vte_pty_read_ptypair(int tunnel, int *parentfd, int *childfd)
1005 {
1006 	int i;
1007 	if (ioctl(tunnel, I_RECVFD, &i) == -1) {
1008 		return;
1009 	}
1010 	*parentfd = i;
1011 	if (ioctl(tunnel, I_RECVFD, &i) == -1) {
1012 		return;
1013 	}
1014 	*childfd = i;
1015 }
1016 #endif
1017 
1018 #ifdef HAVE_SOCKETPAIR
1019 static int
_vte_pty_pipe_open(int * a,int * b)1020 _vte_pty_pipe_open(int *a, int *b)
1021 {
1022 	int p[2], ret = -1;
1023 #ifdef PF_UNIX
1024 #ifdef SOCK_STREAM
1025 	ret = socketpair(PF_UNIX, SOCK_STREAM, 0, p);
1026 #else
1027 #ifdef SOCK_DGRAM
1028 	ret = socketpair(PF_UNIX, SOCK_DGRAM, 0, p);
1029 #endif
1030 #endif
1031 	if (ret == 0) {
1032 		*a = p[0];
1033 		*b = p[1];
1034 		return 0;
1035 	}
1036 #endif
1037 	return ret;
1038 }
1039 #else
1040 static int
_vte_pty_pipe_open(int * a,int * b)1041 _vte_pty_pipe_open(int *a, int *b)
1042 {
1043 	int p[2], ret = -1;
1044 
1045 	ret = pipe(p);
1046 
1047 	if (ret == 0) {
1048 		*a = p[0];
1049 		*b = p[1];
1050 	}
1051 	return ret;
1052 }
1053 #endif
1054 
1055 /* Like read, but hide EINTR and EAGAIN. */
1056 static ssize_t
n_read(int fd,void * buffer,size_t count)1057 n_read(int fd, void *buffer, size_t count)
1058 {
1059 	size_t n = 0;
1060 	char *buf = buffer;
1061 	int i;
1062 	while (n < count) {
1063 		i = read(fd, buf + n, count - n);
1064 		switch (i) {
1065 		case 0:
1066 			return n;
1067 		case -1:
1068 			switch (errno) {
1069 			case EINTR:
1070 			case EAGAIN:
1071 #ifdef ERESTART
1072 			case ERESTART:
1073 #endif
1074 				break;
1075 			default:
1076 				return -1;
1077 			}
1078 			break;
1079 		default:
1080 			n += i;
1081 			break;
1082 		}
1083 	}
1084 	return n;
1085 }
1086 
1087 /* Like write, but hide EINTR and EAGAIN. */
1088 static ssize_t
n_write(int fd,const void * buffer,size_t count)1089 n_write(int fd, const void *buffer, size_t count)
1090 {
1091 	size_t n = 0;
1092 	const char *buf = buffer;
1093 	int i;
1094 	while (n < count) {
1095 		i = write(fd, buf + n, count - n);
1096 		switch (i) {
1097 		case 0:
1098 			return n;
1099 		case -1:
1100 			switch (errno) {
1101 			case EINTR:
1102 			case EAGAIN:
1103 #ifdef ERESTART
1104 			case ERESTART:
1105 #endif
1106 				break;
1107 			default:
1108 				return -1;
1109 			}
1110 			break;
1111 		default:
1112 			n += i;
1113 			break;
1114 		}
1115 	}
1116 	return n;
1117 }
1118 
1119 /*
1120  * _vte_pty_stop_helper:
1121  *
1122  * Terminates the running GNOME PTY helper.
1123  */
1124 static void
_vte_pty_stop_helper(void)1125 _vte_pty_stop_helper(void)
1126 {
1127 	if (_vte_pty_helper_started) {
1128 		close(_vte_pty_helper_tunnel);
1129 		_vte_pty_helper_tunnel = -1;
1130 		kill(_vte_pty_helper_pid, SIGTERM);
1131 		_vte_pty_helper_pid = -1;
1132 		_vte_pty_helper_started = FALSE;
1133 	}
1134 }
1135 
1136 /*
1137  * _vte_pty_start_helper:
1138  * @error: a location to store a #GError, or %NULL
1139  *
1140  * Starts the GNOME PTY helper process, if it is not already running.
1141  *
1142  * Returns: %TRUE if the helper was already started, or starting it succeeded,
1143  *   %FALSE on failure with @error filled in
1144  */
1145 static gboolean
_vte_pty_start_helper(GError ** error)1146 _vte_pty_start_helper(GError **error)
1147 {
1148 	int i, errsv;
1149         int tunnel = -1;
1150         int tmp[2] = { -1, -1 };
1151 
1152         if (_vte_pty_helper_started)
1153                 return TRUE;
1154 
1155 	/* Create a communication link for use with the helper. */
1156 	tmp[0] = open("/dev/null", O_RDONLY);
1157 	if (tmp[0] == -1) {
1158 		goto failure;
1159 	}
1160 	tmp[1] = open("/dev/null", O_RDONLY);
1161 	if (tmp[1] == -1) {
1162 		goto failure;
1163 	}
1164 	if (_vte_pty_pipe_open(&_vte_pty_helper_tunnel, &tunnel) != 0) {
1165 		goto failure;
1166 	}
1167 	close(tmp[0]);
1168 	close(tmp[1]);
1169         tmp[0] = tmp[1] = -1;
1170 
1171 	/* Now fork and start the helper. */
1172 	_vte_pty_helper_pid = fork();
1173 	if (_vte_pty_helper_pid == -1) {
1174 		goto failure;
1175 	}
1176 	if (_vte_pty_helper_pid == 0) {
1177 		/* Child.  Close descriptors.  No need to close all,
1178 		 * gnome-pty-helper does that anyway. */
1179 		for (i = 0; i < 3; i++) {
1180 			close(i);
1181 		}
1182 		/* Reassign the socket pair to stdio. */
1183 		dup2(tunnel, STDIN_FILENO);
1184 		dup2(tunnel, STDOUT_FILENO);
1185 		close(tunnel);
1186 		close(_vte_pty_helper_tunnel);
1187 		/* Exec our helper. */
1188 		execl(LIBEXECDIR "/gnome-pty-helper",
1189 		      "gnome-pty-helper", NULL);
1190 		/* Bail. */
1191 		_exit(1);
1192 	}
1193 	close(tunnel);
1194 	atexit(_vte_pty_stop_helper);
1195 
1196         _vte_pty_helper_started = TRUE;
1197 	return TRUE;
1198 
1199 failure:
1200         errsv = errno;
1201 
1202         g_set_error(error, VTE_PTY_ERROR,
1203                     VTE_PTY_ERROR_PTY_HELPER_FAILED,
1204                     "Failed to start gnome-pty-helper: %s",
1205                     g_strerror (errsv));
1206 
1207         if (tmp[0] != -1)
1208                 close(tmp[0]);
1209         if (tmp[1] != -1)
1210                 close(tmp[1]);
1211         if (tunnel != -1)
1212                 close(tunnel);
1213         if (_vte_pty_helper_tunnel != -1)
1214                 close(_vte_pty_helper_tunnel);
1215 
1216         _vte_pty_helper_pid = -1;
1217         _vte_pty_helper_tunnel = -1;
1218 
1219         errno = errsv;
1220         return FALSE;
1221 }
1222 
1223 /*
1224  * _vte_pty_helper_ops_from_flags:
1225  * @flags: flags from #VtePtyFlags
1226  *
1227  * Translates @flags into the corresponding op code for the
1228  * GNOME PTY helper.
1229  *
1230  * Returns: the #GnomePtyOps corresponding to @flags
1231  */
1232 static int
_vte_pty_helper_ops_from_flags(VtePtyFlags flags)1233 _vte_pty_helper_ops_from_flags (VtePtyFlags flags)
1234 {
1235 	int op = 0;
1236 	static const int opmap[8] = {
1237 		GNOME_PTY_OPEN_NO_DB_UPDATE,		/* 0 0 0 */
1238 		GNOME_PTY_OPEN_PTY_LASTLOG,		/* 0 0 1 */
1239 		GNOME_PTY_OPEN_PTY_UTMP,		/* 0 1 0 */
1240 		GNOME_PTY_OPEN_PTY_LASTLOGUTMP,		/* 0 1 1 */
1241 		GNOME_PTY_OPEN_PTY_WTMP,		/* 1 0 0 */
1242 		GNOME_PTY_OPEN_PTY_LASTLOGWTMP,		/* 1 0 1 */
1243 		GNOME_PTY_OPEN_PTY_UWTMP,		/* 1 1 0 */
1244 		GNOME_PTY_OPEN_PTY_LASTLOGUWTMP,	/* 1 1 1 */
1245 	};
1246 	if ((flags & VTE_PTY_NO_LASTLOG) == 0) {
1247 		op += 1;
1248 	}
1249 	if ((flags & VTE_PTY_NO_UTMP) == 0) {
1250 		op += 2;
1251 	}
1252 	if ((flags & VTE_PTY_NO_WTMP) == 0) {
1253 		op += 4;
1254 	}
1255 	g_assert(op >= 0 && op < (int) G_N_ELEMENTS(opmap));
1256 
1257         return opmap[op];
1258 }
1259 
1260 /*
1261  * _vte_pty_open_with_helper:
1262  * @pty: a #VtePty
1263  * @error: a location to store a #GError, or %NULL
1264  *
1265  * Opens a new file descriptor to a new PTY master using the
1266  * GNOME PTY helper.
1267  *
1268  * Returns: %TRUE on success, %FALSE on failure with @error filled in
1269  */
1270 static gboolean
_vte_pty_open_with_helper(VtePty * pty,GError ** error)1271 _vte_pty_open_with_helper(VtePty *pty,
1272                           GError **error)
1273 {
1274         VtePtyPrivate *priv = pty->priv;
1275 	GnomePtyOps ops;
1276 	int ret;
1277 	int parentfd = -1, childfd = -1;
1278 	gpointer tag;
1279 
1280 	/* We have to use the pty helper here. */
1281 	if (!_vte_pty_start_helper(error))
1282                 return FALSE;
1283 
1284 	/* Try to open a new descriptor. */
1285 
1286         ops = _vte_pty_helper_ops_from_flags(priv->flags);
1287         /* Send our request. */
1288         if (n_write(_vte_pty_helper_tunnel,
1289                     &ops, sizeof(ops)) != sizeof(ops)) {
1290                 g_set_error (error, VTE_PTY_ERROR,
1291                               VTE_PTY_ERROR_PTY_HELPER_FAILED,
1292                               "Failed to send request to gnome-pty-helper: %s",
1293                               g_strerror(errno));
1294                 return FALSE;
1295         }
1296         _vte_debug_print(VTE_DEBUG_PTY, "Sent request to helper.\n");
1297         /* Read back the response. */
1298         if (n_read(_vte_pty_helper_tunnel,
1299                     &ret, sizeof(ret)) != sizeof(ret)) {
1300                 g_set_error (error, VTE_PTY_ERROR,
1301                               VTE_PTY_ERROR_PTY_HELPER_FAILED,
1302                               "Failed to read response from gnome-pty-helper: %s",
1303                               g_strerror(errno));
1304                 return FALSE;
1305         }
1306         _vte_debug_print(VTE_DEBUG_PTY,
1307                         "Received response from helper.\n");
1308         if (ret == 0) {
1309                 g_set_error_literal (error, VTE_PTY_ERROR,
1310                                       VTE_PTY_ERROR_PTY_HELPER_FAILED,
1311                                       "gnome-pty-helper failed to open pty");
1312                 return FALSE;
1313         }
1314         _vte_debug_print(VTE_DEBUG_PTY, "Helper returns success.\n");
1315         /* Read back a tag. */
1316         if (n_read(_vte_pty_helper_tunnel,
1317                     &tag, sizeof(tag)) != sizeof(tag)) {
1318                 g_set_error (error, VTE_PTY_ERROR,
1319                               VTE_PTY_ERROR_PTY_HELPER_FAILED,
1320                               "Failed to read tag from gnome-pty-helper: %s",
1321                               g_strerror(errno));
1322                 return FALSE;
1323         }
1324         _vte_debug_print(VTE_DEBUG_PTY, "Tag = %p.\n", tag);
1325         /* Receive the master and slave ptys. */
1326         _vte_pty_read_ptypair(_vte_pty_helper_tunnel,
1327                               &parentfd, &childfd);
1328 
1329         if ((parentfd == -1) || (childfd == -1)) {
1330                 int errsv = errno;
1331 
1332                 close(parentfd);
1333                 close(childfd);
1334 
1335                 g_set_error (error, VTE_PTY_ERROR,
1336                               VTE_PTY_ERROR_PTY_HELPER_FAILED,
1337                               "Failed to read master or slave pty from gnome-pty-helper: %s",
1338                               g_strerror(errsv));
1339                 errno = errsv;
1340                 return FALSE;
1341         }
1342 
1343         _vte_debug_print(VTE_DEBUG_PTY,
1344                         "Got master pty %d and slave pty %d.\n",
1345                         parentfd, childfd);
1346 
1347         priv->using_helper = TRUE;
1348         priv->helper_tag = tag;
1349         priv->pty_fd = parentfd;
1350 
1351         priv->child_setup_data.mode = TTY_OPEN_BY_FD;
1352         priv->child_setup_data.tty.fd = childfd;
1353 
1354         return TRUE;
1355 }
1356 
1357 #endif /* VTE_USE_GNOME_PTY_HELPER */
1358 
1359 /**
1360  * vte_pty_set_utf8:
1361  * @pty: a #VtePty
1362  * @utf8: whether or not the pty is in UTF-8 mode
1363  * @error: (allow-none): return location to store a #GError, or %NULL
1364  *
1365  * Tells the kernel whether the terminal is UTF-8 or not, in case it can make
1366  * use of the info.  Linux 2.6.5 or so defines IUTF8 to make the line
1367  * discipline do multibyte backspace correctly.
1368  *
1369  * Returns: %TRUE on success, %FALSE on failure with @error filled in
1370  *
1371  * Since: 0.26
1372  */
1373 gboolean
vte_pty_set_utf8(VtePty * pty,gboolean utf8,GError ** error)1374 vte_pty_set_utf8(VtePty *pty,
1375                  gboolean utf8,
1376                  GError **error)
1377 {
1378 #if defined(HAVE_TCSETATTR) && defined(IUTF8)
1379         VtePtyPrivate *priv;
1380 	struct termios tio;
1381 	tcflag_t saved_cflag;
1382 
1383         g_return_val_if_fail(VTE_IS_PTY(pty), FALSE);
1384 
1385         priv = pty->priv;
1386         g_return_val_if_fail (priv->pty_fd > 0, FALSE);
1387 
1388         if (tcgetattr(priv->pty_fd, &tio) == -1) {
1389                 int errsv = errno;
1390                 g_set_error(error, G_IO_ERROR, g_io_error_from_errno(errsv),
1391                             "%s failed: %s", "tcgetattr", g_strerror(errsv));
1392                 errno = errsv;
1393                 return FALSE;
1394         }
1395 
1396         saved_cflag = tio.c_iflag;
1397         if (utf8) {
1398                 tio.c_iflag |= IUTF8;
1399         } else {
1400               tio.c_iflag &= ~IUTF8;
1401         }
1402 
1403         /* Only set the flag if it changes */
1404         if (saved_cflag != tio.c_iflag &&
1405             tcsetattr(priv->pty_fd, TCSANOW, &tio) == -1) {
1406                 int errsv = errno;
1407                 g_set_error(error, G_IO_ERROR, g_io_error_from_errno(errsv),
1408                             "%s failed: %s", "tcgetattr", g_strerror(errsv));
1409                 errno = errsv;
1410                 return FALSE;
1411 	}
1412 #endif
1413 
1414         return TRUE;
1415 }
1416 
1417 /**
1418  * vte_pty_close:
1419  * @pty: a #VtePty
1420  *
1421  * Cleans up the PTY, specifically any logging performed for the session.
1422  * The file descriptor to the PTY master remains open.
1423  *
1424  * Since: 0.26
1425  */
1426 void
vte_pty_close(VtePty * pty)1427 vte_pty_close (VtePty *pty)
1428 {
1429 #ifdef VTE_USE_GNOME_PTY_HELPER
1430         VtePtyPrivate *priv = pty->priv;
1431 	gpointer tag;
1432 	GnomePtyOps ops;
1433 
1434         if (!priv->using_helper)
1435                 return;
1436 
1437         /* Signal the helper that it needs to close its connection. */
1438         tag = priv->helper_tag;
1439 
1440         ops = GNOME_PTY_CLOSE_PTY;
1441         if (n_write(_vte_pty_helper_tunnel,
1442                     &ops, sizeof(ops)) != sizeof(ops)) {
1443                 return;
1444         }
1445         if (n_write(_vte_pty_helper_tunnel,
1446                     &tag, sizeof(tag)) != sizeof(tag)) {
1447                 return;
1448         }
1449 
1450         ops = GNOME_PTY_SYNCH;
1451         if (n_write(_vte_pty_helper_tunnel,
1452                     &ops, sizeof(ops)) != sizeof(ops)) {
1453                 return;
1454         }
1455         n_read(_vte_pty_helper_tunnel, &ops, 1);
1456 
1457         priv->helper_tag = NULL;
1458         priv->using_helper = FALSE;
1459 #endif
1460 }
1461 
1462 /* VTE PTY class */
1463 
1464 enum {
1465         PROP_0,
1466         PROP_FLAGS,
1467         PROP_FD,
1468         PROP_TERM
1469 };
1470 
1471 /* GInitable impl */
1472 
1473 static gboolean
vte_pty_initable_init(GInitable * initable,GCancellable * cancellable,GError ** error)1474 vte_pty_initable_init (GInitable *initable,
1475                        GCancellable *cancellable,
1476                        GError **error)
1477 {
1478         VtePty *pty = VTE_PTY (initable);
1479         VtePtyPrivate *priv = pty->priv;
1480         gboolean ret = FALSE;
1481 
1482         if (cancellable != NULL) {
1483                 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
1484                                     "Cancellable initialisation not supported");
1485                 return FALSE;
1486         }
1487 
1488         /* If we already have a (foreign) FD, we're done. */
1489         if (priv->foreign) {
1490                 g_assert(priv->pty_fd != -1);
1491                 return TRUE;
1492         }
1493 
1494 #ifdef VTE_USE_GNOME_PTY_HELPER
1495 	if ((priv->flags & VTE_PTY_NO_HELPER) == 0) {
1496                 GError *err = NULL;
1497 
1498 		ret = _vte_pty_open_with_helper(pty, &err);
1499                 g_assert(ret || err != NULL);
1500 
1501                 if (ret)
1502                         goto out;
1503 
1504                 _vte_debug_print(VTE_DEBUG_PTY,
1505                                  "_vte_pty_open_with_helper failed: %s\n",
1506                                  err->message);
1507 
1508                 /* Only do fallback if gnome-pty-helper failed! */
1509                 if ((priv->flags & VTE_PTY_NO_FALLBACK) ||
1510                     !g_error_matches(err,
1511                                      VTE_PTY_ERROR,
1512                                      VTE_PTY_ERROR_PTY_HELPER_FAILED)) {
1513                         g_propagate_error (error, err);
1514                         goto out;
1515                 }
1516 
1517                 g_error_free(err);
1518                 /* Fall back to unix98 PTY */
1519         }
1520 #else
1521         if (priv->flags & VTE_PTY_NO_FALLBACK) {
1522                 g_set_error_literal(error, VTE_PTY_ERROR, VTE_PTY_ERROR_PTY_HELPER_FAILED,
1523                                     "VTE compiled without GNOME PTY helper");
1524                 goto out;
1525         }
1526 #endif /* VTE_USE_GNOME_PTY_HELPER */
1527 
1528         ret = _vte_pty_open_unix98(pty, error);
1529 
1530   out:
1531 	_vte_debug_print(VTE_DEBUG_PTY,
1532 			"vte_pty_initable_init returning %s with ptyfd = %d\n",
1533 			ret ? "TRUE" : "FALSE", priv->pty_fd);
1534 
1535 	return ret;
1536 }
1537 
1538 static void
vte_pty_initable_iface_init(GInitableIface * iface)1539 vte_pty_initable_iface_init (GInitableIface  *iface)
1540 {
1541         iface->init = vte_pty_initable_init;
1542 }
1543 
1544 /* GObjectClass impl */
1545 
G_DEFINE_TYPE_WITH_CODE(VtePty,vte_pty,G_TYPE_OBJECT,G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,vte_pty_initable_iface_init))1546 G_DEFINE_TYPE_WITH_CODE (VtePty, vte_pty, G_TYPE_OBJECT,
1547                          G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, vte_pty_initable_iface_init))
1548 
1549 static void
1550 vte_pty_init (VtePty *pty)
1551 {
1552         VtePtyPrivate *priv;
1553 
1554         priv = pty->priv = G_TYPE_INSTANCE_GET_PRIVATE (pty, VTE_TYPE_PTY, VtePtyPrivate);
1555 
1556         priv->flags = VTE_PTY_DEFAULT;
1557         priv->pty_fd = -1;
1558         priv->foreign = FALSE;
1559         priv->using_helper = FALSE;
1560         priv->helper_tag = NULL;
1561         priv->term = vte_terminal_get_default_emulation(NULL /* that's ok, this function is just retarded */); /* already interned */
1562 }
1563 
1564 static void
vte_pty_finalize(GObject * object)1565 vte_pty_finalize (GObject *object)
1566 {
1567         VtePty *pty = VTE_PTY (object);
1568         VtePtyPrivate *priv = pty->priv;
1569 
1570         if (priv->child_setup_data.mode == TTY_OPEN_BY_FD &&
1571             priv->child_setup_data.tty.fd != -1) {
1572                 /* Close the child FD */
1573                 close(priv->child_setup_data.tty.fd);
1574         }
1575 
1576         vte_pty_close(pty);
1577 
1578         /* Close the master FD */
1579         if (priv->pty_fd != -1) {
1580                 close(priv->pty_fd);
1581         }
1582 
1583         G_OBJECT_CLASS (vte_pty_parent_class)->finalize (object);
1584 }
1585 
1586 static void
vte_pty_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)1587 vte_pty_get_property (GObject    *object,
1588                        guint       property_id,
1589                        GValue     *value,
1590                        GParamSpec *pspec)
1591 {
1592         VtePty *pty = VTE_PTY (object);
1593         VtePtyPrivate *priv = pty->priv;
1594 
1595         switch (property_id) {
1596         case PROP_FLAGS:
1597                 g_value_set_flags(value, priv->flags);
1598                 break;
1599 
1600         case PROP_FD:
1601                 g_value_set_int(value, vte_pty_get_fd(pty));
1602                 break;
1603 
1604         case PROP_TERM:
1605                 g_value_set_string(value, priv->term);
1606                 break;
1607 
1608         default:
1609                 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
1610         }
1611 }
1612 
1613 static void
vte_pty_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)1614 vte_pty_set_property (GObject      *object,
1615                        guint         property_id,
1616                        const GValue *value,
1617                        GParamSpec   *pspec)
1618 {
1619         VtePty *pty = VTE_PTY (object);
1620         VtePtyPrivate *priv = pty->priv;
1621 
1622         switch (property_id) {
1623         case PROP_FLAGS:
1624                 priv->flags = g_value_get_flags(value);
1625                 break;
1626 
1627         case PROP_FD:
1628                 priv->pty_fd = g_value_get_int(value);
1629                 priv->foreign = (priv->pty_fd != -1);
1630                 break;
1631 
1632         case PROP_TERM:
1633                 vte_pty_set_term(pty, g_value_get_string(value));
1634                 break;
1635 
1636         default:
1637                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1638         }
1639 }
1640 
1641 static void
vte_pty_class_init(VtePtyClass * klass)1642 vte_pty_class_init (VtePtyClass *klass)
1643 {
1644         GObjectClass *object_class = G_OBJECT_CLASS (klass);
1645 
1646         g_type_class_add_private(object_class, sizeof(VtePtyPrivate));
1647 
1648         object_class->set_property = vte_pty_set_property;
1649         object_class->get_property = vte_pty_get_property;
1650         object_class->finalize     = vte_pty_finalize;
1651 
1652         /**
1653          * VtePty:flags:
1654          *
1655          * Controls how the session is recorded in lastlog, utmp, and wtmp,
1656          * and whether to use the GNOME PTY helper.
1657          *
1658          * Since: 0.26
1659          */
1660         g_object_class_install_property
1661                 (object_class,
1662                  PROP_FLAGS,
1663                  g_param_spec_flags ("flags", NULL, NULL,
1664                                      VTE_TYPE_PTY_FLAGS,
1665                                      VTE_PTY_DEFAULT,
1666                                      G_PARAM_READWRITE |
1667                                      G_PARAM_CONSTRUCT_ONLY |
1668                                      G_PARAM_STATIC_STRINGS));
1669 
1670         /**
1671          * VtePty:fd:
1672          *
1673          * The file descriptor of the PTY master.
1674          *
1675          * Since: 0.26
1676          */
1677         g_object_class_install_property
1678                 (object_class,
1679                  PROP_FD,
1680                  g_param_spec_int ("fd", NULL, NULL,
1681                                    -1, G_MAXINT, -1,
1682                                    G_PARAM_READWRITE |
1683                                    G_PARAM_CONSTRUCT_ONLY |
1684                                    G_PARAM_STATIC_STRINGS));
1685 
1686         /**
1687          * VtePty:term:
1688          *
1689          * The value to set for the TERM environment variable just after
1690          * forking.
1691          *
1692          * Since: 0.26
1693          */
1694         g_object_class_install_property
1695                 (object_class,
1696                  PROP_TERM,
1697                  g_param_spec_string ("term", NULL, NULL,
1698                                       "xterm",
1699                                       G_PARAM_READWRITE |
1700                                       G_PARAM_STATIC_STRINGS));
1701 }
1702 
1703 /* public API */
1704 
1705 /**
1706  * vte_pty_error_quark:
1707  *
1708  * Error domain for VTE PTY errors. Errors in this domain will be from the #VtePtyError
1709  * enumeration. See #GError for more information on error domains.
1710  *
1711  * Returns: the error domain for VTE PTY errors
1712  *
1713  * Since: 0.26
1714  */
1715 GQuark
vte_pty_error_quark(void)1716 vte_pty_error_quark(void)
1717 {
1718   static GQuark quark = 0;
1719 
1720   if (G_UNLIKELY (quark == 0))
1721     quark = g_quark_from_static_string("vte-pty-error");
1722 
1723   return quark;
1724 }
1725 
1726 /**
1727  * vte_pty_new:
1728  * @flags: flags from #VtePtyFlags
1729  * @error: (allow-none): return location for a #GError, or %NULL
1730  *
1731  * Allocates a new pseudo-terminal.
1732  *
1733  * You can later use fork() or the g_spawn_async() family of functions
1734  * to start a process on the PTY.
1735  *
1736  * If using fork(), you MUST call vte_pty_child_setup() in the child.
1737  *
1738  * If using g_spawn_async() and friends, you MUST either use
1739  * vte_pty_child_setup() directly as the child setup function, or call
1740  * vte_pty_child_setup() from your own child setup function supplied.
1741  * Also, you MUST pass the %G_SPAWN_DO_NOT_REAP_CHILD flag.
1742  *
1743  * If GNOME PTY Helper is available and
1744  * unless some of the %VTE_PTY_NO_LASTLOG, %VTE_PTY_NO_UTMP or
1745  * %VTE_PTY_NO_WTMP flags are passed in @flags, the
1746  * session is logged in the corresponding lastlog, utmp or wtmp
1747  * system files.  When passing %VTE_PTY_NO_HELPER in @flags, the
1748  * GNOME PTY Helper is bypassed entirely.
1749  *
1750  * When passing %VTE_PTY_NO_FALLBACK in @flags,
1751  * and opening a PTY using the PTY helper fails, there will
1752  * be no fallback to allocate a PTY using Unix98 PTY functions.
1753  *
1754  * Returns: (transfer full): a new #VtePty, or %NULL on error with @error filled in
1755  *
1756  * Since: 0.26
1757  */
1758 VtePty *
vte_pty_new(VtePtyFlags flags,GError ** error)1759 vte_pty_new (VtePtyFlags flags,
1760              GError **error)
1761 {
1762         return g_initable_new (VTE_TYPE_PTY,
1763                                NULL /* cancellable */,
1764                                error,
1765                                "flags", flags,
1766                                NULL);
1767 }
1768 
1769 /**
1770  * vte_pty_new_foreign:
1771  * @fd: (transfer full): a file descriptor to the PTY
1772  * @error: (allow-none): return location for a #GError, or %NULL
1773  *
1774  * Creates a new #VtePty for the PTY master @fd.
1775  *
1776  * No entry will be made in the lastlog, utmp or wtmp system files.
1777  *
1778  * Note that the newly created #VtePty will take ownership of @fd
1779  * and close it on finalize.
1780  *
1781  * Returns: (transfer full): a new #VtePty for @fd, or %NULL on error with @error filled in
1782  *
1783  * Since: 0.26
1784  */
1785 VtePty *
vte_pty_new_foreign(int fd,GError ** error)1786 vte_pty_new_foreign (int fd,
1787                      GError **error)
1788 {
1789         g_return_val_if_fail(fd >= 0, NULL);
1790 
1791         return g_initable_new (VTE_TYPE_PTY,
1792                                NULL /* cancellable */,
1793                                error,
1794                                "fd", fd,
1795                                NULL);
1796 }
1797 
1798 /**
1799  * vte_pty_get_fd:
1800  * @pty: a #VtePty
1801  *
1802  * Returns: (transfer none): the file descriptor of the PTY master in @pty. The
1803  *   file descriptor belongs to @pty and must not be closed
1804  */
1805 int
vte_pty_get_fd(VtePty * pty)1806 vte_pty_get_fd (VtePty *pty)
1807 {
1808         VtePtyPrivate *priv;
1809 
1810         g_return_val_if_fail(VTE_IS_PTY(pty), -1);
1811 
1812         priv = pty->priv;
1813         g_return_val_if_fail(priv->pty_fd != -1, -1);
1814 
1815         return priv->pty_fd;
1816 }
1817 
1818 /**
1819  * vte_pty_set_term:
1820  * @pty: a #VtePty
1821  * @emulation: (allow-none): the name of a terminal description, or %NULL
1822  *
1823  * Sets what value of the TERM environment variable to set just after forking.
1824  *
1825  * Since: 0.26
1826  */
1827 void
vte_pty_set_term(VtePty * pty,const char * emulation)1828 vte_pty_set_term (VtePty *pty,
1829                   const char *emulation)
1830 {
1831         VtePtyPrivate *priv;
1832 
1833         g_return_if_fail(VTE_IS_PTY(pty));
1834         g_return_if_fail(emulation != NULL);
1835 
1836         priv = pty->priv;
1837         emulation = g_intern_string(emulation);
1838         if (emulation == priv->term)
1839                 return;
1840 
1841         priv->term = emulation;
1842         g_object_notify(G_OBJECT(pty), "term");
1843 }
1844 
1845 /* Reimplementation of the ugly deprecated APIs _vte_pty_*() */
1846 
1847 #ifndef VTE_DISABLE_DEPRECATED_SOURCE
1848 
1849 static GHashTable *fd_to_pty_hash = NULL;
1850 
1851 static VtePty *
get_vte_pty_for_fd(int fd)1852 get_vte_pty_for_fd (int fd)
1853 {
1854         VtePty *pty;
1855 
1856         if (fd_to_pty_hash != NULL &&
1857             (pty = g_hash_table_lookup(fd_to_pty_hash, &fd)) != NULL)
1858                 return pty;
1859 
1860         g_warning("No VtePty found for fd %d!\n", fd);
1861         return NULL;
1862 }
1863 
1864 /**
1865  * _vte_pty_open:
1866  * @child: location to store the new process's ID
1867  * @env_add: a list of environment variables to add to the child's environment
1868  * @command: name of the binary to run
1869  * @argv: arguments to pass to @command
1870  * @directory: directory to start the new command in, or %NULL
1871  * @columns: desired window columns
1872  * @rows: desired window rows
1873  * @lastlog: %TRUE if the lastlog should be updated
1874  * @utmp: %TRUE if the utmp or utmpx log should be updated
1875  * @wtmp: %TRUE if the wtmp or wtmpx log should be updated
1876  *
1877  * Starts a new copy of @command running under a psuedo-terminal, optionally in
1878  * the supplied @directory, with window size set to @rows x @columns
1879  * and variables in @env_add added to its environment.  If any combination of
1880  * @lastlog, @utmp, and @wtmp is set, then the session is logged in the
1881  * corresponding system files.
1882  *
1883  * Returns: an open file descriptor for the pty master, -1 on failure
1884  *
1885  * Deprecated: 0.26: Use #VtePty together with fork() or the g_spawn_async() family of functions instead
1886  */
1887 int
_vte_pty_open(pid_t * child,char ** env_add,const char * command,char ** argv,const char * directory,int columns,int rows,gboolean lastlog,gboolean utmp,gboolean wtmp)1888 _vte_pty_open(pid_t *child,
1889               char **env_add,
1890               const char *command,
1891               char **argv,
1892               const char *directory,
1893               int columns,
1894               int rows,
1895               gboolean lastlog,
1896               gboolean utmp,
1897               gboolean wtmp)
1898 {
1899         VtePty *pty;
1900         GPid pid;
1901         gboolean ret;
1902 
1903         pty = vte_pty_new(__vte_pty_get_pty_flags (lastlog, utmp, wtmp), NULL);
1904         if (pty == NULL)
1905                 return -1;
1906 
1907         if (command != NULL) {
1908                 char **real_argv;
1909                 GSpawnFlags spawn_flags;
1910 
1911                 spawn_flags = G_SPAWN_CHILD_INHERITS_STDIN |
1912                               G_SPAWN_SEARCH_PATH;
1913                 real_argv = __vte_pty_get_argv(command, argv, &spawn_flags);
1914                 ret = __vte_pty_spawn(pty,
1915                                       directory,
1916                                       real_argv,
1917                                       env_add,
1918                                       spawn_flags,
1919                                       NULL, NULL,
1920                                       &pid,
1921                                       NULL);
1922                 g_strfreev(real_argv);
1923         } else {
1924                 ret = __vte_pty_fork(pty, &pid, NULL);
1925         }
1926 
1927         if (!ret) {
1928                 g_object_unref(pty);
1929                 return -1;
1930         }
1931 
1932         vte_pty_set_size(pty, rows, columns, NULL);
1933 
1934         /* Stash the pty in the hash so we can later retrieve it by FD */
1935         if (fd_to_pty_hash == NULL) {
1936                 fd_to_pty_hash = g_hash_table_new_full(g_int_hash,
1937                                                        g_int_equal,
1938                                                        NULL,
1939                                                        (GDestroyNotify) g_object_unref);
1940         }
1941 
1942         g_hash_table_insert(fd_to_pty_hash, &pty->priv->pty_fd, pty /* adopt refcount */);
1943 
1944         if (child)
1945                 *child = (pid_t) pid;
1946 
1947         return vte_pty_get_fd(pty);
1948 }
1949 
1950 /**
1951  * _vte_pty_get_size:
1952  * @master: the file descriptor of the PTY master
1953  * @columns: a place to store the number of columns
1954  * @rows: a place to store the number of rows
1955  *
1956  * Attempts to read the pseudo terminal's window size.
1957  *
1958  * Returns: 0 on success, -1 on failure.
1959  *
1960  * Deprecated: 0.26: Use #VtePty and vte_pty_get_size() instead
1961  */
1962 int
_vte_pty_get_size(int master,int * columns,int * rows)1963 _vte_pty_get_size(int master,
1964                   int *columns,
1965                   int *rows)
1966 {
1967         VtePty *pty;
1968 
1969         if ((pty = get_vte_pty_for_fd(master)) == NULL)
1970                 return -1;
1971 
1972         if (vte_pty_get_size(pty, rows, columns, NULL))
1973                 return 0;
1974 
1975         return -1;
1976 }
1977 
1978 /**
1979  * _vte_pty_set_size:
1980  * @master: the file descriptor of the PTY master
1981  * @columns: the desired number of columns
1982  * @rows: the desired number of rows
1983  *
1984  * Attempts to resize the pseudo terminal's window size.  If successful, the
1985  * OS kernel will send #SIGWINCH to the child process group.
1986  *
1987  * Returns: 0 on success, -1 on failure.
1988  *
1989  * Deprecated: 0.26: Use #VtePty and vte_pty_set_size() instead
1990  */
1991 int
_vte_pty_set_size(int master,int columns,int rows)1992 _vte_pty_set_size(int master,
1993                   int columns,
1994                   int rows)
1995 {
1996         VtePty *pty;
1997 
1998         if ((pty = get_vte_pty_for_fd(master)) == NULL)
1999                 return -1;
2000 
2001         if (vte_pty_set_size(pty, rows, columns, NULL))
2002                 return 0;
2003 
2004         return -1;
2005 }
2006 
2007 /**
2008  * _vte_pty_set_utf8:
2009  * @pty: The pty master descriptor.
2010  * @utf8: Whether or not the pty is in UTF-8 mode.
2011  *
2012  * Tells the kernel whether the terminal is UTF-8 or not, in case it can make
2013  * use of the info.  Linux 2.6.5 or so defines IUTF8 to make the line
2014  * discipline do multibyte backspace correctly.
2015  *
2016  * Deprecated: 0.26: Use #VtePty and vte_pty_set_utf8() instead
2017  */
_vte_pty_set_utf8(int master,gboolean utf8)2018 void _vte_pty_set_utf8(int master,
2019                        gboolean utf8)
2020 {
2021         VtePty *pty;
2022 
2023         if ((pty = get_vte_pty_for_fd(master)) == NULL)
2024                 return;
2025 
2026         vte_pty_set_utf8(pty, utf8, NULL);
2027 }
2028 
2029 /**
2030  * _vte_pty_close:
2031  * @pty: the pty master descriptor.
2032  *
2033  * Cleans up the PTY associated with the descriptor, specifically any logging
2034  * performed for the session.  The descriptor itself remains open.
2035  *
2036  * Deprecated: 0.26: Use #VtePty and vte_pty_close() instead
2037  */
_vte_pty_close(int master)2038 void _vte_pty_close(int master)
2039 {
2040         VtePty *pty;
2041 
2042         if ((pty = get_vte_pty_for_fd(master)) == NULL)
2043                 return;
2044 
2045         /* Prevent closing the FD */
2046         pty->priv->pty_fd = -1;
2047 
2048         g_hash_table_remove(fd_to_pty_hash, &master);
2049 
2050         if (g_hash_table_size(fd_to_pty_hash) == 0) {
2051                 g_hash_table_destroy(fd_to_pty_hash);
2052                 fd_to_pty_hash = NULL;
2053         }
2054 }
2055 
2056 #endif /* !VTE_DISABLE_DEPRECATED_SOURCE */
2057