예제 #1
0
파일: pty.c 프로젝트: gbl/vte
/**
 * vte_pty_close:
 * @pty: a #VtePty
 *
 * Cleans up the PTY, specifically any logging performed for the session.
 * The file descriptor to the PTY master remains open.
 */
void
vte_pty_close (VtePty *pty)
{
#ifdef VTE_USE_GNOME_PTY_HELPER
        VtePtyPrivate *priv = pty->priv;
	gpointer tag;
	GnomePtyOps ops;

        if (!priv->using_helper)
                return;

        /* Signal the helper that it needs to close its connection. */
        tag = priv->helper_tag;

        ops = GNOME_PTY_CLOSE_PTY;
        if (n_write(_vte_pty_helper_tunnel,
                    &ops, sizeof(ops)) != sizeof(ops)) {
                return;
        }
        if (n_write(_vte_pty_helper_tunnel,
                    &tag, sizeof(tag)) != sizeof(tag)) {
                return;
        }

        ops = GNOME_PTY_SYNCH;
        if (n_write(_vte_pty_helper_tunnel,
                    &ops, sizeof(ops)) != sizeof(ops)) {
                return;
        }
        n_read(_vte_pty_helper_tunnel, &ops, 1);

        priv->helper_tag = NULL;
        priv->using_helper = FALSE;
#endif
}
예제 #2
0
파일: pty_open.c 프로젝트: GNOME/vinagre
int
main(int argc, char **argv)
{
	pid_t child = 0;
	char c;
	int ret;
	signal(SIGCHLD, sigchld_handler);
	fd = pty_open(&child, 0, NULL,
		      (argc > 1) ? argv[1] : NULL,
		      (argc > 1) ? argv + 1 : NULL,
		      NULL,
		      0, 0,
		      NULL, NULL, NULL);
	if (child == 0) {
		int i;
		for (i = 0; ; i++) {
			switch (i % 3) {
			case 0:
			case 1:
				fprintf(stdout, "%d\n", i);
				break;
			case 2:
				fprintf(stderr, "%d\n", i);
				break;
			default:
				g_assert_not_reached();
				break;
			}
			sleep(1);
		}
	}
	g_print("Child pid is %d.\n", (int)child);
	do {
		ret = n_read(fd, &c, 1);
		if (ret == 0) {
			break;
		}
		if ((ret == -1) && (errno != EAGAIN) && (errno != EINTR)) {
			break;
		}
		if (argc < 2) {
			n_write(STDOUT_FILENO, "[", 1);
		}
		n_write(STDOUT_FILENO, &c, 1);
		if (argc < 2) {
			n_write(STDOUT_FILENO, "]", 1);
		}
	} while (TRUE);
	return 0;
}
예제 #3
0
int ntttcp_server_select(struct ntttcp_stream_server *ss)
{
	int err_code = NO_ERROR;
	char *log    = NULL;
	bool verbose_log = ss->verbose;

	int n_fds = 0, newfd, current_fd = 0;
	char *buffer;  //receive buffer
	uint64_t nbytes;   //bytes read
	int bytes_to_be_read = 0;  //read bytes from socket
	fd_set read_set, write_set;

	struct sockaddr_storage peer_addr, local_addr; //for remote peer, and local address
	socklen_t peer_addr_size, local_addr_size;
	char *ip_address_str;
	int ip_addr_max_size;

	if ( (buffer = (char *)malloc(ss->recv_buf_size)) == (char *)NULL) {
		PRINT_ERR("cannot allocate memory for receive buffer");
		return ERROR_MEMORY_ALLOC;
	}
	ip_addr_max_size = (ss->domain == AF_INET? INET_ADDRSTRLEN : INET6_ADDRSTRLEN);
	if ( (ip_address_str = (char *)malloc(ip_addr_max_size)) == (char *)NULL) {
		PRINT_ERR("cannot allocate memory for ip address of peer");
		free(buffer);
		return ERROR_MEMORY_ALLOC;
	}

	/* accept new client, receive data from client */
	while (1) {
		memcpy(&read_set, &ss->read_set, sizeof(fd_set));
		memcpy(&write_set, &ss->write_set, sizeof(fd_set));

		/* we are notified by select() */
		n_fds = select(ss->max_fd + 1, &read_set, NULL, NULL, NULL);
		if (n_fds < 0 && errno != EINTR) {
			PRINT_ERR("error happened when select()");
			err_code = ERROR_SELECT;
			continue;
		}

		/*run through the existing connections looking for data to be read*/
		for (current_fd = 0; current_fd <= ss->max_fd; current_fd++){
			if ( !FD_ISSET(current_fd, &read_set) )
				continue;

			/* then, we got one fd to hanle */
			/* a NEW connection coming */
			if (current_fd == ss->listener) {
 				/* handle new connections */
				peer_addr_size = sizeof(peer_addr);
				if ((newfd = accept(ss->listener, (struct sockaddr *) &peer_addr, &peer_addr_size)) < 0 ) {
					err_code = ERROR_ACCEPT;
					break;
				}

				/* then we got a new connection */
				if (set_socket_non_blocking(newfd) == -1) {
					asprintf(&log, "cannot set the new socket as non-blocking: %d", newfd);
					PRINT_DBG_FREE(log);
				}
				FD_SET(newfd, &ss->read_set); /* add the new one to read_set */
				if (newfd > ss->max_fd) {
					/* update the maximum */
					ss->max_fd = newfd;
				}

				/* print out new connection info */
				local_addr_size = sizeof(local_addr);
				if (getsockname(newfd, (struct sockaddr *) &local_addr, &local_addr_size) != 0) {
					asprintf(&log, "failed to get local address information for the new socket: %d", newfd);
					PRINT_DBG_FREE(log);
				}
				else{
					asprintf(&log, "New connection: %s:%d --> local:%d [socket %d]",
							ip_address_str = retrive_ip_address_str(&peer_addr, ip_address_str, ip_addr_max_size),
							ntohs( ss->domain == AF_INET ?
									((struct sockaddr_in *)&peer_addr)->sin_port
									:((struct sockaddr_in6 *)&peer_addr)->sin6_port),
							ntohs( ss->domain == AF_INET ?
									((struct sockaddr_in *)&local_addr)->sin_port
									:((struct sockaddr_in6 *)&local_addr)->sin6_port),
							newfd);
					PRINT_DBG_FREE(log);
				}

				//if there is no synch thread, if any new connection coming, indicates ss started
				if ( ss->no_synch )
					turn_on_light();
				//else, leave the sync thread to fire the trigger
			}
			/* handle data from an EXISTING client */
			else{
				bzero(buffer, ss->recv_buf_size);
				bytes_to_be_read = ss->is_sync_thread ? 1 : ss->recv_buf_size;

				/* got error or connection closed by client */
				if ((nbytes = n_read(current_fd, buffer, bytes_to_be_read)) <= 0) {
					if (nbytes == 0) {
						asprintf(&log, "socket closed: %d", current_fd);
						PRINT_DBG_FREE(log);
					}
					else{
						asprintf(&log, "error: cannot read data from socket: %d", current_fd);
						PRINT_INFO_FREE(log);
						err_code = ERROR_NETWORK_READ;
						/* need to continue test and check other socket, so don't end the test */
					}
					close(current_fd);
					FD_CLR(current_fd, &ss->read_set); /* remove from master set when finished */
				}
				/* report how many bytes received */
				else{
					__sync_fetch_and_add(&(ss->total_bytes_transferred), nbytes);
				}
			}
		}
	}

	free(buffer);
	free(ip_address_str);
	close(ss->listener);
	return err_code;
}
예제 #4
0
int ntttcp_server_epoll(struct ntttcp_stream_server *ss)
{
	int err_code = NO_ERROR;
	char *log    = NULL;
	bool verbose_log = ss->verbose;

	int efd = 0, n_fds = 0, newfd = 0, current_fd = 0;
	char *buffer;  //receive buffer
	uint64_t nbytes;   //bytes read
	int bytes_to_be_read = 0;  //read bytes from socket
	struct epoll_event event, *events;

	struct sockaddr_storage peer_addr, local_addr; //for remote peer, and local address
	socklen_t peer_addr_size, local_addr_size;
	char *ip_address_str;
	int ip_addr_max_size;
	int i = 0;

	if ( (buffer = (char *)malloc(ss->recv_buf_size)) == (char *)NULL) {
		PRINT_ERR("cannot allocate memory for receive buffer");
		return ERROR_MEMORY_ALLOC;
	}
	ip_addr_max_size = (ss->domain == AF_INET? INET_ADDRSTRLEN : INET6_ADDRSTRLEN);
	if ( (ip_address_str = (char *)malloc(ip_addr_max_size)) == (char *)NULL) {
		PRINT_ERR("cannot allocate memory for ip address of peer");
		free(buffer);
		return ERROR_MEMORY_ALLOC;
	}

	efd = epoll_create1 (0);
	if (efd == -1) {
		PRINT_ERR("epoll_create1 failed");
		free(buffer);
		free(ip_address_str);
		return ERROR_EPOLL;
	}

	event.data.fd = ss->listener;
	event.events = EPOLLIN;
	if (epoll_ctl(efd, EPOLL_CTL_ADD, ss->listener, &event) != 0) {
		PRINT_ERR("epoll_ctl failed");
		free(buffer);
		free(ip_address_str);
		close(efd);
		return ERROR_EPOLL;
	}

	/* Buffer where events are returned */
	events = calloc (MAX_EPOLL_EVENTS, sizeof event);

	while (1) {
		n_fds = epoll_wait (efd, events, MAX_EPOLL_EVENTS, -1);
		for (i = 0; i < n_fds; i++) {
			current_fd = events[i].data.fd;

			if ((events[i].events & EPOLLERR) ||
			    (events[i].events & EPOLLHUP) ||
			    (!(events[i].events & EPOLLIN))) {
				/* An error has occured on this fd, or the socket is not ready for reading */
				PRINT_ERR("error happened on the associated connection");
				close (current_fd);
				continue;
			}

			/* then, we got one fd to hanle */
			/* a NEW connection coming */
			if (current_fd == ss->listener) {
				/* We have a notification on the listening socket, which means one or more incoming connections. */
				while (1) {
					peer_addr_size = sizeof (peer_addr);
					newfd = accept (ss->listener, (struct sockaddr *) &peer_addr, &peer_addr_size);
					if (newfd == -1) {
						if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
							/* We have processed all incoming connections. */
							break;
						}
						else {
							PRINT_ERR("error to accept new connections");
							break;
						}
					}
					if (set_socket_non_blocking(newfd) == -1) {
						asprintf(&log, "cannot set the new socket as non-blocking: %d", newfd);
						PRINT_DBG_FREE(log);
					}

					local_addr_size = sizeof(local_addr);
					if (getsockname(newfd, (struct sockaddr *) &local_addr, &local_addr_size) != 0) {
						asprintf(&log, "failed to get local address information for the new socket: %d", newfd);
						PRINT_DBG_FREE(log);
					}
					else {
						asprintf(&log, "New connection: %s:%d --> local:%d [socket %d]",
								ip_address_str = retrive_ip_address_str(&peer_addr, ip_address_str, ip_addr_max_size),
								ntohs( ss->domain == AF_INET ?
										((struct sockaddr_in *)&peer_addr)->sin_port
										:((struct sockaddr_in6 *)&peer_addr)->sin6_port),
								ntohs( ss->domain == AF_INET ?
										((struct sockaddr_in *)&local_addr)->sin_port
										:((struct sockaddr_in6 *)&local_addr)->sin6_port),
								newfd);
						PRINT_DBG_FREE(log);
					}

					event.data.fd = newfd;
					event.events = EPOLLIN;
					if (epoll_ctl (efd, EPOLL_CTL_ADD, newfd, &event) != 0)
						PRINT_ERR("epoll_ctl failed");

					//if there is no synch thread, if any new connection coming, indicates ss started
					if ( ss->no_synch )
						turn_on_light();
					//else, leave the sync thread to fire the trigger
				}
			}
			/* handle data from an EXISTING client */
			else {
				bzero(buffer, ss->recv_buf_size);
				bytes_to_be_read = ss->is_sync_thread ? 1 : ss->recv_buf_size;

				/* got error or connection closed by client */
				if ((nbytes = n_read(current_fd, buffer, bytes_to_be_read)) <= 0) {
					if (nbytes == 0) {
						asprintf(&log, "socket closed: %d", i);
						PRINT_DBG_FREE(log);
					}
					else {
						asprintf(&log, "error: cannot read data from socket: %d", i);
						PRINT_INFO_FREE(log);
						err_code = ERROR_NETWORK_READ;
						/* need to continue ss and check other socket, so don't end the ss */
					}
					close (current_fd);
				}
				/* report how many bytes received */
				else {
					__sync_fetch_and_add(&(ss->total_bytes_transferred), nbytes);
				}
			}
		}
	}

	free(buffer);
	free(ip_address_str);
	free(events);
	close(efd);
	close(ss->listener);
	return err_code;
}
예제 #5
0
파일: pty_open.c 프로젝트: GNOME/vinagre
/* Open the named PTY slave, fork off a child (storing its PID in child),
 * and exec the named command in its own session as a process group leader */
