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