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