Path: utzoo!utgpu!cs.utexas.edu!execu!sequoia!rpp386!jfh From: jfh@rpp386.cactus.org (John F Haugh II) Newsgroups: alt.sources Subject: pty session manager Message-ID: <18853@rpp386.cactus.org> Date: 21 Dec 90 19:48:38 GMT Distribution: alt Organization: River Parishes Programming, Austin TX Lines: 403 this is something i whipped up because i wanted to be able to run multiple sessions on a single tube and i don't have job control or any such stuff. it requires ptys to run. you may want to hack on it and make it set-uid root so it can steal inactive pty's. i didn't bother to do that because i'm lazy this month, and besides, i own all the pty's on this system anyhow ;-) if you need a pty device driver, and you have sco xenix, let me know as i have one laying around here somewheres. it was posted to comp.sources.misc some time back. it understands a few basic commands - create - start a shell on a pty. active - list of active (available) sessions. current - number of current session. jobs - ps output of active shells. connect [ # ] - connect keyboard to currently active shell, or session # if # is given. quit, exit - quit (or exit ;-) to get from "connected" state, you may press ^Z followed by any character to get a "pty->" prompt back. to send a ^Z to your shell, press two. i picked ^Z because that's what i use for "suspend" on aix, and i'm too lazy to learn a new keystroke. someone should probably add a command to make it setable. it doesn't keep your utmp file up to date because it would definitely have to be setuid then. i had a version that executed "login -f " and ran setuid root, but i didn't think anyone would trust this program to run suid 0. as always, unshar and enjoy. ---- #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create: # sm.c # This archive created: Fri Dec 21 13:45:53 1990 # By: John F Haugh II (River Parishes Programming, Austin TX) export PATH; PATH=/bin:/usr/bin:$PATH if test -f 'sm.c' then echo shar: "will not over-write existing file 'sm.c'" else cat << \SHAR_EOF > 'sm.c' /* * This code is in the public domain. * * Written By: John F Haugh II, 12/21/90 */ #include #include #include #include #include #include #include #define MAXSESSIONS 16 int childpids[MAXSESSIONS]; int writepid; int masters[MAXSESSIONS]; int nsessions; int current = -1; int caught = 0; struct termio sanetty; struct termio rawtty; void exit (); void _exit (); char *getlogin (); char *getenv (); struct passwd *getpwnam (); void murder (sig) int sig; { int pid; int i; pid = wait ((int *) 0); /* * See what children have died recently. */ for (i = 0;pid != -1 && i < nsessions;i++) { if (pid == childpids[i]) { childpids[i] = -1; close (masters[i]); masters[i] = -1; } } signal (sig, murder); } void catch (sig) int sig; { caught = 1; signal (sig, catch); } /* * reader - read characters from the pty and write to the screen */ int reader (fd) int fd; { char c; int cnt; signal (SIGINT, SIG_IGN); signal (SIGQUIT, SIG_IGN); while (1) { if ((cnt = read (fd, &c, 1)) == -1) { if (errno != EINTR) return -1; if (caught) return 0; else continue; } if (cnt == 0) return -1; write (1, &c, 1); } } /* * writer - write characters read from the keyboard down the pty */ writer (fd) int fd; { char c; int cnt; int zflg = 0; signal (SIGINT, SIG_IGN); signal (SIGQUIT, SIG_IGN); signal (SIGHUP, _exit); while (1) { errno = 0; if ((cnt = read (0, &c, 1)) == 0) continue; if (cnt == -1) { if (errno == EINTR && caught) continue; else exit (0); } if (c == ('z' & 037)) { if (! zflg++) continue; } else if (zflg) { kill (getppid (), SIGUSR1); exit (0); } zflg = 0; if (write (fd, &c, 1) != 1) break; } exit (0); } usage () { fprintf (stderr, "usage: ptymgr\n"); exit (1); } session () { char mastername[BUFSIZ]; char slavename[BUFSIZ]; char *digits = "0123456789abcdef"; char *letters = "pqrs"; char *shell; int i; int pty; int ptys = 64; for (i = 0;i < nsessions && masters[i] != -1;i++) ; if (i == MAXSESSIONS) return -1; if (i == nsessions) nsessions++; current = i; for (pty = 0;pty < ptys;pty++) { sprintf (mastername, "/dev/pty%c%c", letters[pty >> 4], digits[pty & 0xf]); if ((masters[i] = open (mastername, O_RDWR)) != -1) break; } if (masters[i] == -1) { fprintf (stderr, "Can't find a pty\n"); return -1; } /* * Let's make a child process ... */ switch (childpids[i] = fork ()) { case -1: perror ("fork"); exit (1); case 0: close (0); close (1); for (i = 0;i < nsessions;i++) close (masters[i]); setpgrp (); signal (SIGINT, SIG_DFL); signal (SIGQUIT, SIG_DFL); signal (SIGCLD, SIG_DFL); signal (SIGHUP, SIG_DFL); signal (SIGUSR1, SIG_DFL); sprintf (slavename, "/dev/tty%c%c", letters[pty >> 4], digits[pty & 0xf]); if (open (slavename, O_RDWR) == -1) { fprintf (stderr, "can't open %s\n", slavename); _exit (-1); } close (2); dup (0); dup (0); ioctl (0, TCSETAF, &sanetty); if (! (shell = getenv ("SHELL"))) shell = "/bin/sh"; execl (shell, strrchr (shell, '/') + 1, 0); _exit (-1); } } main (argc, argv) int argc; char **argv; { char buf[BUFSIZ]; char *cp; int i; int pid; for (i = 0;i < MAXSESSIONS;i++) { childpids[i] = -1; masters[i] = -1; } ioctl (0, TCGETA, &sanetty); rawtty = sanetty; /* * Let's have our own little process group */ setpgrp (); rawtty.c_oflag &= ~OPOST; rawtty.c_lflag = 0; rawtty.c_cc[VMIN] = 1; rawtty.c_cc[VTIME] = 1; signal (SIGCLD, murder); signal (SIGUSR1, catch); while (1) { printf ("pty-> "); fflush (stdout); while (errno = 0, gets (buf) == 0) { if (errno == EINTR) continue; else exit (0); } if (! buf[0]) continue; /* * Get the command */ if (strcmp (buf, "quit") == 0 || strcmp (buf, "exit") == 0) { exit (0); } else if (strcmp (buf, "create") == 0) { session (); continue; } else if (strcmp (buf, "current") == 0) { printf ("current session is %d\n", current); continue; } else if (strncmp (buf, "set", 3) == 0) { i = strtol (buf + 3, &cp, 10); if (buf[3] != '\0' && *cp == '\0') current = i; else printf ("eh?\n"); continue; } else if (strcmp (buf, "active") == 0) { for (i = 0;i < nsessions;i++) if (masters[i] != -1) printf ("%d ", i); putchar ('\n'); continue; } else if (strcmp (buf, "jobs") == 0) { int pids = 0; strcpy (buf, "ps -fp "); for (i = 0;i < nsessions;i++) { if (childpids[i] != -1) { if (pids++) strcat (buf, ","); sprintf (buf + strlen (buf), "%d", childpids[i]); } } if (pids) system (buf); continue; } else if (strncmp (buf, "connect", 7) != 0) { printf ("eh?\n"); continue; } i = strtol (buf + 2, &cp, 10); if (*cp == '\0' && buf[2]) { if (masters[i] != -1) current = i; else current = -1; } if (current == -1) { printf ("no current session\n"); continue; } /* * Let's make a process to read from the child ... */ switch (writepid = fork ()) { case -1: kill (childpids[current], SIGKILL); perror ("fork"); break; case 0: writer (masters[current]); exit (1); } ioctl (0, TCSETAF, &rawtty); if (reader (masters[current]) == -1) { close (masters[current]); masters[current] = -1; childpids[current] = -1; current = -1; if (writepid > 0) kill (writepid, SIGTERM); } ioctl (0, TCSETA, &sanetty); } exit (0); } SHAR_EOF fi exit 0 # End of shell archive -- John F. Haugh II UUCP: ...!cs.utexas.edu!rpp386!jfh Ma Bell: (512) 832-8832 Domain: jfh@rpp386.cactus.org "While you are here, your wives and girlfriends are dating handsome American movie and TV stars. Stars like Tom Selleck, Bruce Willis, and Bart Simpson."