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