Path: utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!sdd.hp.com!elroy.jpl.nasa.gov!zardoz.cpd.com!dhw68k!macintosh From: werner@rascal.ics.utexas.edu (Werner Uhrig) Newsgroups: comp.sources.mac Subject: MacLayers 1.00 (part 3 of 6) Message-ID: <1990Jun2.080116.19047@dhw68k.cts.com> Date: 2 Jun 90 08:00:09 GMT References: <1990Jun1.070105.1489@dhw68k.cts.com> <1990Jun1.190100.8893@dhw68k.cts.com> Reply-To: layers@rascal.ics.utexas.edu Organization: U. Texas CS Dept., Austin, Texas Lines: 2242 Approved: bytebug@dhw68k.cts.com (Roger L. Long) [MacLayers 1.00 - part 3 of 6] --- #! /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 * ] 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 X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X X#include "layers.h" X X#ifdef GETTTYENT X#include 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<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; nallocated) 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; nallocated) 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; ichan = 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; ilpid == 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; iworigv, 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<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 ---