static int
_pty_fork_on_pty_name(const char *path, int parent_fd, char **env_add,
		      const char *command, char **argv,
		      const char *directory,
		      int columns, int rows, 
		      int *stdin_fd, int *stdout_fd, int *stderr_fd, 
		      int *held_fd,
		      pid_t *child, gboolean reapchild, gboolean login)
{
	int fd, hold_fd, i;
	char c;
	int ready_a[2] = { 0, 0 };
	int ready_b[2] = { 0, 0 };
	pid_t pid, grandchild_pid;
	int pid_pipe[2];
	int stdin_pipe[2];
	int stdout_pipe[2];
	int stderr_pipe[2];

	/* Optionally hold the slave PTY open in the parent. Needed to prevent
	 * EIO from read() on the master if exec'ing a program that enumerates
	 * and closes open fds before opening /dev/tty (ssh). Partially fixes
	 * bug 644432. */
	if (held_fd) {
		hold_fd = open(path, O_RDWR|O_NOCTTY);
		if (hold_fd == -1) {
			return -1;
		}
	}

	/* Open pipes for synchronizing between parent and child. */
	if (_pty_pipe_open_bi(&ready_a[0], &ready_a[1],
			      &ready_b[0], &ready_b[1]) == -1) {
		/* Error setting up pipes.  Bail. */
		goto bail_ready;
	}

	if (reapchild && pipe(pid_pipe)) {
		/* Error setting up pipes. Bail. */
		goto bail_pid;
	}
	
	if (pipe(stdin_pipe)) {
		/* Error setting up pipes.  Bail. */
		goto bail_stdin;
	}
	if (pipe(stdout_pipe)) {
		/* Error setting up pipes.  Bail. */
		goto bail_stdout;
	}
	if (pipe(stderr_pipe)) {
		/* Error setting up pipes.  Bail. */
		goto bail_stderr;
	}

	/* Start up a child. */
	pid = fork();
	switch (pid) {
	case -1:
		/* Error fork()ing.  Bail. */
		*child = -1;
		return -1;
		break;
	case 0:
		/* Child. Close the parent's ends of the pipes. */
		close(ready_a[0]);
		close(ready_b[1]);
	
		close(stdin_pipe[1]);
		close(stdout_pipe[0]);
		close(stderr_pipe[0]);
		if (held_fd) close(hold_fd);

		if(reapchild) {
			close(pid_pipe[0]);

			/* Fork a intermediate child. This is needed to not
			 * produce zombies! */
			grandchild_pid = fork();

			if (grandchild_pid < 0) {
				/* Error during fork! */
				n_write (pid_pipe[1], &grandchild_pid, 
					 sizeof (grandchild_pid));
				_exit (1);
			} else if (grandchild_pid > 0) {
				/* Parent! (This is the actual intermediate child;
				 * so write the pid to the parent and then exit */
				n_write (pid_pipe[1], &grandchild_pid, 
					 sizeof (grandchild_pid));
				close (pid_pipe[1]);
				_exit (0);
			}
		
			/* Start a new session and become process-group leader. */
			setsid();
			setpgid(0, 0);
		}

		/* Close most descriptors. */
		for (i = 0; i < sysconf(_SC_OPEN_MAX); i++) {
			if ((i != ready_b[0]) && 
			    (i != ready_a[1]) &&
			    (i != stdin_pipe[0]) &&
			    (i != stdout_pipe[1]) &&
			    (i != stderr_pipe[1])) {
				close(i);
			}
		}

		/* Set up stdin/out/err */
		dup2(stdin_pipe[0], STDIN_FILENO);
		close (stdin_pipe[0]);
		dup2(stdout_pipe[1], STDOUT_FILENO);
		close (stdout_pipe[1]);
		dup2(stderr_pipe[1], STDERR_FILENO);
		close (stderr_pipe[1]);

		/* Open the slave PTY, acquiring it as the controlling terminal
		 * for this process and its children. */
		fd = open(path, O_RDWR);
		if (fd == -1) {
			return -1;
		}
#ifdef TIOCSCTTY
		/* TIOCSCTTY is defined?  Let's try that, too. */
		ioctl(fd, TIOCSCTTY, fd);
#endif
		/* Store 0 as the "child"'s ID to indicate to the caller that
		 * it is now the child. */
		*child = 0;
		return _pty_run_on_pty(fd, login,
				       stdin_pipe[1], stdout_pipe[1], stderr_pipe[1], 
				       ready_b[0], ready_a[1],
				       env_add, command, argv, directory);
		break;
	default:
		/* Parent.  Close the child's ends of the pipes, do the ready
		 * handshake, and return the child's PID. */
		close(ready_b[0]);
		close(ready_a[1]);

		close(stdin_pipe[0]);
		close(stdout_pipe[1]);
		close(stderr_pipe[1]);

		if (reapchild) {
			close(pid_pipe[1]);

			/* Reap the intermediate child */
        	wait_again:	
			if (waitpid (pid, NULL, 0) < 0) {
				if (errno == EINTR) {
					goto wait_again;
				} else if (errno == ECHILD) {
					; /* NOOP! Child already reaped. */
				} else {
					g_warning ("waitpid() should not fail in pty-open.c");
				}
			}
	
			/*
			 * Read the child pid from the pid_pipe 
			 * */
			if (n_read (pid_pipe[0], child, sizeof (pid_t)) 
			  	!= sizeof (pid_t) || *child == -1) {
				g_warning ("Error while spanning child!");
				goto bail_fork;
			}
			
			close(pid_pipe[0]);

		} else {
			/* No intermediate child, simple */
			*child = pid;
		}
		
		/* Wait for the child to be ready, set the window size, then
		 * signal that we're ready.  We need to synchronize here to
		 * avoid possible races when the child has to do more setup
		 * of the terminal than just opening it. */
		n_read(ready_a[0], &c, 1);
		_pty_set_size(parent_fd, columns, rows);
		n_write(ready_b[1], &c, 1);
		close(ready_a[0]);
		close(ready_b[1]);

		*stdin_fd = stdin_pipe[1];
		*stdout_fd = stdout_pipe[0];
		*stderr_fd = stderr_pipe[0];
		if (held_fd) *held_fd = hold_fd;

		return 0;
		break;
	}
	g_assert_not_reached();
	return -1;

 bail_fork:
	close(stderr_pipe[0]);
	close(stderr_pipe[1]);
 bail_stderr:
	close(stdout_pipe[0]);
	close(stdout_pipe[1]);
 bail_stdout:
	close(stdin_pipe[0]);
	close(stdin_pipe[1]);
 bail_stdin:
	if(reapchild) {
		close(pid_pipe[0]);
		close(pid_pipe[1]);
	}
 bail_pid:
	close(ready_a[0]);
	close(ready_a[1]);
	close(ready_b[0]);
	close(ready_b[1]);
 bail_ready:
	*child = -1;
	if (held_fd) close(hold_fd);
	return -1;
}
예제 #6
0
파일: pty_open.c 프로젝트: GNOME/vinagre
/* Run the given command (if specified), using the given descriptor as the
 * controlling terminal. */
