rmgr Remote Window Manager for MGR

Howard Chu hyc at math.lsa.umich.edu
Wed Oct 17 11:03:06 AEST 1990


Oops. Left the editor before including the actual sources. Here ya go.

#--------------------------------CUT HERE-------------------------------------
#! /bin/sh
#
# This is a shell archive.  Save this into a file, edit it
# and delete all lines above this comment.  Then give this
# file to sh by executing the command "sh file".  The files
# will be extracted into the current directory owned by
# you with default permissions.
#
# The files contained herein are:
#
# -r--r--r--  1 hyc           725 Oct 14 05:39 Makefile
# -r--r--r--  1 hyc          1027 Oct 14 08:43 README
# -r--r--r--  1 hyc          9341 Oct 14 08:29 rmgr.1
# -rw-r--r--  1 hyc         32339 Oct 15 03:39 rmgr.c
# -r--r--r--  1 hyc          1169 Oct 14 08:29 rmgr.h
#
echo 'x - Makefile'
if test -f Makefile; then echo 'shar: not overwriting Makefile'; else
sed 's/^X//' << '________This_Is_The_END________' > Makefile
X# The following options can be set:
X#
X# -DGETTTYENT -- your system has the new format /etc/ttys (like 4.3 BSD)
X#                and the getttyent(3) library functions.
X#
X# -DUSEBCOPY  -- use the bcopy() from the system's C-library.  If this
X#                is set, bcopy must support overlapping source and
X#                destination.  If USEBCOPY is not set, screen uses its
X#                own version of bcopy.
X#
X# You should install as set-uid with owner root, so that it can read/write
X# /etc/utmp, read /dev/kmem, and chown/chmod the allocated pseudo-ttys.
X
XOPTIONS= -DUSEBCOPY -DGETTTYENT
X
XCFLAGS= -O
X
Xrmgr: rmgr.o
X	$(CC) $(CFLAGS) -o rmgr rmgr.o -lmgr
X
Xrmgr.o: rmgr.c rmgr.h
X	$(CC) $(OPTIONS) $(CFLAGS) -c rmgr.c
________This_Is_The_END________
if test `wc -c < Makefile` -ne      725; then
	echo 'shar: Makefile was damaged during transit (should have been      725 bytes)'
fi
fi		; : end of overwriting check
echo 'x - README'
if test -f README; then echo 'shar: not overwriting README'; else
sed 's/^X//' << '________This_Is_The_END________' > README
X							Howard Chu
X							October 14, 1990
X
XHowdy. This is rmgr, my program for managing multiple remote sessions
Xthru a single communications line (e.g., a dial-up serial port). The
Xheader file rmgr.h tries to #include <mgr/term.h>, so you will need to
Xhave the MGR client library header files installed in a reasonable spot,
Xlike /usr/include/mgr. (Or just add the -Ipath option to the Makefile
Xand fake it...)
X
XThis program is derived from Oliver Laumann's "screen" program, and has
Xnearly the same system requirements as that program. (I.e., BSD sockets
Xand pseudo-ttys are necessary.) Plus, obviously, the console you are
Xsitting in front of must be running an MGR server for you to use this
Xprogram at all.
X
XI am calling this a beta-release, although the program is very stable
Xand I don't expect many problems to crop up. (But read the man page,
Xyou may find that the "features" I note could hamper your use... You're
Xwelcome to tackle the two noted problems and tell me how it went.)
X  -- Howard
X	hyc at math.lsa.umich.edu
________This_Is_The_END________
if test `wc -c < README` -ne     1027; then
	echo 'shar: README was damaged during transit (should have been     1027 bytes)'
