hyc@math.lsa.umich.edu (Howard Chu) (10/17/90)
I tried posting this to alt.sources previously, but it seems not to have made it out intact... Rmgr is a program I wrote, using the socket and session startup code from Oliver Laumann's screen program. Run on a remote host, it allows creation of multiple sessions, each in its own MGR window. It also allows for an rmgr session to be suspended and resumed, as well as completely detached, for reattachment at a later time, even from a different workstation. Like screen, it uses BSD sockets and pseudo-ttys. (The use of sockets can easily be replaced with a FIFO. I don't know anything about setting up virtual terminals in Sys V, though.) The program is stable, but does not yet have any fancy configuration options. I use it from an Atari ST running MGR at home, dialed up to a machine at work. From there I start up rmgr and get everywhere else... If you've got an ST, Sun, DECstation, Mac, or Xenix-PC in the same situation, then you can run MGR and you may want to give this a try. -- -- 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...
hyc@math.lsa.umich.edu (Howard Chu) (10/17/90)
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@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@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...