static int
_pty_run_on_pty(int fd, gboolean login,
			  int stdin_fd, int stdout_fd, int stderr_fd, 
			  int ready_reader, int ready_writer,
			  char **env_add, const char *command, char **argv,
			  const char *directory)
{
	int i;
	char c;
	char **args, *arg;

#ifdef HAVE_STROPTS_H
	if (!ioctl (fd, I_FIND, "ptem") && ioctl (fd, I_PUSH, "ptem") == -1) {
		close (fd);
		_exit (0);
		return -1;
	}

	if (!ioctl (fd, I_FIND, "ldterm") && ioctl (fd, I_PUSH, "ldterm") == -1) {
		close (fd);
		_exit (0);
		return -1;
	}

	if (!ioctl (fd, I_FIND, "ttcompat") && ioctl (fd, I_PUSH, "ttcompat") == -1) {
		perror ("ioctl (fd, I_PUSH, \"ttcompat\")");
		close (fd);
		_exit (0);
		return -1;
	}
#endif /* HAVE_STROPTS_H */

	/* Set any environment variables. */
	for (i = 0; (env_add != NULL) && (env_add[i] != NULL); i++) {
		if (putenv(g_strdup(env_add[i])) != 0) {
			g_warning("Error adding `%s' to environment, "
				    "continuing.", env_add[i]);
		}
	}

	/* Reset our signals -- our parent may have done any number of
	 * weird things to them. */
	_pty_reset_signal_handlers();

	/* Change to the requested directory. */
	if (directory != NULL) {
		i = chdir(directory);
	}

#ifdef HAVE_UTMP_H
	/* This sets stdin, stdout, stderr to the socket */	
	if (login && login_tty (fd) == -1) {
		g_printerr ("mount child process login_tty failed: %s\n", g_strerror (errno));
		return -1;
	}
#endif
	
	/* Signal to the parent that we've finished setting things up by
	 * sending an arbitrary byte over the status pipe and waiting for
	 * a response.  This synchronization step ensures that the pty is
	 * fully initialized before the parent process attempts to do anything
	 * with it, and is required on systems where additional setup, beyond
	 * merely opening the device, is required.  This is at least the case
	 * on Solaris. */
	/* Initialize so valgrind doesn't complain */
	c = 0;
	n_write(ready_writer, &c, 1);
	fsync(ready_writer);
	n_read(ready_reader, &c, 1);
	close(ready_writer);
	if (ready_writer != ready_reader) {
		close(ready_reader);
	}

	/* If the caller provided a command, we can't go back, ever. */
	if (command != NULL) {
		/* Outta here. */
		if (argv != NULL) {
			for (i = 0; (argv[i] != NULL); i++) ;
			args = g_malloc0(sizeof(char*) * (i + 1));
			for (i = 0; (argv[i] != NULL); i++) {
				args[i] = g_strdup(argv[i]);
			}
			execvp(command, args);
		} else {
			arg = g_strdup(command);
			execlp(command, arg, NULL);
		} 

		/* Avoid calling any atexit() code. */
		_exit(0);
		g_assert_not_reached();
	}

	return 0;
}
예제 #7
0
파일: pty.c 프로젝트: gbl/vte
/*
 * _vte_pty_open_with_helper:
 * @pty: a #VtePty
 * @error: a location to store a #GError, or %NULL
 *
 * Opens a new file descriptor to a new PTY master using the
 * GNOME PTY helper.
 *
 * Returns: %TRUE on success, %FALSE on failure with @error filled in
 */