fi
fi		; : end of overwriting check
echo 'x - rmgr.1'
if test -f rmgr.1; then echo 'shar: not overwriting rmgr.1'; else
sed 's/^X//' << '________This_Is_The_END________' > rmgr.1
X.if n .ds Q \&"
X.if n .ds U \&"
X.if t .ds Q ``
X.if t .ds U ''
X.TH RMGR 1 "14 October 1990"
X.UC 4
X.SH NAME
Xrmgr \- remote graphical window manager
X.SH SYNOPSIS
X.B rmgr
X[
X.B \-n \fIwindowname\fP
X] [
X.BR \fIcmd args\fP ]
X.br
X.B rmgr \-r
X[
X.BR \fIhost.tty\fP ]
X.ta .5i 1.8i
X.SH DESCRIPTION
X.I rmgr
Xis a client for MGR, the Bellcore window manager, that
Xmultiplexes a communications line between several processes (typically
Xinteractive shells).  As the name implies, the program is meant to be run
Xon a host other than where the MGR server resides.
X.I Rmgr
Xcreates a window on the local MGR server for each process spawned on the
Xremote host.  Each process has the full facilities of MGR at its disposal.
X.PP
XWhen
X.I rmgr
Xis called, it creates a single window with a shell; the pathname of the
Xshell is taken from the environment symbol $SHELL; if this is not
Xdefined, \*Q/bin/sh\*U is used.
XNew windows can be created at any time by calling
X.I rmgr
Xfrom within a previously created window.
XThe program to be started in a newly created
Xwindow and optional arguments to the program can be supplied when
X.I rmgr
Xis invoked.
XFor instance,
X.IP
Xrmgr csh
X.PP
Xwill create a window with a C-Shell and switch to that window.
XWhen the process associated with the currently displayed window
Xterminates (e.\|g. ^D has been typed to a shell),
X.I rmgr
Xswitches to the previously displayed window;
Xwhen no more windows are left,
X.I rmgr
Xexits.
X.PP
XWhen \*Q/etc/utmp\*U is writable by
X.IR rmgr ,
Xan appropriate record is written to this file for each window and
Xremoved when the window is terminated.
X.PP
XThe
X.B \-r
Xoption is used to resume a
X.I rmgr
Xsession that has been \fIdetached\fP from the terminal by means
Xof the \*QDetach\*U menu option (see below).
XThis mechanism allows a user to disconnect
X.I rmgr
Xtogether with all currently active windows from the terminal
Xand resume it at a later point in time, e.\|g. at a later
Xlogin session, and even from a different MGR host.
XWhen more than one detached session exists, the
X.B \-r
Xoption displays a list of
X.I host.tty
Xpairs identifying the detached
X.IR sessions .
XIn this case an additional
X.I host.tty
Xargument can be given to resume a specific
X.I rmgr
Xsession.
X.SH "MENU ITEMS"
X.I Rmgr
Xadds two menus to each window it creates, invoked by pressing the
Xright mouse button. The first three menu items are simply place-holders.
XThe first item is a title-bar, reading \*QMGR Windows =>\*U, the second
Xitem is the name of the currently selected window, and the third is a
Xdividing line. No actions are associated with these three items. The rest
Xof this menu shows the available commands:
X
X.IP "\fBShell Window\fP"
XCreate a new window with a shell and switch to that window.
X.IP "\fBOther Window\fP"
XCreate a new window, switch to that window, and prompt for a command
Xto run in the window. A window name may also be specified by typing
X\*Q-n \fIwindowname\fP\*U at the beginning of the line.
X.IP "\fBKill Window\fP"
XClose the current window and kill its associated process.
X.IP "\fBSuspend\fP"
XSuspend
X.IR rmgr .
XThe window context is pushed onto the environment stack.
X.IP "\fBDetach\fP"
XDetach
X.I rmgr
X(put it into the background and disconnect it from its control terminal).
XThis also pushes the window context onto the environment stack, in case
Xthe session will be resumed without first destroying any created windows.
XA detached
X.I rmgr
Xsession may be resumed by invoking
X.I rmgr
Xwith the
X.B \-r
Xoption.
X.IR Rmgr
Xmaintains a circular buffer for the last 2048 bytes written to each window,
Xand will replay the contents of each window when the session is resumed and
Xthe windows are recreated. Note that detached processes continue to run, and
Xmay continue to generate output. So while the buffer will usually preserve all
Xnecessary context, sometimes it may lose information, but it will almost
Xalways yield an accurate representation of what the window should contain.
X.IP "\fBQuit\fP\0\0\0\0\0"
XKill all windows and terminate
X.IR rmgr .
X.PP
XThe \*QMGR Windows =>\*U menu item is linked to a second menu. Moving the mouse
Xto that item and sliding right displays the menu, a list of the names of all
Xcurrently active
X.IR rmgr
Xwindows. Selecting any of these items will cause the associated window to
Xpop forward and become the active window. Of course, windows may always be
Xselected in the usual manner, i.\|e., move the mouse cursor to the window
Xand click the left button.
X
X.SH OPERATION DETAILS
XWhen
X.I rmgr
Xis invoked, it pushes a number of elements of the current window environment
Xonto the stack, including the current font, window size and location, defined
Xmenus, events, and text regions, and mode flags. It then sets up an escape
Xcode to be used for its own event and menu strings. Then the main window is
Xmoved to the top left corner of the screen and resized to 80 columns by 24
Xrows of text. Codes are set for window activation, destruction, and reshaping,
Xand the menus are downloaded to the window in menu slots 1 and 2. Finally, a
Xshell (or other process, if specified) is started in the window.
X
XNew windows are always created near the top left corner of the screen, in
Xthe default font, sized for 80 columns by 24 rows. If the window is resized,
X.I rmgr
Xwill attempt to inform the remote process of the new window size. The window's
Xname defaults to the command name if no other name is explicitly given.
X
XKilling a window closes its control terminal and should cause the associated
Xprocesses to terminate.  If a process in an obscured window (i.\|e., a window
Xthat is not the active window) terminates, the window is not closed. Instead,
Xthe message \*Q[window shut down]\*U is written in the window.  The window
Xmay be removed by activating it, and then using either the
X.I rmgr
X\*QKill Window\*U option, or the system \*Qdestroy\*U option.  This behavior
Xis intended to prevent inadvertent loss of information when a hidden window
Xis exited.  Also, window 0, the main window, is never closed, though it may
Xbe reused any number of times for different commands.
X
X.I Rmgr
Xmay be suspended to temporarily disable the alternate windows and allow direct
Xcommunication with the remote host. This is most useful for users dialing up
Xon a serial line, who may wish to perform some file transfer functions, and
Xthen resume the windowing sessions. As with a detached session, the processes
Xin the various windows are not stopped, and may continue to generate output.
XThe windows created by
X.I rmgr
Xremain on the screen, and should not be destroyed while
X.I rmgr
Xis suspended.  They may safely be selected for viewing, and moved around, but
Xshould not be changed in any way, or else when
X.I rmgr
Xis restarted the program will get confused.  The Detach option should be used
Xif the alternate windows are likely to be modified or nonexistent by the time
Xthe
X.I rmgr
Xsession is resumed.
X
XWhen a detached
X.I rmgr
Xsession is resumed,
X.I rmgr
Xpolls the MGR server to see which alternate windows exist, if any. If the
Xsame windows exist as at the time of detachment, then no special state
Xrestoration is performed. If, instead, alternate windows exist which
X.I rmgr
Xdidn't expect to find, they are destroyed. Also, if expected windows are
Xmissing, they are recreated and the contents of the history buffer are
Xreplayed into the new window. This polling process is not particularly
Xclever; the only checks are that the window IDs and text dimensions match,
Xso it is possible to artifically create an environment that fits what
X.I rmgr
Xexpected to find, thus bypassing the restoration of state, but it would be
Xsilly to try to contrive this situation. Usually resuming a detached session
Xwill recreate all the alternate windows in nearly the exact same state as
Xthey were before, plus any new output that may have appeared. Also, if any
Xprocesses terminated while
X.I rmgr
Xwas detached, their windows will still be created, but will have the
X\*Q[window shut down]\*U message attached. (This is also true when
Xresuming a \fIsuspended\fP session.)
X
XAll new windows are created with the TERM and TERMCAP variables set. Most
XMGR clients that expect to talk directly to the MGR server can also work
Xnormally through
X.I rmgr .
XHowever, any client that directs queries to the MGR server and needs a reply
Xcan only run if its window is the active window.  This is a necessary
Xrestriction, because MGR doesn't tag replies with the window ID of the
Xintended recipient, so
X.I rmgr
Xsimply feeds the data to the active window. Clients that only issue commands
Xand never expect replies should be able to work regardless of whether 
Xtheir window is active or obscured. However, it is still possible that some
Xcommands will get garbled, as
X.I rmgr
Xdoes no interpretation of the data streams sent by the alternate windows,
Xand does not guard against escape sequences being split or interleaved with
Xdata from other windows. Perhaps a future version of
X.I rmgr
Xwill address these issues.
X
X.SH FILES
X.nf
X.ta 2i
X$(HOME)/.rmgr	Directory created by \fIrmgr\fP
X.br
X$(HOME)/.rmgr/\fIhost.tty\fP	Socket created by \fIrmgr\fP
X.br
X/etc/utmp	Login records
X.fi
X.SH "SEE ALSO"
Xtermcap(5), utmp(5)
X.br
XMGR, a window system for Unix, by Stephen A. Uhler, Bell Communications Research
X.SH AUTHOR
XHoward Chu @ University of Michigan, hyc at math.lsa.umich.edu
XDerived from \fIscreen\fP by Oliver Laumann @ Technical University of Berlin
________This_Is_The_END________
if test `wc -c < rmgr.1` -ne     9341; then
	echo 'shar: rmgr.1 was damaged during transit (should have been     9341 bytes)'
