steal some bits from qemacs to open the pty.
authorPierre Habouzit <madcoder@debian.org>
Sat, 11 Nov 2006 13:11:05 +0000 (14:11 +0100)
committerPierre Habouzit <madcoder@debian.org>
Sat, 11 Nov 2006 13:11:05 +0000 (14:11 +0100)
we gain non blocking IOs
add rote_vt_read

no more selects \o/

Signed-off-by: Pierre Habouzit <madcoder@debian.org>
demo/boxshell.c
madtty/madtty.c
madtty/madtty.h

index cdc980c..42c2579 100644 (file)
@@ -78,7 +78,12 @@ int main(int argc, char *argv[])
 
     /* create the terminal and have it run bash */
     rt = rote_vt_create(h, w);
-    rote_vt_forkpty(rt, "/bin/bash --login");
+    {
+        const char *path = "/bin/bash";
+        const char *args[] = {"/bin/bash", "--login", NULL};
+
+        rote_vt_forkpty(rt, path, args);
+    }
 
     /* keep reading keypresses from the user and passing them to the terminal;
      * also, redraw the terminal to the window at each iteration */
index 2002e18..6556890 100644 (file)
@@ -19,7 +19,9 @@
     Copyright © 2006 Pierre Habouzit
  */
 
+#include <errno.h>
 #include <stdlib.h>
+#include <fcntl.h>
 #ifdef USE_PTY
 #include <pty.h>
 #endif
