pty session manager
John F Haugh II
jfh at rpp386.cactus.org
Sat Dec 22 06:48:38 AEST 1990
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 <your name>" 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 <sys/types.h>
#include <sys/termio.h>
#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <fcntl.h>
#include <errno.h>
#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 at 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."
More information about the Alt.sources
mailing list