home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Garbo
/
Garbo.cdr
/
mac
/
source
/
layers.zoo
/
layers.3
< prev
next >
Wrap
Text File
|
1990-06-11
|
60KB
|
2,243 lines
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#
# layers.c
#
# This archive created: Thu May 31 21:58:18 1990
# By: Roger L. Long (macintosh@dhw68k.cts.com)
export PATH; PATH=/bin:$PATH
echo shar: extracting "'layers.c'" '(56819 characters)'
if test -f 'layers.c'
then
echo shar: will not over-write existing file "'layers.c'"
else
sed 's/^X//' << \SHAR_EOF > 'layers.c'
X/******** Layers.c
X*********
X********* Layers - MacLayers Multiwindow BSD Socket Driver
X*********
X********* Dave Trissel oakhill!davet
X*********
X********* The sockets handling portion of this control module is based
X********* upon 'screen' by Oliver Laumann whose copyright remains below.
X********* The rest is
X *
X * Copyright (C) 1989 by David W. Trissel
X *
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
Xstatic char LayersVersion[] = "layers 1.00 17-Mar-1990";
X
X/* Layers Changes:
X
X Version .92 22-Mar-1989
X
X Original Distributed version
X
X Version .93 31-Mar-1989
X
X Deleted dl and al termcap entries since they didn't help any
X (al was redundant with sc (scroll) so should never have been created)
X
X SIGINT no longer causes us to quit (left debugging code in by mistake)
X
X Layer #1 is always logged in and takes over as user's login console
X (Real tty disconnected from /etc/utmp file while layers is running)
X
X Version .93b 05-May-1989
X
X Try getenv("PWD") before getwd() so Sun networking won't hang us up
X
X Version .93n 07-Jan-1990
X
X Reset TTY back to normal if initial link handshake fails.
X
X Version 1.00b 22-Jan-1990
X
X Corrected problem of layers forcing all umasks to 000.
X
X Version 1.00 17-Mar-1990
X
X First public release.
X*/
X
X
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 * Modified by Patrick Wolfe (pat@kai.com, kailand!pat)
X * Do whatever you want with (my modifications of) this program, but
X * don't claim you wrote them, don't try to make money from them, and
X * don't remove this notice.
X */
X
X/*
X * Beginning of User Configuration Section
X */
X
X/*
X * SEQUENT -- your host system is Sequent. This changes a setvbuf()
X * call to a setlinebuf(). [Suggested by Peter Newton
X * <newton@cs.utexas.edu>]
X *
X */
X#undef SEQUENT
X
X
X/*
X * GETTTYENT -- your system has the new format /etc/ttys (like 4.3 BSD)
X * and the getttyent(3) library functions.
X *
X */
X#undef GETTTYENT
X
X
X/*
X * LOGINDEFAULT -- if set to 1 (one), windows will login (add entries to
X * /etc/utmp) by default. Set to 0 if you don't want this.
X * (Also see USERLIMIT below). [NOTE: current code always
X * logs layer #1 only unless -l option used on 'layers'
X * command.]
X */
X#define LOGINDEFAULT 0
X
X/*
X * USERLIMIT -- count all non-null entries in /etc/utmp before adding a
X * new entry. Some machine manufacturers (incorrectly) count
X * logins by counting non-null entries in /etc/utmp (instead
X * of counting non-null entries with no hostname and not on
X * a pseudo tty). Sequent does this, so you might reach your
X * limited user license early.
X */
X#define USRLIMIT 32
X
X/*
X * SOCKDIR -- If defined, this directory is where layers sockets will be
X * placed, (actually in a subdirectory by the user's loginid).
X * This is neccessary because NFS doesn't support socket
X * operations, and many people's homes are on NFS mounted
X * partitions. Layers will create this directory if it needs
X * to.
X */
X#define SOCKDIR "/tmp/layers" /* NFS doesn't support named sockets */
X
X/*
X * End of User Configuration Section
X */
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 <sys/ioctl.h>
X
X#include "layers.h"
X
X#ifdef GETTTYENT
X#include <ttyent.h>
X#else
Xstatic struct ttyent
X { char *ty_name;
X } *getttyent();
Xstatic char *tt, *ttnext;
Xstatic char ttys[] = "/etc/ttys";
X#endif
X
X#ifndef FSCALE
X#define FSCALE 1000.0 /* Sequent doesn't define FSCALE...grrrr */
X#endif
X
X#ifdef USRLIMIT
Xstruct utmp utmpbuf;
Xint UserCount;
X#endif
X
X#define Ctrl(c) ((c)&037)
X
X/* C library items */
Xextern char **environ;
Xextern errno;
Xextern sys_nerr;
Xextern char *sys_errlist[];
Xextern char *index(), *rindex(), *malloc(), *getenv();
Xextern char *getlogin(), *ttyname();
X
X/* Local items */
Xstatic void FAbort(), SigHup(), SigChld(), AddCap(), FinitTerm();
Xstatic char *MakeTermcap(), *Filename(), **SaveArgs(), *GetTtyName();
Xstatic void InitWorld(), ClearShape(), BuildTitle(), KillPG();
Xstatic void SetWindowSize(), WriteUtmp();
Xstatic int ReadUtmp(), FindUtmp(), SetUtmp();
X
Xstatic int loginflag = -1;
Xstatic char PtyName[32], TtyName[32];
Xstatic char *ShellProg;
Xstatic char *ShellArgs[2];
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 struct stat RealTtyStat; /* Real tty stat */
Xstatic char RealTtyName[32] = ""; /* Real tty name */
Xstatic int RealSlot = 0; /* Real tty logon slot */
Xstatic struct utmp RealUtmp; /* Real tty logon utmp entry */
Xstatic int RealTtyMode = 0; /* Real tty mode */
Xstatic int Oumask; /* Original user's umask */
Xstatic char SockPath[512];
X#ifdef SOCKDIR
Xstatic char SockDir[] = SOCKDIR;
X#else
Xstatic char SockDir[] = ".layers";
X#endif
Xstatic char *SockNamePtr, *SockName;
Xstatic ServerSocket;
Xstatic char *NewEnv[MAXARGS];
Xstatic char Esc = Ctrl('a');
Xstatic char MetaEsc = 'a';
Xstatic char *home;
Xstatic Abortonmsg;
Xstatic utmp, utmpf;
Xstatic char UtmpName[] = "/etc/utmp";
Xstatic char *LoginName;
Xstatic mflag, nflag, fflag, rflag;
Xstatic char HostName[MAXSTR];
Xstatic char *myname;
Xstatic DevTty;
X
Xstatic struct 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
X#define MSG_CREATE 0
X#define MSG_ERROR 1
X
Xstruct msg
X {
X int type;
X union
X { struct
X { int lflag; /* login flag */
X struct Shape shape; /* window shape */
X int nargs;
X char line[MAXLINE];
X char dir[1024];
X } create;
X char message[MAXLINE];
X } m;
X };
X
X
X /* dynamic keyboard input buffer definition */
Xstruct Kbuff
X { struct Kbuff * next; /* next buffer in chain (or NULL) */
X int size; /* size of data in this buffer */
X int offset; /* start of first data in buffer */
X unsigned char text[IOSIZE]; /* text buffer itself */
X };
X
X /* World layer definition */
Xstruct Layer {
X int chan; /* channel represented by this layer */
X int allocated; /* layer allocated */
X int ptyfd; /* psuedo tty */
X int ptymask; /* mask for pty descriptor */
X int lpid; /* layer head process ID */
X int slot; /* utmp slot number */
X struct Kbuff *kbuff; /* keyboard input buffers */
X struct Shape shape; /* Shape structure to/from MacLayers */
X char cmd[MAXSTR]; /* command to execute */
X char tty[MAXSTR]; /* psuedo tty ID */
X };
X
Xstatic struct Layer World[MAXPCHAN]; /* all layer structures */
X
Xstatic int rows = 24; /* default window height in lines */
Xstatic int cols = 80; /* default window width in chars */
Xstatic char Termcap[1024];
Xstatic char Term[MAXSTR] = "TERM="; /* window's terminal type */
Xstatic char *UserTerm; /* terminal ID we are mimmicing */
Xstatic int flowctl;
Xstatic tcLineLen = 100;
X
X/* co#80 and li$24 added dynamically for proper window size */
Xstatic char TermcapConst1[] = "TERMCAP=SC|";
Xstatic char TermcapConst3[] = "|MacLayers virtual terminal|\\\n\
X :cr=^M:do=^J:nl=^J:bl=^G:cl=\\E[;H\\E[2J:\\\n\
X :le=^H:bs:am:cm=\\E[%i%d;%dH:nd=\\E[C:up=\\E[A:\\\n\
X :ce=\\E[K:cd=\\E[J:so=\\E[7m:se=\\E[m:us=\\E[4m:ue=\\E[m:\\\n\
X :md=\\E[1m:mr=\\E[7m:mb=\\E[5m:me=\\E[m:is=\\E[1;24r\\E[24;1H:\\\n\
X :rf=/usr/lib/tabset/vt100:\\\n\
X :rs=\\E>\\E[?3l\\E[?4l\\E[?5l\\E[?7h\\E[?8h:ks=\\E[?1h\\E=:ke=\\E[?1l\\E>:\\\n\
X :ku=\\EOA:kd=\\EOB:kr=\\EOC:kl=\\EOD:kb=^H:\\\n\
X :ho=\\E[H:k1=\\EOP:k2=\\EOQ:k3=\\EOR:k4=\\EOS:ta=^I:pt:sr=\\EM:vt#3:xn:\\\n\
X :sc=\\E7:rc=\\E8:cs=\\E[%i%d;%dr:\\\n\
X :dc=\\ED:ic=\\EI:";
X/* NOTE: the above two cababilities are beyond vt100 - unique to MacLayers */
X
Xint Dflag; /* debug dump flag */
X
X /* main() */
Xmain(ac, av)
Xchar **av;
X{
X register n;
X register len;
X register struct Layer *layer;
X char *ap;
X struct passwd *ppp;
X int s;
X int r; /* read fd test bits */
X int w; /* write fd test bits */
X int stall; /* stall flag for priority channel */
X int fderr; /* error fd test bits */
X struct timeval tv;
X struct Shape shape; /* window shape */
X time_t now;
X char buf[IOSIZE];
X char rc[256];
X struct stat st;
X struct Kbuff *kbptr; /* input keyboard buffer pointer */
X
X Abortonmsg = 1; /* quit if message generated */
X ClearShape(&shape); /* initialize shape structure */
X myname = (ac == 0) ? "layers" : av[0];
X InitWorld(); /* clear World array structures */
X
X while (ac > 0)
X { ap = *++av;
X if (--ac > 0 && *ap == '-')
X { switch (ap[1])
X { case 'l': /* login this command line */
X loginflag = 1;
X break;
X
X case 'd': /* dump debugging flag */
X Dflag = 1;
X (void) freopen("layers.dump", "a", stderr); /* append mode */
X#ifdef SEQUENT
X setlinebuf(stderr);
X#else
X setvbuf(stderr, NULL, _IOLBF, 0);
X#endif
X break;
X
X case 'u': /* do not login this command line */
X loginflag = 0;
X break;
X
X case 'm': /* force this to be master and not a client */
X mflag = 1;
X break;
X
X case 'n': /* no flow control */
X nflag = 1;
X break;
X
X case 'f': /* flow control on */
X fflag = 1;
X break;
X
X case 'v': /* do nothing but issue layers version */
X printf("%s\n", LayersVersion);
X exit(0);
X
X default:
X help:
X Msg (0,"Use: %s [-f] [-l | -u] [-m] [-n] [cmd args]\n", myname);
X
X } /* end switch on '-' option */
X
X } /* end if '-' */
X
X else
X break;
X
X } /* end while parameters */
X
X if (nflag && fflag)
X Msg (0, "-f and -n are conflicting options.");
X
X if ((ShellProg = getenv ("SHELL")) == 0)
X ShellProg = DefaultShell;
X DO DEBUG("ShellProg %s\n", ShellProg);
X
X /* we mimmic the user's $TERM ID */
X if ((UserTerm = getenv("TERM")) == 0)
X UserTerm = "layers"; /* use "layers" if none */
X (void) strcat(Term, UserTerm);
X DO DEBUG("%s\n", Term);
X
X ShellArgs[0] = ShellProg;
X if (ac == 0)
X { ac = 1;
X av = ShellArgs;
X shape.wattr |= Wa_shell; /* indicate a shell window */
X }
X
X if ((home = getenv ("HOME")) == 0)
X Msg (0, "$HOME is undefined.");
X DO DEBUG("home %s\n", home);
X
X if ((LoginName = getlogin ()) == 0 || LoginName[0] == '\0')
X { if ((ppp = getpwuid (getuid ())) == 0)
X return;
X LoginName = ppp->pw_name;
X }
X DO DEBUG("LoginName %s\n", LoginName);
X
X if ((Oumask=umask(0)) == -1)
X Msg (errno, "Cannot change umask to zero");
X DO DEBUG("Original umask o%o\n", Oumask);
X
X#ifdef SOCKDIR
X if (stat (SOCKDIR, &st) == -1)
X { if (errno == ENOENT)
X { if (mkdir (SOCKDIR, 0777) == -1)
X Msg (errno, "Cannot make directory %s", SOCKDIR);
X (void) chown (SOCKDIR, 0, 0);
X }
X else
X Msg (errno, "Cannot get status of %s", SOCKDIR);
X }
X else
X { if ((st.st_mode & S_IFMT) != S_IFDIR)
X Msg (0, "%s is not a directory.", SOCKDIR);
X if ((st.st_mode & 0777) != 0777)
X Msg (0, "Directory %s must have mode 777.", SOCKDIR);
X }
X sprintf (SockPath, "%s/%s", SockDir, LoginName);
X#else
X sprintf (SockPath, "%s/%s", home, SockDir);
X#endif
X DO DEBUG("SockPath %s\n", SockPath);
X
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 DO DEBUG("SockPath directory made\n");
X }
X else
X Msg (errno, "Cannot get status of %s", SockPath);
X }
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
X (void) strcpy(RealTtyName, GetTtyName()); /* real tty name */
X if (stat(RealTtyName, &RealTtyStat) == -1) /* get current mode */
X Msg(errno, "Cannot get status of %s", RealTtyName);
X DO DEBUG("Mode of %s is %#o\n", RealTtyName, RealTtyStat.st_mode);
X RealTtyMode = RealTtyStat.st_mode; /* save mode for later restore */
X
X (void) gethostname (HostName, MAXSTR);
X HostName[MAXSTR-1] = '\0';
X DO DEBUG("HostName %s\n", HostName);
X
X if (ap = index (HostName, '.'))
X *ap = '\0';
X if (ap)
X DO DEBUG("*ap %s\n", *ap);
X
X strcat (SockPath, "/");
X SockNamePtr = SockPath + strlen (SockPath);
X
X /* if we are a client send create message to startup window and exit */
X if (GetSockName ())
X { DO DEBUG("GetSockName says that we are client\n");
X DO DEBUG("SockName '%s'\n", SockName);
X s = MakeClientSocket (1);
X DO DEBUG("Client socket is %d\n", s);
X DO DEBUG("SendCreateMsg()\n");
X SendCreateMsg (s, ac, av, loginflag, &shape);
X close (s);
X DO DEBUG("after SendCreateMsg(), now exit(0)\n");
X exit (0);
X }
X
X /* we are the server */
X DO DEBUG("SockName '%s'\n", SockName);
X DO DEBUG("We are server\n");
X if ((DevTty = open ("/dev/tty", O_RDWR|O_NDELAY)) == -1)
X Msg (errno, "/dev/tty");
X DO DEBUG("opened /dev/tty fd %d\n", DevTty);
X
X ServerSocket = MakeServerSocket ();
X DO DEBUG("ServerSocket %d\n", ServerSocket);
X s = ServerSocket;
X
X if (fflag)
X flowctl = 1;
X else
X if (nflag)
X flowctl = 0;
X
X if (loginflag == -1)
X loginflag = LOGINDEFAULT;
X
X MakeNewEnv ();
X InitUtmp ();
X RealSlot = FindUtmp(RealTtyName); /* find current logon slot */
X if (RealSlot)
X { if (ReadUtmp(RealSlot, &RealUtmp) > 0) /* read real login utmp */
X RemoveUtmp(RealSlot); /* remove original logon slot */
X else
X RealSlot = 0; /* something's wrong */
X }
X
X signal (SIGHUP, SigHup);
X signal (SIGINT, SIG_IGN); /* we should never see this */
X signal (SIGQUIT, FAbort); /* quit layers on these 2 signals */
X signal (SIGTERM, FAbort);
X signal (SIGTTIN, SIG_IGN);
X signal (SIGTTOU, SIG_IGN);
X signal (SIGALRM, SIG_IGN); /* alarm clock used by protocol.c */
X
X GetTTY (0, &OldMode);
X SetMode (&OldMode, &NewMode);
X SetTTY (0, &NewMode);
X
X if (Initlink() == 0)
X { SetTTY(0, &OldMode); /* revert tty back */
X Msg (0, "\n\n You are not running under MacLayers.");
X }
X
X sprintf (rc, "%.*s/.layersrc", 245, home);
X#if 0 /* NOT YET SUPPORTED */
X /* if no window list start up a default shell window */
X if (ReadRc(rc) == 0)
X#endif
X { n = MakeWindow (0, *av, av, (char *)0, loginflag, &shape);
X if (n == -1)
X { SetTTY (0, &OldMode);
X FQuit(1);
X /* NOT REACHED */
X }
X }
X
X (void) chmod(RealTtyName, 0600); /* lock out broadcasts */
X Abortonmsg = 0; /* don't abort on msg from now on */
X signal (SIGCHLD, SigChld);
X tv.tv_usec = 0;
X tv.tv_sec = 0; /* default no timer wanted */
X
X /* client/Maclayers processing loop */
X
X /* poll .20 secs for new startups */
X#define WUSSTARTUP 200000
X /* stall nonpriority windows .5 seconds when top window I/O active */
X#define WUSSTALL 500000
X
X stall = 0; /* startout no stalled channels */
X fderr = 0; /* startout no error fd's */
X
X while (1)
X { int priochan; /* the top window channel */
X
X priochan = TopChannel(); /* find highest priority channel */
X
X /* check for I/O on all available I/O descriptors */
X r = 1<<0; /* always read MacLayers stream */
X r |= 1<<s; /* always read server socket */
X w = 0; /* initalize to no write tests */
X tv.tv_usec = 0; /* default no startup poll */
X
X /* for all active layers set read and write bits as appropriate */
X if (stall)
X stall = 1; /* start counting output layers */
X for (n=0; n<MAXPCHAN; n++)
X if ((layer = &World[n])->allocated) /* if layer exists ... */
X { /* if not yet started or has just terminated ... */
X if (layer->ptymask & fderr)
X tv.tv_usec = WUSSTARTUP; /* don't spinloop but wait-a-bit */
X else
X { if (layer->kbuff && layer->kbuff->size)
X /* keyboard input for layer */
X w |= layer->ptymask; /* try write to it */
X /* read layer output unless we're being nice to top window */
X if (!stall)
X r |= layer->ptymask; /* read output from layer */
X else
X if (layer->chan == priochan)
X r |= layer->ptymask; /* read top priority output */
X else
X stall++; /* indicate something to stall */
X }
X }
X
X if (stall > 1)
X if (!tv.tv_usec)
X tv.tv_usec = WUSSTALL; /* set stall timout */
X
X /* process signals before trying select */
X if (GotSignal)
X { SigHandler ();
X continue;
X }
X
X DO DEBUG("Select(r %x, w %x, fderr %x, stall %d, prio %d, us %d)\n",
X r, w, fderr, stall, priochan, tv.tv_usec);
X
X switch ( select(32, &r, &w, NULL, tv.tv_usec ? &tv : NULL) )
X { case -1:
X /* errno has report */
X if (errno == EINTR) /* signal delivered or timout */
X { errno = 0;
X tv.tv_usec = 0; /* clear timer wait value */
X fderr = 0; /* turn off error stall */
X stall = 0; /* turn off output priority stall */
X DO DEBUG("select errno EINTR\n");
X continue; /* re-loop */
X }
X Abortonmsg = 1;
X DO DEBUG("select errno %d\n", errno);
X Msg (errno, "select");
X /*NOTREACHED*/
X
X case 0:
X /* timeout reached */
X tv.tv_usec = 0; /* clear timer wait value */
X stall = 0; /* turn off stall */
X fderr = 0; /* turn off error stall */
X continue; /* re-loop */
X
X default:
X /* a channel has read/write status pending */
X break;
X }
X
X DO DEBUG("after select r %x w %x\n", r, w);
X
X /* handle any signal arriving up during select wait */
X if (GotSignal)
X { SigHandler ();
X continue;
X }
X
X /* if server socket has command process that now */
X if (r & 1 << s)
X { ReceiveMsg(s); /* process client control packet */
X continue; /* status may have changed */
X }
X
X /* next process input stream from MacLayers */
X if (r & 1 << 0)
X { ProcessStreamin(); /* key input and control packets */
X continue; /* status may have changed */
X }
X
X /* process keyboard input first so output doesn't hold up
X ** keyboard echo and break/interrupt processing
X */
X priochan = TopChannel(); /* find top priority channel */
X stall = 0; /* assume no stall needed */
X for (n=0; n<MAXPCHAN; n++)
X if ((layer = &World[n])->allocated)
X if (w & layer->ptymask)
X while ((kbptr=layer->kbuff)->size)
X { /* pass as much keyboard as possible */
X if (layer->chan == priochan)
X stall = 1; /* stall lower priority channels */
X len = write(layer->ptyfd, &kbptr->text[kbptr->offset],
X kbptr->size);
X DO DEBUG("keyin len %d to chan %d\n", len, layer->chan);
X if (len <= 0) /* if no data accepted ... */
X { if (errno == EIO) /* if I/O error indicated ... */
X fderr |= layer->ptymask; /* wait-a-bit on this */
X errno = 0; /* clear errno */
X break; /* try again later */
X }
X /* some of buffer accepted */
X kbptr->size -= len; /* length processed */
X kbptr->offset += len; /* bump up offset */
X if (kbptr->size > 0) /* not all buffer accepted ... */
X break; /* try feed again later */
X /* see if another buffer chained */
X if (kbptr->next)
X { /* yes, free up current buffer and queue next */
X layer->kbuff = kbptr->next; /* to next buffer */
X free(kbptr); /* free this buffer up */
X }
X else
X { /* otherwise leave this for next input */
X kbptr->size = 0; /* empty buffer */
X kbptr->offset = 0; /* refill from the start */
X }
X }
X
X /* first process the highest priority channel (top window) */
X if (priochan > 0 && priochan <= MAXPCHAN) /* if valid ... */
X if ((layer = &World[priochan-1])->allocated)
X if (r & layer->ptymask)
X { /* output to send to top MacLayers window */
X len = read(layer->ptyfd, buf, IOSIZE);
X if (len >= 0) /* if no error ... */
X { DO DEBUG("read output len %d chan %d\n", len, layer->chan);
X }
X else
X { /* We expect EIO error if socket not yet open on other end
X ** or if process using socket has terminated. We expect
X ** EWOULDBLOCK also after process terminates.
X **/
X DO DEBUG("read output err chan %d errno %d len %d\n",
X layer->chan, errno, len);
X if (errno == EIO || errno == EWOULDBLOCK)
X DO DEBUG(" ...anticipated\n");
X /* layer not ready or just terminated so wait-a-bit */
X fderr |= layer->ptymask;
X r &= ~layer->ptymask; /* don't read it again below */
X errno = 0; /* clear errno */
X }
X if (len > 0)
X SendData(layer->chan, buf, len);
X
X if (len >= 0)
X /* To keep lower priority channels from hogging the line
X ** we delay any output from them until the primary window
X ** has no more data to be sent to it.
X */
X stall = 1; /* stall output from others */
X }
X
X /* now pass all available output to MacLayers */
X if (!stall)
X for (n=0; n<MAXPCHAN; n++)
X if ((layer = &World[n])->allocated)
X if (r & layer->ptymask)
X { /* output to send to MacLayers window */
X len = read(layer->ptyfd, buf, IOSIZE);
X if (len >= 0) /* if no error ... */
X { DO DEBUG("output chan %d len %d\n", layer->chan, len);
X }
X else
X { /* We expect EIO error if socket not yet open on other end
X ** or if process using socket has terminated. We expect
X ** EWOULDBLOCK also after process terminates.
X **/
X DO DEBUG("read output err chan %d errno %d len %d\n",
X layer->chan, errno, len);
X if (errno == EIO || errno == EWOULDBLOCK)
X { DO DEBUG(" ...anticipated\n");
X }
X /* layer not ready or just terminated so wait-a-bit */
X fderr |= layer->ptymask;
X errno = 0; /* clear errno */
X }
X if (len > 0)
X SendData(layer->chan, buf, len);
X }
X
X /* handle signals again */
X if (GotSignal)
X SigHandler ();
X
X } /* end while (1) */
X
X /* NOT REACHED */
X
X} /* main() */
X
X /* ReceiveQuit() - MacLayers sends Quit packet */
X
Xvoid
XReceiveQuit()
X{
X /* We completely quit layers cancelling all active processes */
X DO DEBUG("ReceiveQuit()\n");
X FQuit(0); /* normal termination */
X /* NOT REACHED */
X
X} /* ReceiveQuit() */
X
X
X /* ReceiveNew() - MacLayers requests a new shell layer */
X
Xvoid
XReceiveNew(chanid, shape)
Xint chanid; /* channel for new shell layer */
Xstruct Shape *shape; /* shape for new channel */
X{
X DO DEBUG("ReceiveNew(%d)\n", chanid);
X (void) MakeWindow (chanid, *ShellArgs, ShellArgs,
X (char *) 0, loginflag, shape);
X
X} /* ReceiveNew() */
X
X
X /* ReceiveDelete() - MacLayers has removed a layer */
Xvoid
XReceiveDelete(chanid)
Xint chanid; /* channel which was deleted */
X{
X struct Layer *layer; /* layer pointer */
X
X /* validate channel */
X DO DEBUG("ReceiveDelete(%d)\n", chanid);
X if (chanid <= 0 || chanid > MAXPCHAN)
X return; /* ignore invalid channel */
X
X /* if this layer active then kill it off, else ignore request */
X layer = &World[chanid-1]; /* locate target layer */
X if (layer->allocated)
X KillWindow(layer);
X
X} /* ReceiveDelete() */
X
X
X /* ReceiveSignal() - send signal to layer's process group */
X
Xvoid
XReceiveSignal(chanid, signal)
Xint chanid; /* layer's channel */
Xint signal; /* signal.h signal ID */
X{
X struct Layer *layer; /* layer pointer */
X
X DO DEBUG("ReceiveSignal(%d,%d)\n", chanid, signal);
X /* verify channel */
X if (chanid <= 0 || chanid > MAXPCHAN)
X return; /* ignore invalid channel */
X
X /* if this layer is active send the signal to the process group */
X layer = &World[chanid-1]; /* locate target layer */
X if (layer->allocated && layer->lpid)
X KillPG(layer, signal);
X
X} /* ReceiveSignal() */
X
X
X /* ReceiveReshape() - windowsize and location updated */
Xvoid
XReceiveReshape(chanid, shape)
Xint chanid; /* channel having shape */
Xstruct Shape *shape; /* shape structure */
X{
X struct Layer *layer; /* layer pointer */
X
X DO DEBUG("ReceiveReshape(%d)\n", chanid);
X
X /* verify channel */
X if (chanid <= 0 || chanid > MAXPCHAN)
X return; /* ignore invalid channel */
X
X /* if this layer is active then reshape it's window */
X layer = &World[chanid-1]; /* locate target layer */
X if (layer->allocated && layer->lpid)
X { layer->shape = *shape; /* install as our new shape */
X SetWindowSize(layer); /* udpate the O/S window info */
X }
X
X} /* ReceiveReshape() */
X
X
X /* SetWindowSize() - tell O/S about new window size */
X
Xstatic void
XSetWindowSize(layer)
Xstruct Layer *layer; /* layer to resize */
X{
X#ifdef TIOCSWINSZ
X struct winsize wsize; /* window size structure */
X int retcode; /* ioctl return code */
X
X wsize.ws_col = layer->shape.wchars; /* character width */
X wsize.ws_row = layer->shape.wlines; /* window height */
X wsize.ws_xpixel = 0; /* necessary? */
X wsize.ws_ypixel = 0;
X /* update O/S window state */
X retcode = ioctl(layer->ptyfd, TIOCSWINSZ, &wsize);
X DO DEBUG("SetWindowSize(chan %d) col %d, row %d iotcl() = %d\n",
X layer->chan, layer->shape.wchars, layer->shape.wlines,
X retcode);
X
X retcode = ioctl(layer->ptyfd, TIOCGWINSZ, &wsize);
X DO DEBUG("TIOCGWINSZ: col %d, row %d iotcl() = %d\n",
X wsize.ws_col, wsize.ws_row, retcode);
X
X#endif
X} /* SetWindowSize() */
X
X
X /* ReceiveData() - received keyboard input for layer */
Xvoid
XReceiveData(chanid, buff, cnt)
Xint chanid; /* channel receiving input */
Xchar *buff; /* buffer containing data */
Xint cnt; /* count of data bytes */
X{
X struct Layer *layer; /* layer pointer */
X struct Kbuff *kb; /* keybuff pointer */
X
X DO DEBUG("ReceiveData(%d, '%.*s')\n", chanid, cnt, buff);
X /* verify channel */
X if (chanid <= 0 || chanid > MAXPCHAN)
X return; /* ignore invalid channel */
X layer = &World[chanid-1]; /* locate target layer */
X
X /* add character stream to layer's input buffers for main loop processing */
X for (kb=layer->kbuff; kb->next; kb=kb->next); /* find oldest buffer */
X while (cnt--)
X {
X /* if current buffer full then chain in a new one */
X if (kb->offset+kb->size >= IOSIZE)
X { kb->next = (struct Kbuff *) malloc(sizeof(struct Kbuff));
X kb = kb->next; /* base new keybuff */
X kb->next = NULL; /* no next yet */
X kb->size = 0; /* this character is first */
X kb->offset = 0; /* at zero offset */
X }
X
X /* add new character to the end of this buffer */
X kb->text[kb->offset+kb->size++] = *buff++; /* insert at end of data */
X }
X
X} /* ReceiveData() */
X
X
X
X /* InitWorld() - initialize layer structures */
X
Xstatic void
XInitWorld()
X{
X struct Layer *layer; /* layer pointer */
X struct Kbuff *kb; /* keybuff pointer */
X int i; /* work variable */
X
X for (i=0; i<MAXPCHAN; i++)
X { layer = &World[i]; /* point to layer */
X layer->chan = i+1; /* channel ID */
X layer->allocated = 0; /* does not exist yet */
X layer->lpid = 0; /* head process */
X layer->ptyfd = 0; /* no pseduo pty yet */
X layer->ptymask = 0; /* no pty mask yet */
X layer->slot = 0; /* no Utmp slot */
X ClearShape(&layer->shape); /* clear shape structure */
X
X /* allocate the primary input keybuffer for this layer */
X layer->kbuff = (struct Kbuff *) malloc(sizeof(struct Kbuff));
X layer->kbuff->next = NULL; /* no next buffer */
X layer->kbuff->size = 0; /* no data in buffer */
X layer->kbuff->offset = 0; /* start filling at front */
X
X } /* end for layer scan */
X
X} /* InitWorld() */
X
X
X /* clearshape() - initialize shape structure */
X
Xstatic void
XClearShape(shape)
Xstruct Shape *shape; /* shape structure pointer */
X{
X shape->worigv = 0; /* default window position */
X shape->worigh = 0;
X shape->wlines = 0; /* default size */
X shape->wchars = 0;
X shape->wfont = 0; /* default font size */
X shape->wattr = 0; /* no attributes */
X
X} /* clearshape() */
X
X
X /* SigHandler() - process signals */
X
XSigHandler ()
X{
X DO DEBUG("GotSignal()\n");
X while (GotSignal)
X { GotSignal = 0;
X DoWait (); /* handle dead or stopped children processes */
X }
X}
X
Xstatic void
XSigChld ()
X{
X DO DEBUG("SigChld()\n");
X /* flag child process is stopped or dead */
X GotSignal = 1;
X}
X
Xstatic void
XSigHup ()
X{
X DO DEBUG("SigHup()\n");
X /* Detach (0); */
X FQuit(1); /* stop all processes */
X /* NOT REACHED */
X
X}
X
X /* DoWait() - send SIGCONT to stopped windows, Free dead process windows */
Xstatic
XDoWait()
X{
X register pid;
X register struct Layer *layer;
X union wait wstat;
X int i;
X
X DO DEBUG("DoWait()\n");
X while ((pid = wait3 (&wstat, WNOHANG|WUNTRACED, NULL)) > 0)
X /* dead or stopped child process found */
X for (i=0; i<MAXPCHAN; i++)
X if ((layer = &World[i])->lpid == pid)
X { if (WIFSTOPPED (wstat))
X { /* stopped process so restart it */
X /*** DO WE REALLY NEED TO DO THIS? ***/
X DO DEBUG("killpg(, SIGCONT)\n");
X KillPG(layer, SIGCONT);
X }
X else
X { /* remove dead process's layer */
X DO DEBUG("kill dead process window %d\n", layer->chan);
X KillWindow (layer);
X /* tell MacLayers layer is dead */
X SendDelete(layer->chan);
X }
X }
X
X} /* DoWait() */
X
X
X /* KillPG() - send signal to layer's process group */
X
Xstatic void
XKillPG(layer, signal)
Xstruct Layer *layer; /* layer to signal */
Xint signal; /* signal to send */
X{
X int retcode; /* work variable */
X int pgrp; /* process group for layer */
X int tgrp; /* terminal control process group */
X
X DO DEBUG("KillPG(%d, sig %d)\n", layer->chan, signal);
X
X if (layer->lpid)
X { pgrp = getpgrp(layer->lpid); /* get process group */
X DO DEBUG("getpgrp() = %d\n", pgrp);
X if (pgrp != -1)
X { retcode = killpg(pgrp, signal); /* signal it */
X DO DEBUG("killpg() = %d\n", retcode);
X }
X
X /* After a lot of experimenting it was determined that csh
X ** creates a new terminal control group when it runs a command.
X ** Thus the above code failed to forward SIGINT to such commands.
X ** (Csh would get it but just ignore it.) Thus the following code.
X */
X
X /* forward SIGINT to the terminal control group also */
X if (signal == SIGINT)
X { retcode = ioctl(layer->ptyfd, TIOCGPGRP, &tgrp);
X DO DEBUG("ioctl(ptyfd,TIOCGPGRP) termcntlgrp %d, retcode = %d\n",
X tgrp, retcode);
X /* but only if not the same process group */
X if (retcode != -1 && tgrp != pgrp)
X { retcode = killpg(tgrp, signal); /* signal it */
X DO DEBUG("killpg(%d) = %d\n", tgrp, retcode);
X }
X }
X }
X
X} /* KillPG() */
X
X
X /* KillWindow() - remove a layer from the system */
X
X/* Note: This function does NOT tell MacLayers about the dead layer */
X
Xstatic
XKillWindow (layer)
Xstruct Layer *layer;
X{
X struct Kbuff *kb; /* work buffer free pointer */
X
X if (layer->allocated)
X { /* SHOULD THIS BE SIGKILL ??? */
X if (layer->lpid) /* if layer process started ... */
X { KillPG(layer, SIGHUP); /* kill processes */
X layer->lpid = 0; /* clear pid field */
X }
X RemoveUtmp(layer->slot);
X (void) chmod(layer->tty, 0666);
X (void) chown(layer->tty, 0, 0);
X close(layer->ptyfd);
X DO DEBUG("chmod/chown %s, SendDelete(%d)\n",layer->tty, layer->chan);
X
X ClearShape(&layer->shape); /* reset the shape structure */
X /* free all keybuffers but one and reprime it */
X for (kb=layer->kbuff; kb->next; kb=kb->next)
X free(kb); /* free input buffers */
X kb->size = 0; /* empty buffer */
X kb->offset = 0; /* start refill from front */
X layer->allocated = 0; /* window no longer allocated */
X }
X
X} /* KillWindow() */
X
X
X /* FAbort() - signal catcher for quitting */
Xstatic void
XFAbort()
X{
X DO DEBUG("FAbort()\n");
X FQuit (1); /* quit with error exit */
X
X} /* FAbort() */
X
X
X /* FQuit() - terminate layers */
Xvoid
XFQuit(exitcode)
Xint exitcode;
X{
X int i;
X
X DO DEBUG("FQuit(%d)\n",exitcode);
X for (i=0; i<MAXPCHAN; i++)
X KillWindow(&World[i]); /* kill all windows */
X
X DO DEBUG("SendQuit()\n");
X SendQuit(); /* tell MacLayers to exit layers mode */
X SetTTY (0, &OldMode);
X if (RealTtyMode)
X (void) chmod(RealTtyName, RealTtyMode); /* restore mode */
X if (RealSlot)
X WriteUtmp(RealSlot, &RealUtmp); /* restore original login */
X FinitTerm ();
X sleep(2); /* wait for port to reset */
X printf ("[layers terminated]\n");
X
X exit (exitcode);
X
X} /* FQuit() */
X
X
X /* MakeWindow() - create new layer */
Xstatic
XMakeWindow (chan, prog, args, dir, lflag, shape)
Xint chan; /* zero or channel to use for window */
Xchar *prog;
Xchar **args;
Xchar *dir;
Xint lflag; /* one if this to be logged in */
Xstruct Shape *shape; /* shape to use for window */
X{
X register struct Layer *layer;
X register char **cp;
X register f, j;
X int tf;
X int mypid;
X char ebuf[10];
X
X DO DEBUG("MakeWindow(%d, %s, %s, dir %s, ",
X chan, prog, args[0], dir ? dir : "(none)");
X DO DEBUG("login %d\n", lflag);
X DO DEBUG(" origv %d, origh %d, lines %d, chars %d, ",
X shape->worigv, shape->worigh, shape->wlines, shape->wchars);
X DO DEBUG("font %d, attr 0x%x\n", shape->wfont, shape->wattr);
X
X if ((f = OpenPTY ()) == -1)
X { Msg (0, "No more PTYs.");
X return ( -1 );
X }
X
X /* if channel not given obtain one from MacLayers */
X if (chan == 0)
X { chan = SendNew(shape); /* try to get free window */
X if (chan == 0)
X { Msg (0, "No more windows.");
X return ( -1 );
X }
X DO DEBUG("SendNew() == %d\n", chan);
X }
X
X /* verify channel */
X if (chan <= 0 || chan > MAXPCHAN)
X { Msg(0, "Invalid channel %d.", chan);
X return ( -1 );
X }
X
X /* login this window if it's layer #1 */
X if (chan == 1)
X lflag = 1;
X
X if (lflag == -1)
X lflag = loginflag;
X
X#ifdef USRLIMIT
X /*
X * Count current number of users, and if logging windows in,
X */
X if (lflag == 1)
X { (void) lseek (utmpf, 0, 0);
X UserCount = 0;
X while (read(utmpf, &utmpbuf, sizeof(struct utmp)) > 0)
X { if (utmpbuf.ut_name[0] != '\0')
X UserCount++;
X }
X if (UserCount >= USRLIMIT)
X { Msg (0, "User limit reached. Window will not be logged in.");
X lflag = 0;
X }
X }
X#endif USRLIMIT
X
X layer = &World[chan-1]; /* find layer structure */
X layer->shape = *shape; /* install new window shape */
X
X /* ??? What do we do if layer is still active as far as we're concerned? */
X if (layer->allocated)
X { DO DEBUG("??? newlayer not free !!!\n");
X KillWindow(layer); /* kill off old layer */
X SendDelete(layer->chan); /* kill window back off */
X Msg (0, "Makewindow error: Duplicate active layer %d.", chan);
X return ( -1 ); /* return failed */
X }
X
X layer->allocated = 1; /* show layer now in use */
X BuildTitle(chan, prog, args); /* install window title */
X
X (void) fcntl (f, F_SETFL, FNDELAY);
X layer->ptyfd = f; /* pseudo pty for task's I/O */
X layer->ptymask = 1<<f; /* set pty device mask */
X strncpy (layer->cmd, Filename (args[0]), MAXSTR-1);
X layer->cmd[MAXSTR-1] = '\0';
X strncpy (layer->tty, TtyName, MAXSTR-1);
X DO DEBUG("forking %s, tty %s, ptyfd %d, mask %x\n",
X layer->cmd, layer->tty, layer->ptyfd, layer->ptymask);
X (void) chown (TtyName, getuid (), getgid ());
X if (lflag == 1)
X { layer->slot = SetUtmp(TtyName, chan == 1);
X if (chan == 1 && RealTtyMode)
X /* set to original tty umask */
X (void) chmod(TtyName, RealTtyMode);
X else
X /* force to this mode */
X (void) chmod(TtyName, TtyMode);
X }
X else
X { layer->slot = -1;
X /* do not allow any other user access to this device */
X (void) chmod (TtyName, 0600);
X }
X switch (layer->lpid = fork ())
X { case -1:
X Msg (errno, "fork");
X layer->lpid = 0; /* clear pid field */
X return ( -1 ); /* return failed */
X
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 signal (SIGALRM, 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 DO DEBUG("Now in new process\n");
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
X { struct winsize wsize; /* window size structure */
X int retcode; /* ioctl return code */
X
X wsize.ws_col = layer->shape.wchars; /* character width */
X wsize.ws_row = layer->shape.wlines; /* window height */
X wsize.ws_xpixel = 0; /* necessary? */
X wsize.ws_ypixel = 0;
X /* update O/S window state */
X retcode = ioctl(0, TIOCSWINSZ, &wsize);
X }
X (void) umask(Oumask); /* restore user's original umask */
X NewEnv[2] = MakeTermcap(layer->shape.wlines, layer->shape.wchars);
X sprintf (ebuf, "LAYER=%d", chan);
X NewEnv[3] = ebuf;
X execvpe (prog, args, NewEnv);
X printf("%s: cannot exec %s: %s", myname, prog, sys_errlist[errno]);
X exit (1);
X }
X
X return ( chan );
X
X} /* MakeWindow() */
X
Xstatic
Xexecvpe (prog, args, env)
Xchar *prog, **args, **env;
X{
X register char *path, *p;
X char buf[1024];
X char *shargs[MAXARGS+1];
X register i;
X register eaccess = 0;
X
X if (prog[0] == '/')
X path = "";
X else
X 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 execve (DefaultShell, shargs, env);
X return;
X
X case EACCES:
X eaccess = 1;
X break;
X
X case ENOMEM: case E2BIG: case ETXTBSY:
X return;
X
X } /* end switch */
X
X } while (*path);
X
X if (eaccess)
X errno = EACCES;
X
X} /* execvpe() */
X
X
X /* BuildTitle() - create and install window title */
X
Xstatic void
XBuildTitle(chan, prog, args)
Xint chan; /* channel for title */
Xchar *prog; /* program being executed */
Xchar **args; /* arg list */
X{
X int i; /* arg scan index */
X char buff[1024]; /* super huge title buffer */
X
X /* skip any leading "/bin/" */
X if (strncmp(prog, "/bin/", 5) == 0)
X strcpy(buff, prog+5); /* leave /bin off */
X else
X strcpy(buff, prog); /* start with program name */
X
X /* add all aguments but stop if option ("-") seen */
X for (i=1; args[i] && args[i][0] != '-'; i++)
X { strcat(buff, " "); /* delimiter */
X strcat(buff, args[i]); /* add next parameter */
X }
X
X SendTitle(chan, buff, strlen(buff)); /* set new window title */
X
X} /* BuildTitle() */
X
X
X#ifdef sequent
Xstatic
XOpenPTY ()
X{
X char *m, *s;
X register f;
X
X f = getpseudotty (&s, &m);
X strncpy (PtyName, m, sizeof (PtyName));
X strncpy (TtyName, s, sizeof (TtyName));
X ioctl (f, TIOCFLUSH, (char *)0);
X return (f);
X}
X
X#else
X
Xstatic
XOpenPTY ()
X{
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
X return -1;
X
X} /* OpenPTY() */
X#endif
X
Xstatic
XSetTTY (fd, mp)
Xstruct mode *mp;
X{
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} /* SetTTY() */
X
Xstatic
XGetTTY (fd, mp)
Xstruct mode *mp;
X{
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} /* GetTTY() */
X
Xstatic
XSetMode (op, np)
Xstruct mode *op, *np;
X{
X *np = *op;
X#if 1
X if (flowctl)
X { np->m_ttyb.sg_flags &= ~(CRMOD|ECHO);
X np->m_ttyb.sg_flags |= CBREAK | ANYP;
X }
X else
X np->m_ttyb.sg_flags = RAW | ANYP;
X#else
X np->m_ttyb.sg_flags &= ~(CRMOD|ECHO);
X np->m_ttyb.sg_flags |= CBREAK | ANYP;
X#endif
X np->m_tchars.t_intrc = -1;
X np->m_tchars.t_quitc = -1;
X if (!flowctl)
X { np->m_tchars.t_startc = -1;
X np->m_tchars.t_stopc = -1;
X }
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} /* SetMode() */
X
Xstatic char *
XGetTtyName ()
X{
X int n;
X char *p;
X
X for (p = 0, n = 0; n <= 2 && !(p = ttyname (n)); n++);
X
X if (!p || *p == '\0')
X Msg (0, "layers must run on a tty.");
X
X return ( p );
X
X} /* GetTtyName() */
X
X
Xstatic
XKill (pid, sig)
X{
X if (pid != 0)
X (void) kill (pid, sig);
X}
X
X /* GetSockName() - set SockName; if LTY env return 1 else 0 */
Xstatic
XGetSockName ()
X{
X register client;
X static char buf[2*MAXSTR];
X
X if (!mflag && (SockName = getenv ("LTY")) != 0 && *SockName != '\0')
X { client = 1;
X setuid (getuid ());
X setgid (getgid ());
X }
X else
X { sprintf (buf, "%s.%s", HostName, Filename (RealTtyName));
X SockName = buf;
X client = 0;
X }
X return client;
X
X} /* GetSockName() */
X
Xstatic
XMakeServerSocket ()
X{
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 layers running on %s.", p);
X /*NOTREACHED*/
X }
X DO DEBUG("MakeServerSocket: unlink(SockPath)/bind()/chown/listen\n");
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} /* MakeServerSocket() */
X
Xstatic
XMakeClientSocket (err)
X{
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} /* MakeClientSocket() */
X
Xstatic
XSendCreateMsg (s, ac, av, lflag, shape)
Xchar **av;
Xstruct Shape *shape;
X{
X struct msg m;
X register char *p;
X register len, n;
X char *pwd; /* PWD environment string */
X
X DO DEBUG("SendCreateMsg(%d, ac %d, lflag %d\n", s, ac, lflag);
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+MAXLINE)
X break;
X strcpy (p, *av);
X p += len;
X }
X DO DEBUG(" nargs %d, create line = '%s'\n", n, m.m.create.line);
X m.m.create.nargs = n;
X m.m.create.lflag = lflag;
X m.m.create.shape = *shape; /* pass window shape */
X
X /* Since Suns can hang up on getwd() [damn their stupid networking]
X ** we try to get the current working directory first from the PWD
X ** environment variable.
X */
X if ((pwd=getenv("PWD")) && strlen(pwd) < 1024)
X (void) strcpy(m.m.create.dir, pwd);
X else
X if (getwd (m.m.create.dir) == 0)
X { DO DEBUG("getwd() failed!!\n");
X Msg (0, "%s", m.m.create.dir);
X }
X DO DEBUG(" create.dir = '%s'\n", m.m.create.dir);
X
X if (write (s, (char *)&m, sizeof (m)) != sizeof (m))
X { DO DEBUG(" write failed!!\n");
X Msg (errno, "write");
X }
X DO DEBUG("SendCreateMsg() done\n");
X
X} /* SendCreateMsg() */
X
X/*VARARGS1*/
Xstatic
XSendErrorMsg (fmt, p1, p2, p3, p4, p5, p6)
Xchar *fmt;
X{
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
XReceiveMsg (s)
X{
X register ns;
X struct sockaddr_un a;
X int left, len = sizeof (a);
X struct msg m;
X char *p;
X
X DO DEBUG ("ReceiveMsg()\n");
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 DO DEBUG("MSG_CREATE:\n");
X ExecCreate (&m);
X break;
X
X case MSG_ERROR:
X DO DEBUG("MSG_ERROR:\n");
X Msg (0, "%s", m.m.message);
X break;
X
X default:
X Msg (0, "Invalid message (type %d).", m.type);
X
X } /* end switch */
X
X} /* ReceiveMsg() */
X
Xstatic
XExecCreate (mp)
Xstruct msg *mp;
X{
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 n = MakeWindow (0, mp->m.create.line, args, mp->m.create.dir,
X mp->m.create.lflag, &mp->m.create.shape);
X
X} /* ExecCreate() */
X
X#if 0
Xstatic
XReadRc (fn)
Xchar *fn;
X{
X FILE *f;
X register char *p, **pp, **ap;
X register argc, num, c;
X char buf[256];
X char *args[MAXARGS];
X int key;
X struct Shape shape; /* shape for new window */
X
X ClearShape(&shape); /* initialize shape */
X ap = args;
X if (access (fn, R_OK) == -1)
X return;
X if ((f = fopen (fn, "r")) == NULL)
X return;
X while (fgets (buf, 256, f) != NULL)
X { if (p = rindex (buf, '\n'))
X *p = '\0';
X if ((argc = Parse (fn, buf, ap)) == 0)
X continue;
X if (strcmp (ap[0], "escape") == 0)
X { p = ap[1];
X if (argc < 2 || strlen (p) != 2)
X Msg (0, "%s: two characters required after escape.", fn);
X Esc = *p++;
X MetaEsc = *p;
X ktab[Esc].type = KEY_OTHER;
X }
X else
X if (strcmp (ap[0], "login") == 0)
X { loginflag = 1;
X }
X else
X if (strcmp (ap[0], "unlogin") == 0)
X { loginflag = 0; }
X else
X if (strcmp (ap[0], "nologin") == 0)
X { loginflag = 0; }
X else
X if (strcmp (ap[0], "chdir") == 0)
X { p = argc < 2 ? home : ap[1];
X if (chdir (p) == -1)
X Msg (errno, "%s", p);
X }
X else
X if (strcmp (ap[0], "mode") == 0)
X { if (argc != 2)
X { Msg (0, "%s: mode: one argument required.", fn); }
X else
X if (!IsNum (ap[1], 7))
X { Msg (0, "%s: mode: octal number expected.", fn); }
X else
X (void) sscanf (ap[1], "%o", &TtyMode);
X }
X else
X if (strcmp (ap[0], "bell") == 0)
X { if (argc != 2)
X { Msg (0, "%s: bell: one argument required.", fn); }
X else
X { if ((BellString = malloc (strlen (ap[1]) + 1)) == 0)
X Msg (0, "Out of memory.");
X istrcpy (BellString, ap[1]);
X }
X }
X else
X if (strcmp (ap[0], "screen") == 0)
X { num = 0;
X if (argc > 1 && IsNum (ap[1], 10))
X { num = atoi (ap[1]);
X if (num < 0 || num > MAXWIN-1)
X Msg (0, "%s: illegal screen number %d.", fn, num);
X --argc; ++ap;
X }
X if (argc < 2)
X { ap[1] = ShellProg; argc = 2; }
X ap[argc] = 0;
X (void) MakeWindow (0, ap[1], ap+1, (char *)0, loginflag, &shape);
X }
X else
X if (strcmp (ap[0], "bind") == 0)
X { p = ap[1];
X if (argc < 2 || *p == '\0')
X Msg (0, "%s: key expected after bind.", fn);
X if (p[1] == '\0')
X { key = *p; }
X else
X if (p[0] == '^' && p[1] != '\0' && p[2] == '\0')
X { c = p[1];
X if (isupper (c))
X p[1] = tolower (c);
X key = Ctrl(c);
X }
X else
X if (IsNum (p, 7))
X { (void) sscanf (p, "%o", &key);
X }
X else
X { Msg (0, "%s: bind: character, ^x, or octal number expected.", fn); }
X ktab[key].lflag = loginflag;
X if (argc < 3)
X { ktab[key].type = 0;
X }
X else
X { for (pp = KeyNames; *pp; ++pp)
X if (strcmp (ap[2], *pp) == 0) break;
X if (*pp)
X { ktab[key].type = pp-KeyNames+1; }
X else
X { ktab[key].type = KEY_CREATE;
X ktab[key].args = SaveArgs (argc-2, ap+2);
X }
X }
X }
X else
X Msg (0, "%s: unknown keyword \"%s\".", fn, ap[0]);
X }
X (void) fclose (f);
X
X} /* ReadRc() */
X
Xstatic
XParse (fn, buf, args)
Xchar *fn, *buf, **args;
X{
X register char *p, **ap;
X register delim, argc;
X
X p = buf;
X ap = args;
X argc = 0;
X for (;;)
X { while (*p && (*p == ' ' || *p == '\t'))
X ++p;
X if (*p == '\0' || *p == '#')
X return argc;
X if (argc > MAXARGS-1)
X Msg (0, "%s: too many tokens.", fn);
X delim = 0;
X if (*p == '"' || *p == '\'')
X { delim = *p; *p = '\0'; ++p; }
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, "%s: Missing quote.", fn);
X else
X return argc;
X }
X *p++ = '\0';
X }
X
X} /* Parse() */
X
Xstatic char **
XSaveArgs (argc, argv)
Xregister argc;
Xregister char **argv;
X{
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} /* SaveArgs() */
X#endif
X
Xstatic
XMakeNewEnv ()
X{
X register char **op, **np = NewEnv;
X static char buf[MAXSTR];
X
X if (strlen (SockName) > MAXSTR-5)
X SockName = "?";
X sprintf (buf, "LTY=%s", SockName);
X *np++ = buf;
X *np++ = Term;
X np += 2;
X for (op = environ; *op; ++op)
X { if (np == NewEnv + MAXARGS - 1)
X break;
X if ( !IsSymbol (*op, "TERM")
X && !IsSymbol (*op, "TERMCAP")
X && !IsSymbol (*op, "LTY")
X )
X *np++ = *op;
X }
X *np = 0;
X
X} /* MakeNewEnv() */
X
Xstatic
XIsSymbol (e, s)
Xregister char *e, *s;
X{
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
X return 0;
X
X} /* IsSymbol() */
X
X/*VARARGS2*/
XMsg (err, fmt, p1, p2, p3, p4, p5, p6)
Xchar *fmt;
X{
X char buf[1024];
X register char *p = buf;
X
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 if (!Abortonmsg)
X { /* MakeStatus (buf, curr);*/
X printf("%s\r\n", buf);
X }
X else
X { printf ("%s\r\n", buf);
X exit (1);
X }
X
X} /* Msg() */
X
Xstatic char *
XFilename (s)
Xchar *s;
X{
X register char *p;
X
X p = s + strlen (s) - 1;
X while (p >= s && *p != '/')
X --p;
X return ++p;
X
X} /* Filename() */
X
Xstatic
XIsNum (s, base)
Xregister char *s;
Xregister base;
X{
X for (base += '0'; *s; ++s)
X if (*s < '0' || *s > base)
X return 0;
X return 1;
X
X} /* IsNum() */
X
X
Xstatic
XInitUtmp ()
X{
X if ((utmpf = open (UtmpName, O_RDWR)) == -1)
X { if (errno != EACCES)
X Msg (errno, UtmpName);
X return;
X }
X utmp = 1;
X
X} /* InitUtmp() */
X
X
Xstatic int
XFindUtmp(name)
Xchar *name;
X{
X register char *p;
X register struct ttyent *tp;
X register slot;
X
X DO DEBUG("FindUtmp(%s)\n", name);
X slot = 1;
X if (!utmp)
X return 0;
X if (p = rindex (name, '/'))
X ++p;
X else
X p = name;
X setttyent ();
X while ( (tp = getttyent ()) != NULL
X && strcmp (p, tp->ty_name) != 0
X )
X ++slot;
X if (tp == NULL)
X return 0;
X
X DO DEBUG(" slot %d\n", slot);
X return slot;
X
X} /* FindUtmp() */
X
X
Xstatic int
XSetUtmp (name, mainlogin)
Xchar *name; /* tty name */
Xint mainlogin; /* this is primary login */
X{
X register char *p;
X register slot;
X struct utmp u;
X
X if ((slot=FindUtmp(name)) == 0)
X return ( 0 );
X
X if (p = rindex (name, '/'))
X ++p;
X else
X p = name;
X
X strncpy (u.ut_line, p, 8);
X strncpy (u.ut_name, LoginName, 8);
X#if 1
X strncpy(u.ut_host, Filename (RealTtyName), 16); /* host is real tty */
X#else
X u.ut_host[0] = '\0';
X#endif
X if (RealSlot && mainlogin)
X u.ut_time = RealUtmp.ut_time; /* use original login time */
X else
X time (&u.ut_time);
X (void) lseek (utmpf, (long)(slot * sizeof (u)), 0);
X (void) write (utmpf, (char *)&u, sizeof (u));
X
X return ( slot );
X
X} /* SetUtmp() */
X
Xstatic int
XReadUtmp(slot, entry)
Xint slot; /* slot to read */
Xstruct utmp *entry; /* entry to read into */
X{
X int cnt; /* return count */
X
X if (!utmp)
X return; /* no utmp access */
X
X (void) lseek(utmpf, (long)(slot * sizeof(struct utmp)), 0);
X cnt = read(utmpf, (char *)entry, sizeof(struct utmp));
X DO DEBUG("ReadUtmp cnt %d, errno %d, line %.8s, name %.8s, host %.16s\n",
X cnt, errno, entry->ut_line, entry->ut_name, entry->ut_host);
X
X return ( cnt );
X
X} /* ReadUtmp() */
X
Xstatic void
XWriteUtmp(slot, entry)
Xint slot; /* slot to write */
Xstruct utmp *entry; /* entry to write from */
X{
X int cnt; /* write return code */
X
X if (!utmp)
X return; /* no utmp access */
X
X (void) lseek(utmpf, (long)(slot * sizeof(struct utmp)), 0);
X cnt = write(utmpf, (char *)entry, sizeof(struct utmp));
X DO DEBUG("WriteUtmp() slot %d cnt %d line %.8s name %.8s host %.16s\n",
X slot, cnt, entry->ut_line, entry->ut_name, entry->ut_host);
X
X} /* WriteUtmp() */
X
Xstatic
XRemoveUtmp (slot)
X{
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} /* RemoveUtmp() */
X
X#ifndef GETTTYENT
X
Xstatic
Xsetttyent ()
X{
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')
X *p = '\0';
X *p = '\0';
X ttnext = tt;
X
X} /* setttyent() */
X
Xstatic struct ttyent *
Xgetttyent ()
X{
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} /* getttyend() */
X
X#endif
X
X
X
X /* FinitTerm() - reset vt100 terminal */
Xstatic void
XFinitTerm ()
X{
X /* print out termcap 'is' string to reset terminal */
X#if 0
X /* This string sets scroll region 1-24 and puts cursor at bottom line */
X printf("\033[1;24r\033[24;1H");
X#endif
X fflush(stdout);
X}
X
Xstatic void
XAddCap (s)
Xchar *s;
X{
X register n;
X
X if (tcLineLen + (n = strlen (s)) > 55)
X { strcat (Termcap, "\\\n\t:");
X tcLineLen = 0;
X }
X strcat (Termcap, s);
X tcLineLen += n;
X}
X
Xstatic char *
XMakeTermcap(lines, chars)
Xint lines; /* default window lines */
Xint chars; /* default window chars */
X{
X char buf[1024];
X register char **pp, *p;
X
X strcpy(Termcap, TermcapConst1); /* start TERMCAP build */
X strcat(Termcap, UserTerm); /* fill in User's terminal type */
X strcat(Termcap, TermcapConst3); /* finish our own definition */
X
X if (lines <= 0 || lines > 200)
X lines = rows; /* force default if none or invalid */
X if (chars <= 0 || chars > 300)
X chars = cols; /* force default if none or invalid */
X
X sprintf(buf, "li#%d:co#%d:", lines, chars);
X AddCap(buf);
X
X return ( Termcap );
X
X} /* MakeTermcap() */
X
X
X /* DEBUG() - dump output routine */
X
Xvoid
XDEBUG(format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)
Xchar *format;
Xint arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8;
X{
X fprintf(stderr, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
X}
SHAR_EOF
if test 56819 -ne "`wc -c < 'layers.c'`"
then
echo shar: error transmitting "'layers.c'" '(should have been 56819 characters)'
fi
fi # end of overwriting check
# End of shell archive
exit 0
--- end of part 3 ---