summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/misc/forkpty.c71
1 files changed, 45 insertions, 26 deletions
diff --git a/src/misc/forkpty.c b/src/misc/forkpty.c
index 07f8d01e..caf13adb 100644
--- a/src/misc/forkpty.c
+++ b/src/misc/forkpty.c
@@ -1,38 +1,57 @@
#include <pty.h>
+#include <utmp.h>
#include <unistd.h>
-#include <sys/ioctl.h>
+#include <errno.h>
#include <fcntl.h>
+#include <sys/wait.h>
+#include <pthread.h>
-int forkpty(int *m, char *name, const struct termios *tio, const struct winsize *ws)
+int forkpty(int *pm, char *name, const struct termios *tio, const struct winsize *ws)
{
- int s, t, i, istmp[3]={0};
- pid_t pid;
-
- if (openpty(m, &s, name, tio, ws) < 0) return -1;
-
- /* Ensure before forking that we don't exceed fd limit */
- for (i=0; i<3; i++) {
- if (fcntl(i, F_GETFL) < 0) {
- t = fcntl(s, F_DUPFD, i);
- if (t<0) break;
- else if (t!=i) close(t);
- else istmp[i] = 1;
- }
+ int m, s, ec=0, p[2], cs;
+ pid_t pid=-1;
+ sigset_t set, oldset;
+
+ if (openpty(&m, &s, name, tio, ws) < 0) return -1;
+
+ sigfillset(&set);
+ pthread_sigmask(SIG_BLOCK, &set, &oldset);
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
+
+ if (pipe2(p, O_CLOEXEC)) {
+ close(s);
+ goto out;
}
- pid = i==3 ? fork() : -1;
+
+ pid = fork();
if (!pid) {
- close(*m);
- setsid();
- ioctl(s, TIOCSCTTY, (char *)0);
- dup2(s, 0);
- dup2(s, 1);
- dup2(s, 2);
- if (s>2) close(s);
+ close(m);
+ close(p[0]);
+ if (login_tty(s)) {
+ write(p[1], &errno, sizeof errno);
+ _exit(127);
+ }
+ close(p[1]);
+ pthread_setcancelstate(cs, 0);
+ pthread_sigmask(SIG_SETMASK, &oldset, 0);
return 0;
}
- for (i=0; i<3; i++)
- if (istmp[i]) close(i);
close(s);
- if (pid < 0) close(*m);
+ close(p[1]);
+ if (read(p[0], &ec, sizeof ec) > 0) {
+ int status;
+ waitpid(pid, &status, 0);
+ pid = -1;
+ errno = ec;
+ }
+ close(p[0]);
+
+out:
+ if (pid > 0) *pm = m;
+ else close(m);
+
+ pthread_setcancelstate(cs, 0);
+ pthread_sigmask(SIG_SETMASK, &oldset, 0);
+
return pid;
}