Add a directory 'contrib/cygtermd', containing the source code for my
[u/mdw/putty] / contrib / cygtermd / pty.c
diff --git a/contrib/cygtermd/pty.c b/contrib/cygtermd/pty.c
new file mode 100644 (file)
index 0000000..e30f9e0
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * pty.c - pseudo-terminal handling
+ */
+
+#define _XOPEN_SOURCE
+#include <features.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <pwd.h>
+
+#include "pty.h"
+#include "malloc.h"
+
+static char ptyname[FILENAME_MAX];
+int master = -1;
+
+void pty_preinit(void)
+{
+    /*
+     * Allocate the pty.
+     */
+    master = open("/dev/ptmx", O_RDWR);
+    if (master < 0) {
+       perror("/dev/ptmx: open");
+       exit(1);
+    }
+
+    if (grantpt(master) < 0) {
+       perror("grantpt");
+       exit(1);
+    }
+    
+    if (unlockpt(master) < 0) {
+       perror("unlockpt");
+       exit(1);
+    }
+}
+
+void pty_resize(int w, int h)
+{
+    struct winsize sz;
+
+    assert(master >= 0);
+
+    sz.ws_row = h;
+    sz.ws_col = w;
+    sz.ws_xpixel = sz.ws_ypixel = 0;
+    ioctl(master, TIOCSWINSZ, &sz);
+}
+
+int run_program_in_pty(const struct shell_data *shdata,
+                       char *directory, char **program_args)
+{
+    int slave, pid;
+    char *fallback_args[2];
+
+    assert(master >= 0);
+
+    ptyname[FILENAME_MAX-1] = '\0';
+    strncpy(ptyname, ptsname(master), FILENAME_MAX-1);
+
+#if 0
+    {
+       struct winsize ws;
+       struct termios ts;
+
+       /*
+        * FIXME: think up some good defaults here
+        */
+
+       if (!ioctl(0, TIOCGWINSZ, &ws))
+           ioctl(master, TIOCSWINSZ, &ws);
+       if (!tcgetattr(0, &ts))
+           tcsetattr(master, TCSANOW, &ts);
+    }
+#endif
+
+    slave = open(ptyname, O_RDWR | O_NOCTTY);
+    if (slave < 0) {
+       perror("slave pty: open");
+       return 1;
+    }
+
+    /*
+     * Fork and execute the command.
+     */
+    pid = fork();
+    if (pid < 0) {
+       perror("fork");
+       return 1;
+    }
+
+    if (pid == 0) {
+       int i, fd;
+
+       /*
+        * We are the child.
+        */
+       close(master);
+
+       fcntl(slave, F_SETFD, 0);    /* don't close on exec */
+       dup2(slave, 0);
+       dup2(slave, 1);
+       if (slave != 0 && slave != 1)
+           close(slave);
+       dup2(1, 2);
+       setsid();
+       setpgrp();
+        i = 0;
+#ifdef TIOCNOTTY
+        if ((fd = open("/dev/tty", O_RDWR)) >= 0) {
+            ioctl(fd, TIOCNOTTY, &i);
+            close(fd);
+        }
+#endif
+#ifdef TIOCSCTTY
+        ioctl(0, TIOCSCTTY, &i);
+#endif
+       tcsetpgrp(0, getpgrp());
+
+       for (i = 0; i < shdata->nenvvars; i++)
+            putenv(shdata->envvars[i]);
+       if (shdata->termtype)
+            putenv(shdata->termtype);
+
+        if (directory)
+            chdir(directory);
+
+       /*
+        * Use the provided shell program name, if the user gave
+        * one. Failing that, use $SHELL; failing that, look up
+        * the user's default shell in the password file; failing
+        * _that_, revert to the bog-standard /bin/sh.
+        */
+       if (!program_args) {
+            char *shell;
+            
+           shell = getenv("SHELL");
+            if (!shell) {
+                const char *login;
+                uid_t uid;
+                struct passwd *pwd;
+
+                /*
+                 * For maximum generality in the face of multiple
+                 * /etc/passwd entries with different login names and
+                 * shells but a shared uid, we start by using
+                 * getpwnam(getlogin()) if it's available - but we
+                 * insist that its uid must match our real one, or we
+                 * give up and fall back to getpwuid(getuid()).
+                 */
+                uid = getuid();
+                login = getlogin();
+                if (login && (pwd = getpwnam(login)) && pwd->pw_uid == uid)
+                    shell = pwd->pw_shell;
+                else if ((pwd = getpwuid(uid)))
+                    shell = pwd->pw_shell;
+            }
+            if (!shell)
+                shell = "/bin/sh";
+
+            fallback_args[0] = shell;
+            fallback_args[1] = NULL;
+            program_args = fallback_args;
+        }
+
+        execv(program_args[0], program_args);
+
+       /*
+        * If we're here, exec has gone badly foom.
+        */
+       perror("exec");
+       exit(127);
+    }
+
+    close(slave);
+
+    return master;
+}