$OpenBSD: patch-daemon_pty_open_c,v 1.5 2012/07/13 15:23:59 ajacoutot Exp $

From 2fec402d6523e4cf416d754f69d012dc67a58a2e Mon Sep 17 00:00:00 2001
From: Martin Pieuchot <mpi@openbsd.org>
Date: Fri, 13 Jul 2012 14:51:39 +0000
Subject: daemon: Support allocating PTYs through openpty on BSD

--- daemon/pty_open.c.orig	Tue May 15 20:01:50 2012
+++ daemon/pty_open.c	Tue Jul 10 11:03:27 2012
@@ -72,6 +72,12 @@
 #include <glib.h>
 #include "pty_open.h"
 
+#if defined(HAVE_PTSNAME_R) || defined(HAVE_PTSNAME) || defined(TIOCGPTN)
+#define HAVE_UNIX98_PTY
+#else
+#undef HAVE_UNIX98_PTY
+#endif
+
 int _pty_set_size(int master, int columns, int rows);
 
 /* Solaris does not have the login_tty() function so implement locally. */
@@ -710,6 +716,8 @@ _pty_unlockpt(int fd)
 #endif
 }
 
+#if defined(HAVE_UNIX98_PTY)
+
 static int
 _pty_open_unix98(pid_t *child, guint flags, char **env_add,
 			   const char *command, char **argv,
@@ -746,6 +754,114 @@ _pty_open_unix98(pid_t *child, guint flags, char **env
 	return fd;
 }
 
+#elif defined(HAVE_OPENPTY)
+
+static int
+_pty_open_bsd(pid_t *child, const char *command, char **argv,
+	      int *stdin_fd, int *stdout_fd, int *stderr_fd)
+{
+	int master, slave;
+	char **args, *arg;
+	int stdin_pipe[2];
+	int stdout_pipe[2];
+	int stderr_pipe[2];
+	pid_t pid;
+	int i;
+
+	if (pipe(stdin_pipe))
+		goto bail_stdin;
+	if (pipe(stdout_pipe))
+		goto bail_stdout;
+	if (pipe(stderr_pipe))
+		goto bail_stderr;
+
+	if (openpty(&master, &slave, NULL, NULL, NULL) == -1)
+		return (-1);
+
+	switch(pid = fork()) {
+	case -1:
+		goto bail_fork;
+	case 0:
+		/*
+		 * Child
+		 */
+		close(master);
+		close(stdin_pipe[1]);
+		close(stdout_pipe[0]);
+		close(stderr_pipe[0]);
+
+		setsid();
+		if (ioctl(slave, TIOCSCTTY, (char *)NULL) == -1)
+			_exit(0);
+
+		/* Set up stdin/out/err */
+		dup2(stdin_pipe[0], STDIN_FILENO);
+		dup2(stdout_pipe[1], STDOUT_FILENO);
+		dup2(stderr_pipe[1], STDERR_FILENO);
+		close(stdin_pipe[0]);
+		close(stdout_pipe[1]);
+		close(stderr_pipe[1]);
+
+		/* Reset our signals -- our parent may have done any number of
+		 * weird things to them. */
+		_pty_reset_signal_handlers();
+
+		/* 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();
+	}
+
+	/*
+	 * Parent
+	 */
+
+	/* XXX Don't close the slave pty, it's now the control
+	 *     terminal of the child and ssh needs it to authenticate.
+	close(slave);
+	 */
+	close(stdin_pipe[0]);
+	close(stdout_pipe[1]);
+	close(stderr_pipe[1]);
+
+	*child = pid;
+	*stdin_fd = stdin_pipe[1];
+	*stdout_fd = stdout_pipe[0];
+	*stderr_fd = stderr_pipe[0];
+
+	return (master);
+
+ 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:
+
+ 	*child = -1;
+ 	return -1;
+}
+
+#else
+#error Have neither UNIX98 PTY nor BSD openpty!
+#endif /* HAVE_UNIX98_PTY */
+
 /**
  * pty_open:
  * @child: location to store the new process's ID
@@ -774,11 +890,17 @@ pty_open(pid_t *child, guint flags, char **env_add, 
 	 int *stdin_fd, int *stdout_fd, int *stderr_fd)
 {
 	int ret = -1;
-	if (ret == -1) {
-		ret = _pty_open_unix98(child, flags, env_add, command, 
-						 argv, directory, columns, rows,
-						 stdin_fd, stdout_fd, stderr_fd);
-	}
+
+#if defined(HAVE_UNIX98_PTY)
+	ret = _pty_open_unix98(child, flags, env_add, command, argv, directory,
+			       columns, rows, stdin_fd, stdout_fd, stderr_fd);
+#elif defined(HAVE_OPENPTY)
+	ret = _pty_open_bsd(child, command, argv,
+			    stdin_fd, stdout_fd, stderr_fd);
+#else
+#error Have neither UNIX98 PTY nor BSD openpty!
+#endif
+
 	return ret;
 }
 