static gboolean
_vte_pty_open_with_helper(VtePty *pty,
                          GError **error)
{
        VtePtyPrivate *priv = pty->priv;
	GnomePtyOps ops;
	int ret;
	int parentfd = -1, childfd = -1;
	gpointer tag;

	/* We have to use the pty helper here. */
	if (!_vte_pty_start_helper(error))
                return FALSE;

	/* Try to open a new descriptor. */

        ops = _vte_pty_helper_ops_from_flags(priv->flags);
        /* Send our request. */
        if (n_write(_vte_pty_helper_tunnel,
                    &ops, sizeof(ops)) != sizeof(ops)) {
                g_set_error (error, VTE_PTY_ERROR,
                              VTE_PTY_ERROR_PTY_HELPER_FAILED,
                              "Failed to send request to gnome-pty-helper: %s",
                              g_strerror(errno));
                return FALSE;
        }
        _vte_debug_print(VTE_DEBUG_PTY, "Sent request to helper.\n");
        /* Read back the response. */
        if (n_read(_vte_pty_helper_tunnel,
                    &ret, sizeof(ret)) != sizeof(ret)) {
                g_set_error (error, VTE_PTY_ERROR,
                              VTE_PTY_ERROR_PTY_HELPER_FAILED,
                              "Failed to read response from gnome-pty-helper: %s",
                              g_strerror(errno));
                return FALSE;
        }
        _vte_debug_print(VTE_DEBUG_PTY,
                        "Received response from helper.\n");
        if (ret == 0) {
                g_set_error_literal (error, VTE_PTY_ERROR,
                                      VTE_PTY_ERROR_PTY_HELPER_FAILED,
                                      "gnome-pty-helper failed to open pty");
                return FALSE;
        }
        _vte_debug_print(VTE_DEBUG_PTY, "Helper returns success.\n");
        /* Read back a tag. */
        if (n_read(_vte_pty_helper_tunnel,
                    &tag, sizeof(tag)) != sizeof(tag)) {
                g_set_error (error, VTE_PTY_ERROR,
                              VTE_PTY_ERROR_PTY_HELPER_FAILED,
                              "Failed to read tag from gnome-pty-helper: %s",
                              g_strerror(errno));
                return FALSE;
        }
        _vte_debug_print(VTE_DEBUG_PTY, "Tag = %p.\n", tag);
        /* Receive the master and slave ptys. */
        _vte_pty_read_ptypair(_vte_pty_helper_tunnel,
                              &parentfd, &childfd);

        if ((parentfd == -1) || (childfd == -1)) {
                int errsv = errno;

                close(parentfd);
                close(childfd);

                g_set_error (error, VTE_PTY_ERROR,
                              VTE_PTY_ERROR_PTY_HELPER_FAILED,
                              "Failed to read master or slave pty from gnome-pty-helper: %s",
                              g_strerror(errsv));
                errno = errsv;
                return FALSE;
        }

        _vte_debug_print(VTE_DEBUG_PTY,
                        "Got master pty %d and slave pty %d.\n",
                        parentfd, childfd);

        priv->using_helper = TRUE;
        priv->helper_tag = tag;
        priv->pty_fd = parentfd;

        priv->child_setup_data.mode = TTY_OPEN_BY_FD;
        priv->child_setup_data.tty.fd = childfd;

        return TRUE;
}