@@ -134,37 +136,100 @@ void rote_vt_draw(RoteTerm *rt, WINDOW *win, int srow, int scol,
 
 #endif
 
-#ifdef USE_PTY
+/******************************************************/
+
+#define PTYCHAR1 "pqrstuvwxyz"
+#define PTYCHAR2 "0123456789abcdef"
+
+/* allocate one pty/tty pair */
+static int get_pty(char *tty_str)
+{
+   int fd;
+   char ptydev[] = "/dev/pty??";
+   char ttydev[] = "/dev/tty??";
+   int len = strlen(ttydev);
+   const char *c1, *c2;
+
+   for (c1 = PTYCHAR1; *c1; c1++) {
+       ptydev[len-2] = ttydev[len-2] = *c1;
+       for (c2 = PTYCHAR2; *c2; c2++) {
+           ptydev[len-1] = ttydev[len-1] = *c2;
+           if ((fd = open(ptydev, O_RDWR)) >= 0) {
+               if (access(ttydev, R_OK|W_OK) == 0) {
+                   strcpy(tty_str, ttydev);
+                   return fd;
+               }
+               close(fd);
+           }
+       }
+   }
+   return -1;
+}
 
-pid_t rote_vt_forkpty(RoteTerm *rt, const char *command)
+static int
+run_process(const char *path, const char **argv, int *fd_ptr, int *pid_ptr)
 {
+    int pty_fd, pid, i, nb_fds;
+    char tty_name[32];
     struct winsize ws;
-    pid_t childpid;
 
-    ws.ws_row = rt->rows;
-    ws.ws_col = rt->cols;
-    ws.ws_xpixel = ws.ws_ypixel = 0;
+    pty_fd = get_pty(tty_name);
+    if (pty_fd < 0)
+        return -1;
 
-    childpid = forkpty(&rt->pd->pty, NULL, NULL, &ws);
-    if (childpid < 0)
+    fcntl(pty_fd, F_SETFL, O_NONBLOCK);
+
+    /* set dummy screen size */
+    ws.ws_col = 80;
+    ws.ws_row = 25;
+    ws.ws_xpixel = ws.ws_col;
+    ws.ws_ypixel = ws.ws_row;
+    ioctl(pty_fd, TIOCSWINSZ, &ws);
+
+    pid = fork();
+    if (pid < 0)
         return -1;
 
-    if (childpid == 0) {
-        /* we are the child, running under the slave side of the pty. */
+    if (pid == 0) {
+        /* child process */
+        nb_fds = getdtablesize();
+        for (i = 0; i < nb_fds; i++)
+            close(i);
+        /* open pseudo tty for standard i/o */
+        open(tty_name, O_RDWR);
+        dup(0);
+        dup(0);
+
+        setsid();
 
-        /* Cajole application into using linux-console-compatible escape
-         * sequences (which is what we are prepared to interpret) */
         setenv("TERM", "linux", 1);
+        execv(path, (char *const*)argv);
+        fprintf(stderr, "\nexecv() failed.\nCommand: '%s'\n", argv[0]);
+        exit(1);
+    }
+    /* return file info */
+    *fd_ptr = pty_fd;
+    *pid_ptr = pid;
+    return 0;
+}
+
+
+#ifdef USE_PTY
 
-        /* Now we will exec /bin/sh -c command. */
-        execl("/bin/sh", "/bin/sh", "-c", command, NULL);
+pid_t rote_vt_forkpty(RoteTerm *rt, const char *path, const char *argv[])
+{
+    struct winsize ws;
 
-        fprintf(stderr, "\nexecl() failed.\nCommand: '%s'\n", command);
-        exit(127);  /* error exec'ing */
+    ws.ws_row = rt->rows;
+    ws.ws_col = rt->cols;
+    ws.ws_xpixel = ws.ws_ypixel = 0;
+
+    if (run_process(path, argv, &rt->pd->pty, &rt->childpid)) {
+        return -1;
     }
 
-    /* if we got here we are the parent process */
-    return (rt->childpid = childpid);
+    ioctl(rt->pd->pty, TIOCSWINSZ, &ws);
+    return rt->childpid;
 }
 
 void rote_vt_forsake_child(RoteTerm *rt)
@@ -179,13 +244,9 @@ void rote_vt_forsake_child(RoteTerm *rt)
 
 void rote_vt_update(RoteTerm *rt)
 {
-    fd_set ifs;
-    struct timeval tvzero;
     char buf[512];
-    int bytesread;
+    int nbread;
     int n = ROTE_VT_UPDATE_ITERATIONS;
-    if (rt->pd->pty < 0)
-        return;  /* nothing to pump */
 
     while (n--) { /* iterate at most ROVE_VT_UPDATE_ITERATIONS times.
                    * As Phil Endecott pointed out, if we don't restrict this,
@@ -195,21 +256,23 @@ void rote_vt_update(RoteTerm *rt)
                    * calling rote_vt_update often, as the documentation
                    * recommends :-) */
 
-        /* check if pty has something to say */
-        FD_ZERO(&ifs); FD_SET(rt->pd->pty, &ifs);
-        tvzero.tv_sec = 0; tvzero.tv_usec = 0;
-
-        if (select(rt->pd->pty + 1, &ifs, NULL, NULL, &tvzero) <= 0)
-            return; /* nothing to read, or select() failed */
-
-        /* read what we can. This is guaranteed not to block, since
-         * select() told us there was something to read. */
-        bytesread = read(rt->pd->pty, buf, 512);
-        if (bytesread <= 0) return;   
+        nbread = rote_vt_read(rt, buf, sizeof(buf));
+        if (nbread <= 0)
+            return;
 
         /* inject the data into the terminal */
-        rote_vt_inject(rt, buf, bytesread);
+        rote_vt_inject(rt, buf, nbread);
+    }
+}
+
+int rote_vt_read(RoteTerm *rt, char *buf, int buflen)
+{
+    if (rt->pd->pty < 0) {
+        errno = EINVAL;
+        return -1;
     }
+
+    return read(rt->pd->pty, buf, buflen);
 }
 
 void rote_vt_write(RoteTerm *rt, const char *data, int len)
index 373d1f3..cb34762 100644 (file)
@@ -166,7 +166,7 @@ void rote_vt_destroy(RoteTerm *rt);
  * to execute the command and will exit with status 127. You can catch
  * that by installing a SIGCHLD handler if you want.
  */
-pid_t rote_vt_forkpty(RoteTerm *rt, const char *command);
+pid_t rote_vt_forkpty(RoteTerm *rt, const char *path, const char *argv[]);
 
 /* Disconnects the RoteTerm from its forked child process. This function
  * should be called when the child process dies or something of the sort.
@@ -183,6 +183,8 @@ void rote_vt_forsake_child(RoteTerm *rt);
  * read from the child process it will return immediately. */
 void rote_vt_update(RoteTerm *rt);
 
+int rote_vt_read(RoteTerm *rt, char *buf, int buflen);
+
 /* Puts data into the terminal: if there is a forked process running,
  * the data will be sent to it. If there is no forked process,
  * the data will simply be injected into the terminal (as in