fi
fi		; : end of overwriting check
echo 'x - rmgr.c'
if test -f rmgr.c; then echo 'shar: not overwriting rmgr.c'; else
sed 's/^X//' << '________This_Is_The_END________' > rmgr.c
X/* Copyright (c) 1987,1988 Oliver Laumann, Technical University of Berlin.
X * Not derived from licensed software.
X *
X * Permission is granted to freely use, copy, modify, and redistribute
X * this software, provided that no attempt is made to gain profit from it,
X * the author is not construed to be liable for any results of using the
X * software, alterations are clearly marked as such, and this notice is
X * not modified.
X */
X
X/* This program Copyright 1990 by Howard Chu.
X *
X * This is rmgr, a remote window manager for Bellcore's MGR window
X * manager, by Howard Chu @ University of Michigan (Ann Arbor, MI).
X * Much of this program consists of Oliver Laumann's code; I have not
X * (usually) highlighted my changes in any way. Oliver didn't use any
X * comments at all; for the most part I have followed his lead...  }-)
X * Suffice to say, some of the routines are original, some are heavily
X * hacked upon versions of his code, and some are untouched from his
X * program. Of course, the volume of code here represents only half
X * of the code used in the screen program; the rest, support for ANSI
X * terminal emulation, was discarded.
X */
X
Xstatic char Version[] = "$Header: /usr/src/rmgr/RCS/rmgr.c,v 1.5 90/10/15 03:39:02 hyc Stable $";
X
X#include <stdio.h>
X#include <sgtty.h>
X#include <signal.h>
X#include <errno.h>
X#include <ctype.h>
X#include <utmp.h>
X#include <pwd.h>
X#include <nlist.h>
X#include <fcntl.h>
X#include <sys/types.h>
X#include <sys/time.h>
X#include <sys/file.h>
X#include <sys/wait.h>
X#include <sys/socket.h>
X#include <sys/un.h>
X#include <sys/stat.h>
X#include <sys/dir.h>
X#include "rmgr.h"
X
X#ifdef GETTTYENT
X#   include <ttyent.h>
X#else
X    static struct ttyent {
X	char *ty_name;
X    } *getttyent();
X    static char *tt, *ttnext;
X    static char ttys[] = "/etc/ttys";
X#endif
X
X#define MAXWIN     10
X#define MSGWAIT     5
X
X#define Ctrl(c) ((c)&037)
X
Xextern char **environ;
Xextern errno;
Xextern sys_nerr;
Xextern char *sys_errlist[];
Xextern char *index(), *rindex(), *malloc(), *getenv(), *MakeTermcap();
Xextern char *getlogin(), *ttyname();
Xstatic void AttacherFinit(), Finit(), SigHup(), SigChld();
Xstatic char *Filename(), **SaveArgs(), *GetTtyName();
X
Xstatic char PtyName[32], TtyName[32];
Xstatic char *ShellProg;
Xstatic char *ShellArgs[2];
Xstatic char inbuf[IOSIZE];
Xstatic inlen;
Xstatic ESCseen;
Xstatic GotSignal;
Xstatic char DefaultShell[] = "/bin/sh";
Xstatic char DefaultPath[] = ":/usr/ucb:/bin:/usr/bin";
Xstatic char PtyProto[] = "/dev/ptyXY";
Xstatic char TtyProto[] = "/dev/ttyXY";
Xstatic int TtyMode = 0622;
Xstatic char SockPath[512];
Xstatic char SockDir[] = ".rmgr";
Xstatic char *SockNamePtr, *SockName;
Xstatic ServerSocket;
Xstatic char *NewEnv[MAXARGS];
Xstatic char Esc = Ctrl('a');
Xstatic char *home;
Xstatic HasWindow;
Xstatic utmp, utmpf;
Xstatic char UtmpName[] = "/etc/utmp";
Xstatic char *LoginName;
Xstatic char HostName[MAXSTR];
Xstatic Detached;
Xstatic AttacherPid;	/* Non-Zero in child if we have an attacher */
Xstatic DevTty;
Xstatic char EventStr[]="%c%cA";
Xstatic char CommNames[MAXLINE];
Xstatic int VoluntaryDetach = 0;
X
Xstruct mode {
X    struct sgttyb m_ttyb;
X    struct tchars m_tchars;
X    struct ltchars m_ltchars;
X    int m_ldisc;
X    int m_lmode;
X} OldMode, NewMode;
X
Xstatic struct win *curr, *other;
Xstatic CurrNum, OtherNum;
Xstatic struct win *wtab[MAXWIN];
X
Xmain (ac, av) char **av; {
X    register n, len;
X    register struct win **pp, *p;
X    char *ap;
X    static InitMenu();
X    int s, r, w, x = 0;
X    int rflag = 0;
X    struct timeval tv;
X    time_t now;
X    char buf[IOSIZE], *myname = (ac == 0) ? "rmgr" : av[0];
X    struct stat st;
X    char *winname="";
X
X    while (ac > 0) {
X	ap = *++av;
X	if (--ac > 0 && *ap == '-') {
X	    switch (ap[1]) {
X	    case 'r':
X		rflag = 1;
X		if (ap[2]) {
X		    SockName = ap+2;
X		    if (ac != 1) goto help;
X		} else if (--ac == 1) {
X		    SockName = *++av;
X		} else if (ac != 0) goto help;
X		break;
X	    case 'n':
X		if (ap[2]) {
X		    ap += 2;
X		} else {
X		    if (--ac == 0) goto help;
X		    ap = *++av;
X		}
X		winname=ap;
X		break;
X	    default:
X	    help:
X		Msg (0, "Use: %s [-n windowname] [cmd args]\n\
X or: %s -r [host.tty]", myname, myname);
X	    }
X	} else break;
X    }
X    CommNames[0]='}';
X    if ((ShellProg = getenv ("SHELL")) == 0)
X	ShellProg = DefaultShell;
X    ShellArgs[0] = ShellProg;
X    if (ac == 0) {
X	ac = 1;
X	av = ShellArgs;
X    }
X    if ((home = getenv ("HOME")) == 0)
X	Msg (0, "$HOME is undefined.");
X    sprintf (SockPath, "%s/%s", home, SockDir);
X    if (stat (SockPath, &st) == -1) {
X	if (errno == ENOENT) {
X	    if (mkdir (SockPath, 0700) == -1)
X		Msg (errno, "Cannot make directory %s", SockPath);
X	    (void) chown (SockPath, getuid (), getgid ());
X	} else Msg (errno, "Cannot get status of %s", SockPath);
X    } else {
X	if ((st.st_mode & S_IFMT) != S_IFDIR)
X	    Msg (0, "%s is not a directory.", SockPath);
X	if ((st.st_mode & 0777) != 0700)
X	    Msg (0, "Directory %s must have mode 700.", SockPath);
X	if (st.st_uid != getuid ())
X	    Msg (0, "You are not the owner of %s.", SockPath);
X    }
X    (void) gethostname (HostName, MAXSTR);
X    HostName[MAXSTR-1] = '\0';
X    if (ap = index (HostName, '.'))
X	*ap = '\0';
X    strcat (SockPath, "/");
X    SockNamePtr = SockPath + strlen (SockPath);
X    if ((DevTty = open ("/dev/tty", O_RDWR|O_NDELAY)) == -1)
X	Msg (errno, "/dev/tty");
X    if (rflag) {
X	Attach (MSG_ATTACH);
X	Attacher ();
X	/*NOTREACHED*/
X    }
X    if (GetSockName ()) {
X	s = MakeClientSocket (1);
X	SendCreateMsg (s, ac, av, winname);
X	close (s);
X	exit (0);
X    }
X    switch (fork ()) {
X    case -1:
X	Msg (errno, "fork");
X	/*NOTREACHED*/
X    case 0:
X	break;
X    default:
X	Attacher ();
X	/*NOTREACHED*/
X    }
X    AttacherPid = getppid ();
X    ServerSocket = s = MakeServerSocket ();
X    InitTerm ();
X    InitMenu ();
X    MakeNewEnv ();
X    GetTTY (0, &OldMode);
X    InitUtmp ();
X    signal (SIGHUP, SigHup);
X    signal (SIGINT, Finit);
X    signal (SIGQUIT, Finit);
X    signal (SIGTERM, Finit);
X    signal (SIGTTIN, SIG_IGN);
X    signal (SIGTTOU, SIG_IGN);
X    if ((n = MakeWindow (*av, av, winname, (char *)0)) == -1) {
X	SetTTY (0, &OldMode);
X	FinitTerm ();
X	Kill (AttacherPid, SIGHUP);
X	exit (1);
X    }
X    SetCurrWindow (n);
X    HasWindow = 1;
X    SetMode (&OldMode, &NewMode);
X    SetTTY (0, &NewMode);
X    signal (SIGCHLD, SigChld);
X    tv.tv_usec = 0;
X    while (1) {
X	r = 0;
X	w = 0;
X	if (inlen && curr->wpid>=0)
X	    w |= 1 << curr->ptyfd;
X	else
X	    r |= 1 << 0;
X	for (pp = wtab; pp < wtab+MAXWIN; ++pp) {
X	    if (!(p = *pp))
X		continue;
X	    if (p->wpid >=0)
X	    	r |= 1 << p->ptyfd;
X	}
X	r |= 1 << s;
X	(void) fflush (stdout);
X	if (GotSignal) {
X	    SigHandler ();
X	    continue;
X	}
X	if (select (32, &r, &w, &x, (struct timeval *)0) == -1) {
X	    if (errno == EINTR)
X		continue;
X	    HasWindow = 0;
X	    Msg (errno, "select");
X	    /*NOTREACHED*/
X	}
X	if (GotSignal) {
X	    SigHandler ();
X	    continue;
X	}
X	if (r & 1 << s) {
X	    ReceiveMsg (s);
X	}
X	if (r & 1 << 0) {
X	    if (ESCseen) {
X		inbuf[0] = Esc;
X		inlen = read (0, inbuf+1, IOSIZE-1) + 1;
X		ESCseen = 0;
X	    } else {
X		inlen = read (0, inbuf, IOSIZE);
X	    }
X	    if (inlen > 0)
X		inlen = ProcessInput (inbuf, inlen);
X	    if (inlen > 0)
X		continue;
X	}
X	if (GotSignal) {
X	    SigHandler ();
X	    continue;
X	}
X	if (curr && w & 1 << curr->ptyfd && inlen > 0) {
X	    if ((len = write (curr->ptyfd, inbuf, inlen)) > 0) {
X		inlen -= len;
X		bcopy (inbuf+len, inbuf, inlen);
X	    }
X	}
X	if (GotSignal) {
X	    SigHandler ();
X	    continue;
X	}
X	for (n=0; n<MAXWIN; n++) {
X	    if (!(p = wtab[n]))
X		continue;
X	    if (r & 1 << p->ptyfd) {
X		if ((len = read (p->ptyfd, buf, IOSIZE)) == -1) {
X		    if (errno == EWOULDBLOCK)
X			len = 0;
X		}
X		if (len > 0)
X		    WriteString (n, buf, len);
X	    }
X	}
X	if (GotSignal)
X	    SigHandler ();
X    }
X    /*NOTREACHED*/
X}
X
Xstatic InitTerm() {
X    register char *s;
X
X    if ((s = getenv("TERM")) == 0)
X	Msg(0, "No TERM in environment.");
X    if (strcmp(s,"mgr"))
X	Msg(0, "Only runs on mgr terminals.");
X
X    m_setup(M_FLUSH);
X    m_push(P_DEFAULT|P_POSITION);
X    m_dupkey(Esc);
X}
X
Xstatic FinitTerm() {
X    m_nomenu2(); m_nomenu();
X    m_clearmenu(1); m_clearmenu(2);
X    m_clearevent(ACTIVATE); m_clearevent(RESHAPE); m_clearevent(DESTROY);
X    m_popall();
X}
X    
Xstatic WriteString(n, buf, len) int n, len; char *buf; {
X    register int i, j;
X    register struct win *p;
X
X    p=wtab[n];
X
X    if (!Detached) {
X	m_selectwin(n);
X	write(fileno(m_termout), buf, len);
X    } else
X	p->win_ok=0;		/* Screen no longer matches reality... */
X
X    i=SCRBUFSIZE - (p->outptr - p->outbuf);
X    if (i>=len) {
X	bcopy(buf,p->outptr,len);
X	if (i==len) {
X		p->outptr=p->outbuf;
X		p->outful=1;
X	} else
X		p->outptr+=len;
X    } else {
X	j=len-i;
X	bcopy(buf,p->outptr,i);
X	bcopy(&buf[i],p->outbuf,j);
X	p->outptr=p->outbuf+j;
X	p->outful=1;
X    }
X}
X
Xstatic SigHandler () {
X    while (GotSignal) {
X	GotSignal = 0;
X	DoWait ();
X    }
X}
X
Xstatic void SigChld () {
X    GotSignal = 1;
X}
X
Xstatic void SigHup () {
X    Detach (0);
X}
X
Xstatic DoWait () {
X    register pid;
X    register int n;
X    register struct win **pp;
X    union wait wstat;
X
X    while ((pid = wait3 (&wstat, WNOHANG|WUNTRACED, NULL)) > 0) {
X	for (n = 0; n<MAXWIN; n++) {
X	    if (wtab[n] && pid == wtab[n]->wpid) {
X		if (WIFSTOPPED (wstat)) {
X		    (void) killpg (getpgrp (wtab[n]->wpid), SIGCONT);
X		} else
X			KillWindow(n);
X	    }
X	}
X    }
X    for (n=0; n<MAXWIN; n++)
X	if (wtab[n] && wtab[n]->wpid >=0)
X		break;
X    if (n == MAXWIN)
X	Finit();
X}
X
Xstatic KillWindow (n) int n; {
X    if (Detached || n!=CurrNum || !n) {
X	WriteString(n,"[window shut down]",18);
X	wtab[n]->wpid = -1;	/* Shut it down, but don't close */
X	if (!n)
X    	    FreeWindow (n);	/* Don't ever close window 0, but free it */
X    } else {
X	CurrNum = 0;
X	curr = 0;
X    	FreeWindow (n);
X    }
X}
X
Xstatic void Finit () {
X    register int n;
X    register struct win *p, **pp;
X
X    for (n=0; n<MAXWIN; n++) {
X	if (wtab[n])
X	    FreeWindow(n);
X    }
X    SetTTY (0, &OldMode);
X    FinitTerm ();
X    printf ("\r[rmgr is terminating]\033c\n");
X    Kill (AttacherPid, SIGHUP);
X    exit (0);
X}
X
Xstatic linemode(onoff) int onoff; {
X	struct sgttyb buff;
X	gtty(0,&buff);
X	if (onoff)
X		buff.sg_flags &= ~CBREAK;
X	else
X		buff.sg_flags |= CBREAK;
X	stty(0,&buff);
X}
X
Xstatic ProcessInput (buf, len) char *buf; {
X    register n, k;
X    register char *s, *p, *q;
X    register struct win **pp;
X
X    for (s = p = buf; len > 0; len--, s++) {
X	if (*s == Esc) {
X	    if (len > 1) {
X		len--; s++;
X		if (*s == Esc) {
X		    *p++ = Esc;
X		} else {		/* Process event or menu string */
X		if (*s != ' ') {
X		    n=(*s++)-'!';
X		    len--;
X		    if (!len) {		/* Make sure to get 3rd char */
X			while(!read(0,s,1));
X			len++;
X		    }
X		    p=buf;
X		    switch (*s) {	/* Event Strings */
X			case 'A':
X				SetCurrWindow(n);
X				break;
X			case 'D':
X				FreeWindow(n);
X				break;
X			case 'R':
X				SizeWindow(n);
X				break;
X			case 'c':	/* Menu Strings */
X				if ((n=MakeWindow((char *)0, (char *)0,
X					"", (char *)0)) != -1)
X					SetCurrWindow(n);
X				break;
X			case 'd':
X				VoluntaryDetach=1;
X				Detach(0);
X				break;
X			case 'f':
X				SwitchWindow(n);
X				break;
X			case 'k':
X				KillWindow(n);
X				break;
X			case 'n':
X				if ((n=MakeWindow(ShellProg, ShellArgs,
X					"", (char *)0)) != -1)
X					SetCurrWindow(n);
X				break;
X			case 'q':
X				Finit();
X				break;
X			case 's':
X				VoluntaryDetach=1;
X				Detach(1);
X				break;
X			}
X		    }
X		} 
X	    } else ESCseen = 1;
X	} else *p++ = *s;
X    }
X    return p - buf;
X}
X
Xstatic SwitchWindow(n) {
X    if (wtab[n]) {
X    	SetCurrWindow(n);
X    	m_setmode(M_ACTIVATE);
X    }
X}
X
Xstatic SetCurrWindow (n) {
X    CurrNum = n;
X    curr = wtab[n];
X    m_selectwin(n);
X    if (curr->wpid < 0)
X	m_setmode(M_NOINPUT);	/* Dead window, disallow input */
X}
X
Xstatic SizeWindow (n) int n; {
X	struct winsize wbuf;
X	char buf[32]; 
X
X	m_selectwin(n);
X	m_getinfo(G_WINSIZE);
X	linemode(1);
X	read(0,buf,31);
X	linemode(0);
X	buf[31]='\0';
X	sscanf(buf,"%*c %d %d",&(wtab[n]->cols),&(wtab[n]->rows));
X#ifdef	TIOCSWINSZ
X	wbuf.ws_row = wtab[n]->rows;
X	wbuf.ws_col = wtab[n]->cols;
X	ioctl (wtab[n]->ptyfd, TIOCSWINSZ, &wbuf);
X#endif
X}
X
Xstatic FreeWindow (n) register int n; {
X    register i;
X    register struct win *wp;
X
X    wp=wtab[n];
X    if (wp) {
X    	RemoveUtmp (wp->slot);
X    	(void) chmod (wp->tty, 0666);
X    	(void) chown (wp->tty, 0, 0);
X    	close (wp->ptyfd);
X    	free (wp);
X    	wtab[n]=0;
X    }
X    if (n) {
X	m_selectwin(n);
X	m_clearevent(DESTROY);	/* Avoid redundant calling of FreeWindow */
X	m_destroywin(n);
X    }
X    CollectNames();
X    sleep(1);
X    UpdateMenu(n);
X}
X
Xstatic CollectNames() {		/* Collect command names for second menu */
X    register struct win **wp;
X    register int n;
X    char buf[8];
X
X    CommNames[1]='\0';
X    for (wp=wtab; wp<wtab+MAXWIN; wp++)
X	if (*wp) {
X	    strcat(CommNames,(*wp)->cmd);
X	    strcat(CommNames,"}");
X	}
X
X    buf[0]=Esc;
X    buf[1]='!';
X    buf[2]='f';
X    buf[3]='}';
X    buf[4]='\0';
X    for (n=0;n<MAXWIN;n++)
X	if (wtab[n]) {
X	    buf[1]=n+'!';
X	    strcat(CommNames,buf);
X	}
X}
X
Xstatic char *MenuNames[]={"MGR Windows =>", "", "-=-=-=-=-=-=-",
X	"Shell Window","Other Window","Kill Window","Suspend","Detach","Quit"};
Xstatic char MenuNull='\0';
Xstatic char MenuActs[24];
Xstatic char MenuLabs[8]="ncksdq";
Xstatic struct menu_entry MainMenu[9];
X
Xstatic InitMenu() {
X    register int i;
X    register char *ptr;
X
X    ptr=MenuActs;
X    for (i=3;i<9;i++) {
X	MainMenu[i].action=ptr;
X	*ptr++=Esc;
X	*ptr++='!';
X	*ptr++=MenuLabs[i-3];
X	*ptr++='\0';
X    }
X    for (i=0;i<3;i++)
X	MainMenu[i].action=(&MenuNull);
X
X    for (i=0;i<9;i++)
X	MainMenu[i].value=MenuNames[i];
X}
X
Xstatic MakeMenu(n) int n; {
X    MenuActs[9]=n+'!';
X    MainMenu[1].value=wtab[n]->cmd;
X    menu_load(1,9,MainMenu);
X    m_loadmenu(2,CommNames);
X    m_linkmenu(1,0,2,MF_SNIP);
X    m_selectmenu2(1);
X}
X
Xstatic UpdateMenu(n) int n; {
X    register int i;
X
X    for (i=0; i<MAXWIN; i++)
X	if (wtab[i] && i!=n) {
X	    m_selectwin(i);
X	    m_loadmenu(2,CommNames);
X	}
X}
X
Xstatic Parse (buf, args) char *buf, **args; {
X    register char *p = buf, **ap = args;
X    register delim, argc = 0;
X
X    argc = 0;
X    for (;;) {
X	while (*p && (*p == ' ' || *p == '\t')) ++p;
X	if (*p == '\0' || *p == '#')
X	    return argc;
X	if (argc > MAXARGS-1)
X	    Msg (0, "Too many tokens.");
X	delim = 0;
X	if (*p == '"' || *p == '\'') {
X	    delim = *p; *p = '\0'; ++p;
X	}
X	++argc;
X	*ap = p; ++ap;
X	while (*p && !(delim ? *p == delim : (*p == ' ' || *p == '\t')))
X	    ++p;
X	if (*p == '\0') {
X	    if (delim)
X		Msg (0, "Missing quote.");
X	    else
X		return argc;
X	}
X	*p++ = '\0';
X    }
X}
X
Xstatic char TermBuf[MAXLINE];
X
Xstatic MakeWindow (prog, args, name, dir)
X	char *prog, *name, **args, *dir; {
X    register struct win *p;
X    register char **cp;
X    register n, f;
X    int tf;
X    int mypid;
X    char ebuf[16];
X    char ibuf[MY_MAXLINE];
X    char *av[MAXARGS];
X    char *ptr,*index();
X
X    if ((f = OpenPTY ()) == -1) {
X	Msg (0, "No more PTYs.");
X	return -1;
X    }
X    (void) fcntl (f, F_SETFL, FNDELAY);
X    if ((p = (struct win *)malloc (sizeof (struct win))) == 0) {
X	Msg (0, "Out of memory.");
X	return -1;
X    }
X    p->outful=0;
X    p->outptr=p->outbuf;
X
X    m_ttyset();
X    if (!wtab[0]) {
X	n=0;
X    } else {
X	m_resetflags(CBREAK);
X	m_newwin(0,0,50,50);
X	read(0, TermBuf, MAXLINE);
X	TermBuf[MAXLINE-1]='\0';
X    	n = atoi(&TermBuf[2]);
X    }
X    	m_selectwin(n);
X        m_dupkey(Esc);
X    	m_sizeall(n*5,n*5,80,24);
X	strcpy(TermBuf,"TERMCAP=");
X	m_getinfo(G_TERMCAP);
X	read(0, &TermBuf[6], MAXLINE);
X	TermBuf[MAXLINE-1]='\0';
X	m_setflags(CBREAK);
X	TermBuf[6]='P';
X	TermBuf[7]='=';
X	ptr=index(TermBuf,'\n');
X	*ptr='\0';
X    m_clear();
X    sprintf(ebuf,EventStr,Esc,n+'!');
X    m_setevent(ACTIVATE,ebuf);
X    ebuf[2]='D';
X    m_setevent(DESTROY,ebuf);
X    ebuf[2]='R';
X    m_setevent(RESHAPE,ebuf);
X    m_ttyreset();
X    wtab[n]=p;
X
X    p->ptyfd = f;
X
X    if (!prog) {
X	int argc;
X
X	SetTTY(0, &OldMode);
X	printf("Enter command and arguments: ");
X	fflush(stdout);
X	ptr=gets(ibuf);
X	SetTTY(0, &NewMode);
X	if (!ptr || ((argc = Parse(ibuf, av)) == 0)) {
X	    KillWindow(n);
X	    return;
X	}
X	if (strcmp(av[0],"-n") == 0) {
X	    name=av[1];
X	    args=(&av[2]);
X	} else 
X	    args=av;
X	prog=args[0];
X    }
X
X    strncpy (p->cmd, *name ? name : Filename (args[0]), MAXSTR-1);
X    p->cmd[MAXSTR-1] = '\0';
X
X    CollectNames();
X    MakeMenu(n);
X    UpdateMenu(n);
X    strncpy (p->tty, TtyName, MAXSTR-1);
X    (void) chown (TtyName, getuid (), getgid ());
X    (void) chmod (TtyName, TtyMode);
X    p->slot = SetUtmp (TtyName);
X    p->rows=24;
X    p->cols=80;
X    switch (p->wpid = fork ()) {
X    case -1:
X	Msg (errno, "fork");
X	free ((char *)p);
X	return -1;
X    case 0:
X	signal (SIGHUP, SIG_DFL);
X	signal (SIGINT, SIG_DFL);
X	signal (SIGQUIT, SIG_DFL);
X	signal (SIGTERM, SIG_DFL);
X	signal (SIGTTIN, SIG_DFL);
X	signal (SIGTTOU, SIG_DFL);
X	setuid (getuid ());
X	setgid (getgid ());
X	if (dir && chdir (dir) == -1) {
X	    SendErrorMsg ("Cannot chdir to %s: %s", dir, sys_errlist[errno]);
X	    exit (1);
X	}
X	mypid = getpid ();
X	ioctl (DevTty, TIOCNOTTY, (char *)0);
X	if ((tf = open (TtyName, O_RDWR)) == -1) {
X	    SendErrorMsg ("Cannot open %s: %s", TtyName, sys_errlist[errno]);
X	    exit (1);
X	}
X	(void) dup2 (tf, 0);
X	(void) dup2 (tf, 1);
X	(void) dup2 (tf, 2);
X	for (f = getdtablesize () - 1; f > 2; f--)
X	    close (f);
X	ioctl (0, TIOCSPGRP, &mypid);
X	(void) setpgrp (0, mypid);
X	SetTTY (0, &OldMode);
X#ifdef	TIOCSWINSZ
X	{
X		struct winsize wbuf;
X		wbuf.ws_row = p->rows;
X		wbuf.ws_col=p->cols;
X		ioctl (0, TIOCSWINSZ, &wbuf);
X	}
X#endif
X	NewEnv[2] = TermBuf;
X	sprintf (ebuf, "WINDOW=%d", n);
X	NewEnv[3] = ebuf;
X	execvpe (prog, args, NewEnv);
X	SendErrorMsg ("Cannot exec %s: %s", prog, sys_errlist[errno]);
X	exit (1);
X    }
X    return n;
X}
X
Xstatic execvpe (prog, args, env) char *prog, **args, **env; {
X    register char *path, *p;
X    char buf[1024];
X    char *shargs[MAXARGS+1];
X    register i, eaccess = 0;
X
X    if (prog[0] == '/')
X	path = "";
X    else if ((path = getenv ("PATH")) == 0)
X	path = DefaultPath;
X    do {
X	p = buf;
X	while (*path && *path != ':')
X	    *p++ = *path++;
X	if (p > buf)
X	    *p++ = '/';
X	strcpy (p, prog);
X	if (*path)
X	    ++path;
X	execve (buf, args, env);
X	switch (errno) {
X	case ENOEXEC:
X	    shargs[0] = DefaultShell;
X	    shargs[1] = buf;
X	    for (i = 1; shargs[i+1] = args[i]; ++i)
X		;
X	    execve (DefaultShell, shargs, env);
X	    return;
X	case EACCES:
X	    eaccess = 1;
X	    break;
X	case ENOMEM: case E2BIG: case ETXTBSY:
X	    return;
X	}
X    } while (*path);
X    if (eaccess)
X	errno = EACCES;
X}
X
Xstatic OpenPTY () {
X    register char *p, *l, *d;
X    register i, f, tf;
X
X    strcpy (PtyName, PtyProto);
X    strcpy (TtyName, TtyProto);
X    for (p = PtyName, i = 0; *p != 'X'; ++p, ++i) ;
X    for (l = "qpr"; *p = *l; ++l) {
X	for (d = "0123456789abcdef"; p[1] = *d; ++d) {
X	    if ((f = open (PtyName, O_RDWR)) != -1) {
X		TtyName[i] = p[0];
X		TtyName[i+1] = p[1];
X		if ((tf = open (TtyName, O_RDWR)) != -1) {
X		    close (tf);
X		    return f;
X		}
X		close (f);
X	    }
X	}
X    }
X    return -1;
X}
X
Xstatic SetTTY (fd, mp) struct mode *mp; {
X    ioctl (fd, TIOCSETP, &mp->m_ttyb);
X    ioctl (fd, TIOCSETC, &mp->m_tchars);
X    ioctl (fd, TIOCSLTC, &mp->m_ltchars);
X    ioctl (fd, TIOCLSET, &mp->m_lmode);
X    ioctl (fd, TIOCSETD, &mp->m_ldisc);
X}
X
Xstatic GetTTY (fd, mp) struct mode *mp; {
X    ioctl (fd, TIOCGETP, &mp->m_ttyb);
X    ioctl (fd, TIOCGETC, &mp->m_tchars);
X    ioctl (fd, TIOCGLTC, &mp->m_ltchars);
X    ioctl (fd, TIOCLGET, &mp->m_lmode);
X    ioctl (fd, TIOCGETD, &mp->m_ldisc);
X}
X
Xstatic SetMode (op, np) struct mode *op, *np; {
X    *np = *op;
X    np->m_ttyb.sg_flags &= ~(CRMOD|ECHO);
X    np->m_ttyb.sg_flags |= CBREAK;
X    np->m_tchars.t_intrc = -1;
X    np->m_tchars.t_quitc = -1;
X    np->m_ltchars.t_suspc = -1;
X    np->m_ltchars.t_dsuspc = -1;
X    np->m_ltchars.t_flushc = -1;
X    np->m_ltchars.t_lnextc = -1;
X}
X
Xstatic char *GetTtyName () {
X    register char *p;
X    register n;
X
X    for (p = 0, n = 0; n <= 2 && !(p = ttyname (n)); n++)
X	;
X    if (!p || *p == '\0')
X	Msg (0, "rmgr must run on a tty.");
X    return p;
X}
X
Xstatic Attach (how) {
X    register s, lasts, found = 0;
X    register DIR *dirp;
X    register struct direct *dp;
X    struct msg m;
X    char last[MAXNAMLEN+1];
X
X    if (SockName) {
X	if ((lasts = MakeClientSocket (0)) == -1)
X	    if (how == MSG_CONT)
X		Msg (0,
X		    "This session has already been continued from elsewhere.");
X	    else
X		Msg (0, "There is no session to be resumed from %s.", SockName);
X    } else {
X	if ((dirp = opendir (SockPath)) == NULL)
X	    Msg (0, "Cannot open %s", SockPath);
X	while ((dp = readdir (dirp)) != NULL) {
X	    SockName = dp->d_name;
X	    if (SockName[0] == '.')
X		continue;
X	    if ((s = MakeClientSocket (0)) != -1) {
X		if (found == 0) {
X		    strcpy (last, SockName);
X		    lasts = s;
X		} else {
X		    if (found == 1) {
X			printf ("There are detached sessions on:\n");
X			printf ("   %s\n", last);
X			close (lasts);
X		    }
X		    printf ("   %s\n", SockName);
X		    close (s);
X		}
X		found++;
X	    }
X	}
X	if (found == 0)
X	    Msg (0, "There is no session to be resumed.");
X	if (found > 1)
X	    Msg (0, "Type \"rmgr -r host.tty\" to resume one of them.");
X	closedir (dirp);
X	strcpy (SockNamePtr, last);
X	SockName = SockNamePtr;
X    }
X    m.type = how;
X    strcpy (m.m.attach.tty, GetTtyName ());
X    m.m.attach.apid = getpid ();
X    if (write (lasts, (char *)&m, sizeof (m)) != sizeof (m))
X	Msg (errno, "write");
X}
X
Xstatic void AttacherFinit () {
X    exit (0);
X}
X
Xstatic void ReAttach () {
X    Attach (MSG_CONT);
X}
X
Xstatic Attacher () {
X    signal (SIGHUP, AttacherFinit);
X    signal (SIGCONT, ReAttach);
X    while (1)
X	pause ();
X}
X
Xstatic Detach (suspend) {
X    register struct win **pp;
X    register int i;
X
X    if (Detached)
X	return;
X    signal (SIGHUP, SIG_IGN);
X    if (VoluntaryDetach) {	/* Can't save state if line dropped */
X    for (i=MAXWIN-1;i>0;i--) {
X	if (wtab[i]) {
X	m_selectwin(i);
X	m_nomenu2();
X	m_push(P_EVENT);
X    	}
X    }
X    m_selectwin(0);
X    m_nomenu2(); m_nomenu();
X    m_push(P_ALL);
X    m_setmode(M_ACTIVATE);
X    }
X    SetTTY (0, &OldMode);
X    if (suspend) {
X	Kill (AttacherPid, SIGTSTP);
X	for (pp=wtab; pp < wtab+MAXWIN; ++pp)
X	    if (*pp) (*pp)->win_ok=1;
X    } else {
X	for (pp=wtab; pp < wtab+MAXWIN; ++pp)
X	    if (*pp) RemoveUtmp ((*pp)->slot);
X	printf ("\n[detached]\n");
X	Kill (AttacherPid, SIGHUP);
X	AttacherPid = 0;
X    }
X    close (0);
X    close (1);
X    close (2);
X    ioctl (DevTty, TIOCNOTTY, (char *)0);
X    Detached = 1;
X    do {
X	ReceiveMsg (ServerSocket); 
X    } while (Detached);
X    if (!suspend) {
X	for (pp = wtab; pp < wtab+MAXWIN; ++pp)
X	    if (*pp) (*pp)->slot = SetUtmp ((*pp)->tty);
X    }
X    signal (SIGHUP, SigHup);
X}
X
Xstatic Kill (pid, sig) {
X    if (pid != 0)
X	(void) kill (pid, sig);
X}
X
Xstatic GetSockName () {
X    register client;
X    static char buf[2*MAXSTR];
X
X    if ((SockName = getenv ("STY")) != 0 && *SockName != '\0') {
X	client = 1;
X	setuid (getuid ());
X	setgid (getgid ());
X    } else {
X	sprintf (buf, "%s.%s", HostName, Filename (GetTtyName ()));
X	SockName = buf;
X	client = 0;
X    }
X    return client;
X}
X
Xstatic MakeServerSocket () {
X    register s;
X    struct sockaddr_un a;
X    char *p;
X
X    if ((s = socket (AF_UNIX, SOCK_STREAM, 0)) == -1)
X	Msg (errno, "socket");
X    a.sun_family = AF_UNIX;
X    strcpy (SockNamePtr, SockName);
X    strcpy (a.sun_path, SockPath);
X    if (connect (s, (struct sockaddr *)&a, strlen (SockPath)+2) != -1) {
X	p = Filename (SockPath);
X	Msg (0, "You already have a session running on %s.\n\
XIf it has been detached, try \"rmgr -r\".", p);
X	/*NOTREACHED*/
X    }
X    (void) unlink (SockPath);
X    if (bind (s, (struct sockaddr *)&a, strlen (SockPath)+2) == -1)
X	Msg (errno, "bind");
X    (void) chown (SockPath, getuid (), getgid ());
X    if (listen (s, 5) == -1)
X	Msg (errno, "listen");
X    return s;
X}
X
Xstatic MakeClientSocket (err) {
X    register s;
X    struct sockaddr_un a;
X
X    if ((s = socket (AF_UNIX, SOCK_STREAM, 0)) == -1)
X	Msg (errno, "socket");
X    a.sun_family = AF_UNIX;
X    strcpy (SockNamePtr, SockName);
X    strcpy (a.sun_path, SockPath);
X    if (connect (s, (struct sockaddr *)&a, strlen (SockPath)+2) == -1) {
X	if (err) {
X	    Msg (errno, "connect: %s", SockPath);
X	} else {
X	    close (s);
X	    return -1;
X	}
X    }
X    return s;
X}
X
Xstatic SendCreateMsg (s, ac, av, name) char **av, *name; {
X    struct msg m;
X    register char *p;
X    register len, n;
X
X    m.type = MSG_CREATE;
X    p = m.m.create.line;
X    for (n = 0; ac > 0 && n < MAXARGS-1; ++av, --ac, ++n) {
X	len = strlen (*av) + 1;
X	if (p + len >= m.m.create.line+MY_MAXLINE)
X	    break;
X	strcpy (p, *av);
X	p += len;
X    }
X    m.m.create.nargs = n;
X    if (name)
X	strcpy(m.m.create.name,name);
X    else
X	m.m.create.name[0]='\0';
X    if (getwd (m.m.create.dir) == 0)
X	Msg (0, "%s", m.m.create.dir);
X    if (write (s, (char *)&m, sizeof (m)) != sizeof (m))
X	Msg (errno, "write");
X}
X
X/*VARARGS1*/
Xstatic SendErrorMsg (fmt, p1, p2, p3, p4, p5, p6) char *fmt; {
X    register s;
X    struct msg m;
X
X    s = MakeClientSocket (1);
X    m.type = MSG_ERROR;
X    sprintf (m.m.message, fmt, p1, p2, p3, p4, p5, p6);
X    (void) write (s, (char *)&m, sizeof (m));
X    close (s);
X    sleep (2);
X}
X
Xstatic char m_rbuf[MAXLINE];
X
Xstatic char *m_read() {
X    register char *ptr; char *index();
X    register int i;
X
X    i=read(0,m_rbuf,MAXLINE-1);
X    m_rbuf[i]='\0';
X    ptr=index(m_rbuf,Esc);
X    if (!ptr)
X	ptr=m_rbuf;
X    else
X	ptr++;
X    return(ptr);
X}
X
Xstatic ReplayWindow(p) struct win *p; {
X    register int n;
X
X	n=p->outptr-p->outbuf;
X	if (p->outful)		/* Replay last screen of data */
X		write(1,p->outptr,SCRBUFSIZE-n);
X	write(1,p->outbuf,n);
X}
X
Xstatic RestoreWindows(mt) int mt; {
X    register int i, j=0;
X    struct window_data wd;
X    register char *ptr;
X    register struct win *p;
X    char buf[8];
X    int n, nw;
X    int oldset=1;
X
X    if (mt != MSG_CONT) {	/* Restore after a detach, not suspend */
X    linemode(1);
X    for (i=MAXWIN-1; i>=0; i--) {
X	m_selectwin(i);
X	m_getinfo(G_ID);
X	ptr=m_read();
X	sscanf(ptr,"%d %d",&n, &nw);
X	if (wtab[i]) {
X	    p=wtab[i];
X	    if (n!=i) {		/* That one didn't exist, remake it... */
X		oldset=0;	/* Looks like the old context is gone. */
X		m_newwin(j,j,50,50);
X		ptr=m_read();
X		n=atoi(ptr);
X		if (!n) 
X			Msg(0, "Error restoring window.");
X		m_selectwin(n);
X	    }
X	    if (!oldset) {
X		m_size(p->cols,p->rows);
X	    	m_dupkey(Esc);
X	    	sprintf(buf,EventStr,Esc,n+'!');
X	    	m_setevent(ACTIVATE,buf);
X	    	buf[2]='D';
X	    	m_setevent(DESTROY,buf);
X	    	buf[2]='R';
X	    	m_setevent(RESHAPE,buf);
X		MakeMenu(n);
X		ReplayWindow(p);
X	    } else if (VoluntaryDetach) {
X		m_pop();
X	    } else ReplayWindow(p);	/* Redraw for hangup */
X	} else if (i) {		/* Gee, a window we didn't create. Byebye. */
X	    oldset=0;		/* Again, we must have lost the old stuff */
X	    m_destroywin(i);
X	}
X    }
X    linemode(0);
X    } else {
X	for (i=0;i<MAXWIN;i++) {
X	    p=wtab[i];
X	    if (p) {
X		m_selectwin(i);
X		m_pop();
X		m_selectmenu2(1);
X		if (!p->win_ok)
X		    ReplayWindow(p);
X	    }
X	}
X    }
X    VoluntaryDetach=0;
X}
X	    
Xstatic ReceiveMsg (s) {
X    register ns;
X    struct sockaddr_un a;
X    int left, len = sizeof (a);
X    struct msg m;
X    char *p;
X
X    if ((ns = accept (s, (struct sockaddr *)&a, &len)) == -1) {
X	Msg (errno, "accept");
X	return;
X    }
X    p = (char *)&m;
X    left = sizeof (m);
X    while (left > 0 && (len = read (ns, p, left)) > 0) {
X	p += len;
X	left -= len;
X    }
X    close (ns);
X    if (len == -1)
X	Msg (errno, "read");
X    if (left > 0)
X	return;
X    switch (m.type) {
X    case MSG_CREATE:
X	if (!Detached)
X	    ExecCreate (&m);
X	break;
X    case MSG_CONT:
X	if (m.m.attach.apid != AttacherPid || !Detached)
X	    break;	/* Intruder Alert */
X	/*FALLTHROUGH*/
X    case MSG_ATTACH:
X	if (Detached) {
X	    if (kill (m.m.attach.apid, 0) == 0 &&
X		    open (m.m.attach.tty, O_RDWR) == 0) {
X		(void) dup (0);
X		(void) dup (0);
X		AttacherPid = m.m.attach.apid;
X		Detached = 0;
X		GetTTY (0, &OldMode);
X		SetMode (&OldMode, &NewMode);
X		SetTTY (0, &NewMode);
X		RestoreWindows(m.type);
X		SwitchWindow(CurrNum);
X	    }
X	} else {
X	    Kill (m.m.attach.apid, SIGHUP);
X	    Msg (0, "Not detached.");
X	}
X	break;
X    case MSG_ERROR:
X	Msg (0, "%s", m.m.message);
X	break;
X    default:
X	Msg (0, "Invalid message (type %d).", m.type);
X    }
X}
X
Xstatic ExecCreate (mp) struct msg *mp; {
X    char *args[MAXARGS];
X    register n;
X    register char **pp = args, *p = mp->m.create.line;
X
X    for (n = mp->m.create.nargs; n > 0; --n) {
X	*pp++ = p;
X	p += strlen (p) + 1;
X    }
X    *pp = 0;
X    if ((n = MakeWindow (mp->m.create.line, args, mp->m.create.name,
X	    mp->m.create.dir)) != -1)
X	SwitchWindow (n);
X}
X
Xstatic char **SaveArgs (argc, argv) register argc; register char **argv; {
X    register char **ap, **pp;
X
X    if ((pp = ap = (char **)malloc ((argc+1) * sizeof (char **))) == 0)
X	Msg (0, "Out of memory.");
X    while (argc--) {
X	if ((*pp = malloc (strlen (*argv)+1)) == 0)
X	    Msg (0, "Out of memory.");
X	strcpy (*pp, *argv);
X	++pp; ++argv;
X    }
X    *pp = 0;
X    return ap;
X}
X
Xstatic MakeNewEnv () {
X    register char **op, **np = NewEnv;
X    static char buf[MAXSTR];
X
X    if (strlen (SockName) > MAXSTR-5)
X	SockName = "?";
X    sprintf (buf, "STY=%s", SockName);
X    *np++ = buf;
X    *np++ = "TERM=mgr";
X    np += 2;
X    for (op = environ; *op; ++op) {
X	if (np == NewEnv + MAXARGS - 1)
X	    break;
X	if (!IsSymbol (*op, "TERM") && !IsSymbol (*op, "TERMCAP")
X		&& !IsSymbol (*op, "STY"))
X	    *np++ = *op;
X    }
X    *np = 0;
X}
X
Xstatic IsSymbol (e, s) register char *e, *s; {
X    register char *p;
X    register n;
X
X    for (p = e; *p && *p != '='; ++p) ;
X    if (*p) {
X	*p = '\0';
X	n = strcmp (e, s);
X	*p = '=';
X	return n == 0;
X    }
X    return 0;
X}
X
X/*VARARGS2*/
XMsg (err, fmt, p1, p2, p3, p4, p5, p6) char *fmt; {
X    char buf[1024];
X    register char *p = buf;
X
X    if (Detached)
X	return;
X    sprintf (p, fmt, p1, p2, p3, p4, p5, p6);
X    if (err) {
X	p += strlen (p);
X	if (err > 0 && err < sys_nerr)
X	    sprintf (p, ": %s", sys_errlist[err]);
X	else
X	    sprintf (p, ": Error %d", err);
X    }
X    printf ("%s\r\n", buf);
X    if (!HasWindow) {
X	Kill (AttacherPid, SIGHUP);
X	exit (1);
X    }
X}
X
Xstatic char *Filename (s) char *s; {
X    register char *p;
X
X    p = s + strlen (s) - 1;
X    while (p >= s && *p != '/') --p;
X    return ++p;
X}
X
Xstatic IsNum (s, base) register char *s; register base; {
X    for (base += '0'; *s; ++s)
X	if (*s < '0' || *s > base)
X	    return 0;
X    return 1;
X}
X
Xstatic InitUtmp () {
X    struct passwd *p;
X
X    if ((utmpf = open (UtmpName, O_WRONLY)) == -1) {
X	if (errno != EACCES)
X	    Msg (errno, UtmpName);
X	return;
X    }
X    if ((LoginName = getlogin ()) == 0 || LoginName[0] == '\0') {
X	if ((p = getpwuid (getuid ())) == 0)
X	    return;
X	LoginName = p->pw_name;
X    }
X    utmp = 1;
X}
X
Xstatic SetUtmp (name) char *name; {
X    register char *p;
X    register struct ttyent *tp;
X    register slot = 1;
X    struct utmp u;
X
X    if (!utmp)
X	return 0;
X    if (p = rindex (name, '/'))
X	++p;
X    else p = name;
X    setttyent ();
X    while ((tp = getttyent ()) != NULL && strcmp (p, tp->ty_name) != 0)
X	++slot;
X    if (tp == NULL)
X	return 0;
X    strncpy (u.ut_line, p, 8);
X    strncpy (u.ut_name, LoginName, 8);
X    u.ut_host[0] = '\0';
X    time (&u.ut_time);
X    (void) lseek (utmpf, (long)(slot * sizeof (u)), 0);
X    (void) write (utmpf, (char *)&u, sizeof (u));
X    return slot;
X}
X
Xstatic RemoveUtmp (slot) {
X    struct utmp u;
X
X    if (slot) {
X	bzero ((char *)&u, sizeof (u));
X	(void) lseek (utmpf, (long)(slot * sizeof (u)), 0);
X	(void) write (utmpf, (char *)&u, sizeof (u));
X    }
X}
X
X#ifndef GETTTYENT
X
Xstatic setttyent () {
X    struct stat s;
X    register f;
X    register char *p, *ep;
X
X    if (ttnext) {
X	ttnext = tt;
X	return;
X    }
X    if ((f = open (ttys, O_RDONLY)) == -1 || fstat (f, &s) == -1)
X	Msg (errno, ttys);
X    if ((tt = malloc (s.st_size + 1)) == 0)
X	Msg (0, "Out of memory.");
X    if (read (f, tt, s.st_size) != s.st_size)
X	Msg (errno, ttys);
X    close (f);
X    for (p = tt, ep = p + s.st_size; p < ep; ++p)
X	if (*p == '\n') *p = '\0';
X    *p = '\0';
X    ttnext = tt;
X}
X
Xstatic struct ttyent *getttyent () {
X    static struct ttyent t;
X
X    if (*ttnext == '\0')
X	return NULL;
X    t.ty_name = ttnext + 2;
X    ttnext += strlen (ttnext) + 1;
X    return &t;
X}
X
X#endif
X
X#ifndef USEBCOPY
Xbcopy (s1, s2, len) register char *s1, *s2; register len; {
X    if (s1 < s2 && s2 < s1 + len) {
X	s1 += len; s2 += len;
X	while (len-- > 0) {
X	    *--s2 = *--s1;
X	}
X    } else {
X	while (len-- > 0) {
X	    *s2++ = *s1++;
X	}
X    }
X}
X#endif
________This_Is_The_END________
if test `wc -c < rmgr.c` -ne    32339; then
	echo 'shar: rmgr.c was damaged during transit (should have been    32339 bytes)'
fi
fi		; : end of overwriting check
echo 'x - rmgr.h'
if test -f rmgr.h; then echo 'shar: not overwriting rmgr.h'; else
sed 's/^X//' << '________This_Is_The_END________' > rmgr.h
X/* Copyright (c) 1987,1988 Oliver Laumann, Technical University of Berlin.
X * Not derived from licensed software.
X *
X * Permission is granted to freely use, copy, modify, and redistribute
X * this software, provided that no attempt is made to gain profit from it,
X * the author is not construed to be liable for any results of using the
X * software, alterations are clearly marked as such, and this notice is
X * not modified.
X */
X
X#include <mgr/term.h>
X
X#define MAXSTR       128
X#define	MAXARGS      64
X
X#define IOSIZE       256
X#define	SCRBUFSIZE	2048	/* About 1 screenful of characters */
X
Xstruct win {
X    int wpid;
X    int ptyfd;
X    int rows;
X    int cols;
X    char cmd[MAXSTR];
X    char tty[MAXSTR];
X    int slot;
X    int win_ok;
X    int outful;
X    char outbuf[SCRBUFSIZE];
X    char *outptr;
X};
X
X#define MY_MAXLINE 1024
X
X#define MSG_CREATE    0
X#define MSG_ERROR     1
X#define MSG_ATTACH    2
X#define MSG_CONT      3
X
Xstruct msg {
X    int type;
X    union {
X	struct {
X	    int nargs;
X	    char line[MY_MAXLINE];
X	    char dir[1024];
X	    char name[MAXSTR];
X	} create;
X	struct {
X	    int apid;
X	    char tty[1024];
X	} attach;
X	char message[MY_MAXLINE];
X    } m;
X};
________This_Is_The_END________
if test `wc -c < rmgr.h` -ne     1169; then
	echo 'shar: rmgr.h was damaged during transit (should have been     1169 bytes)'
fi
fi		; : end of overwriting check
exit 0
--
  -- Howard Chu @ University of Michigan
  one million data bits stored on a chip, one million bits per chip
	if one of those data bits happens to flip,
		one million data bits stored on the chip...



More information about the Comp.sys.att mailing list