From: Eric Rosen <eric@cse.ucsc.edu> Date: Sun, 17 Jan 1993 23:12:39 -0800 Subject: comm/maclayers-unix-side.shar # This is a shell archive. Remove anything before this line, # then unpack it by saving it in a file and typing "sh file". # # Wrapped by on Sun Jan 17 23:08:12 CST 1993 # Contents: layers_1.10/ layers_1.10/INFO layers_1.10/README # layers_1.10/layers.1 layers_1.10/layers.c layers_1.10/layers.h # layers_1.10/layersize.c layers_1.10/layertitle.c layers_1.10/macbput.1 # layers_1.10/macbput.c layers_1.10/macbput.h layers_1.10/makefile # layers_1.10/protocol.c echo mkdir - layers_1.10 mkdir layers_1.10 chmod u=rwx,g=rx,o=rx layers_1.10 echo x - layers_1.10/INFO sed 's/^@//' > "layers_1.10/INFO" <<'@//E*O*F layers_1.10/INFO//' MacLayers 1.20 January 16, 1993 MacLayers is a vt100 terminal emulator that allows multiple terminal windows to be opened to a host simultaneously over a single serial connection. The remote host computer must be a UNIX system which has sockets support. Each terminal window may be associated with a shell, login to a different host, or an individual command. Complete facilities are available for controlling the window and the associated host processes attached thereto. MacLayers is free for all to use, but is not in the public domain. You may not sell MacLayers or any program derived from it. MacLayers is distributed in two parts, a Macintosh side and a Unix side. Both are available via anonymous FTP from the official MacLayers archive site, rascal.ics.utexas.edu, in mac/COMM/MacLayers. Quick Start for MacLayers Experts --------------------------------- Here is the minimum information that users of MacLayers 1.10 need in order to upgrade. 1. You need to rebuild your startup (settings) files since the format has changed. Erratic behaviour may result from the use of old settings files. 2. You need to install a new version of macbput on your UNIX host. 3. Other UNIX programs (layers, layersize, layertitle) need not be upgraded. @//E*O*F layers_1.10/INFO// chmod u=rw,g=r,o=r layers_1.10/INFO echo x - layers_1.10/README sed 's/^@//' > "layers_1.10/README" <<'@//E*O*F layers_1.10/README//' MacLayers v1.20 Unix-Side (layers) 1.20 Release 17 January, 1993 The following files are provided with the Unix side of the MacLayers v1.20 release. README - this file layers.1 - layers command Unix manual page makefile - layers Makefile layers.c - layers source files layers.h protocol.c layersize.c layertitle.c macbput.1 - v1.20 updated Unix manual page for macbput macbput.c - v1.20 updated source to macbput macbput.h Note: macbput is a public domain download utility; the version distributed with layers v1.20 is intended for use only with MacLayers v1.20. If you are upgrading from layers v1.10, note that no changes have been made to the layers, layertitle, and layersize programs. Only the program macbput and associated man page have changed. *** SUPPORTED SYSTEMS *** MacLayers has been successfully installed on the following systems: SunOS 4.0.3 SunOS 3.2 Sequent DYNIX 3.0.12 (#DEFINE SEQUENT) Sequent DYNIX 3.0.4 (#DEFINE SEQUENT) MacLayers has failed to install on these systems: AIX 2.2.1 on IBM RT Unsupported ioctls IRIX 4.0.1 on 440VGX *** INSTALLATION *** There are three ways to install the layers system: make all or make installsetuid or make installnosetuid Installing the layers utility setuid allows the layers command to perform two necessary actions as part of its operation. First, if your system keeps /etc/utmp protected from general user access the setuid allows proper updating of /etc/utmp so that logons are properly displayed by commands such as 'who'. More importantly utilities like talk and write will not function properly for layers users without the setuid installation. Second, setuid allows the layers utility to perform chown/chmod on its allocated pseudo-ttys. This is necessary to plug security holes in some systems which otherwise would allow outside users free access to the pseudo control terminal ttys of layers users. There are a few customizing parameters which may be of interest to system administrators near the beginning of file layers.c (especially for Sequent systems.) **** end of document **** @//E*O*F layers_1.10/README// chmod u=rw,g=r,o=r layers_1.10/README echo x - layers_1.10/layers.1 sed 's/^@//' > "layers_1.10/layers.1" <<'@//E*O*F layers_1.10/layers.1//' @.TH LAYERS l "17 March 1990" @.SH NAME @.PP layers, layertitle \- Protocol for MacLayers Multiwindow Terminal Emulator for Unix @.SH SYNOPSIS layers - start window protocol @.LP layers [-l] [command] - create new window @.LP layertitle string - retitle current layers window @.IX "layers" "start layers protocol, create new window" @.IX "layertitle" "rename current layers window" @.SH DESCRIPTION @.PP MacLayers provides multi-window capability for a Macintosh (greater than 128K) connected to a host UNIX(TM) system with sockets support. Each window may be associated with a shell, login to a different host, or an individual command. Complete facilities are available for controlling the window and the associated host processes attached thereto. @.PP To use MacLayers, you must have the MacLayers vt-100 terminal emulation program on a Macintosh and the layers protocol program installed on your Unix host. @.SH MACLAYERS OPERATION @.PP The Maclayers Application on the Mac starts up as a garden variety host-to-terminal vt-100 emulator. (As such you can run it with any host, not just a Unix machine.) Baud rate and other configurations are set by selections in the Control Menu. @.PP Enter the @.I layers command to the Unix host using no options or parameters to start the layers protocol. The initial terminal window will be closed and replaced with a layers protocol window. A shell is run in this window, either /bin/sh or the shell indicated by your $SHELL variable. @.PP You can start a new shell layer by picking "New" on the Layers menu. You can also start a new layer window by issuing the @.I layers command to a shell layer window. If you use no operands then the new layer window will be a shell. However, you can specify any command you wish by simply adding it as a parameter. Examples: "layers vi testfile.c", "layers telnet earth." @.PP If you are specifying a shell then you can also elect to have it be a login shell by adding a -l option. This allows broadcast/write/talk capabilities for that window. The initial layer window shell defaults to a login shell. @.PP When a layer process group terminates its window is automatically closed. MacLayers exits layers mode when the last (or only) layer window is closed. You may also use the Layers Menu "Shutdown" to terminate layers mode. You cannot quit the MacLayers application while in layers mode but must Shutdown the multi-window mode first. @.PP You can abort host @.I layers by using the Control Menu "Abort Host Layers" item which is always available. This may be necessary if your Mac loses contact with the host and you restart the MacLayers application at which time the host would still be in multi-window layers mode while the application would not. If the MacLayers application terminates due to a non-recoverable problem it will always issue an order to terminate layers mode on the host before returning to the Finder. @.SH XMODEM DOWNLOADING @.PP MacLayers has a download facility for downloading XMODEM MacTerminal ('macput' command) and MacBinary ('macbput' command) files. Straight vanilla XMODEM is not supported. Only one window can be doing a XMODEM download at any one time. Downloading does not effect any other MacLayers operations so you can freely use any other windows or applications (with MultiFinder) while a download is in progress. Remember though that the topmost window receives the highest priority data transfer from the host. So for the fastest downloading keep the XMODEM layer window the active window. @.SH FILES @.PP /usr/tmp/layers/<login> Directory created by @.I layers /usr/tmp/layers/<login>/host.ttySocket Created by @.I layers @.SH AUTHOR Dave Trissel @.SH BUGS AND CAVEATS @.PP *) The shell TERM variable must have the same value in your layer shells as it does when you initially start @.I layers up. @.PP *) If you set the BSD shell TERMCAP variable then that variable must be set in your .login file. It may not be changed to something different in .cshrc. @.PP *) The @.I layers command will not properly work when being issued from a remote login into the same machine which is already running the initial @.I layers startup command. @.PP *) There is no file upload facility. @.PP *) MacLayers will access the disk less often if you have RAM Cache enabled on the Macintosh. @.PP *)Layers must be installed as set-uid with owner root in order to be able to correctly change the owner of the tty device file for each window. Special permission may also be required to write the file "/etc/utmp". @.SH SEE ALSO @.PP The MacLayers program is completely described in the manual that accompanies it. @.PP Manual page courtesy of Peter Newton. @.PP UNIX(TM) is a registered trademark of American Telephone and Telegraph. Macintosh is a trademark of McIntosh Laboratories and is licensed to Apple Computer. @//E*O*F layers_1.10/layers.1// chmod u=rw,g=r,o=r layers_1.10/layers.1 echo x - layers_1.10/layers.c sed 's/^@//' > "layers_1.10/layers.c" <<'@//E*O*F layers_1.10/layers.c//' /******** Layers.c ********* ********* Layers - MacLayers Multiwindow BSD Socket Driver ********* ********* Dave Trissel oakhill!davet ********* ********* The sockets handling portion of this control module is based ********* upon 'screen' by Oliver Laumann whose copyright remains below. ********* The rest is * * Copyright (C) 1989 by David W. Trissel * * Not derived from licensed software. * * Permission is granted to freely use, copy, modify, and redistribute * this software, provided that no attempt is made to gain profit from it, * the author is not construed to be liable for any results of using the * software, alterations are clearly marked as such, and this notice is * not modified. * */ static char LayersVersion[] = "layers 1.10 19-Oct-1991"; /* Layers Changes: Version 1.00 17-Mar-1990 First public release. Version 1.01 10-May-1990 * Fixed bug 001 which caused failure of protocol mode to establish if USRLIMIT check is enabled and USRLIMIT or more login shells are already active. Could also keep new login layer windows from responding until another layer is brought up front and then sent back. Version 1.01 27-Jun-1991 * Corrected bugs where utmp login record or initial tty settings may not be restored if initial client connection fails * Added return code support so shell scripts can determine what happened: 0 - layers connected, ran and terminated sucessfully 1 - not running under MacLayers client terminal 2 - layers terminated due to some abnormal condition 3 - SIGQUIT or SIGTERM signal received 4 - SIGHUP signal received * Rewrote protocl.c to avoid using scanf which was prohibiting 7-bit communication lines from working under layers mode. (Scanf does not work on 7-bit line in RAW mode since high bit invalidates its character checking.) * Removed internal TERMCAP entries for "ic" and "dc" (insert character and delete character) which are not ANSI standard. The "dc" collided with a real vt-100 capability that the author did not know about! (Note of 6/11/91: perhaps "dc" and "ic" will be reinstated with their true ANSI definitions. 6/11/91) * If socket directory not 0700 then try to change it to 0700. Version 1.10 26-Sep-1991 * Host/client version ID sharing at startup. This is transparent on both sides with older versions. 1) If client identifies itself as at least 1.10 MacLayers add vt102 TERMCAP entries for ic, dc, IC, DC with correct capabilities. (V1.00 had incorrect ones-see above.) */ /* Copyright (c) 1987,1988 Oliver Laumann, Technical University of Berlin. * Not derived from licensed software. * * Permission is granted to freely use, copy, modify, and redistribute * this software, provided that no attempt is made to gain profit from it, * the author is not construed to be liable for any results of using the * software, alterations are clearly marked as such, and this notice is * not modified. * * Modified by Patrick Wolfe (pat@kai.com, kailand!pat) * Do whatever you want with (my modifications of) this program, but * don't claim you wrote them, don't try to make money from them, and * don't remove this notice. */ /* * Beginning of User Configuration Section */ /* * SEQUENT -- your host system is Sequent. This changes a setvbuf() * call to a setlinebuf(). Also extends pty search names. * */ #undef SEQUENT /* * GETTTYENT -- your system has the new format /etc/ttys (like 4.3 BSD) * and the getttyent(3) library functions. * */ #undef GETTTYENT /* * LOGINDEFAULT -- if set to 1 (one), windows will login (add entries to * /etc/utmp) by default. Set to 0 if you don't want this. * (Also see USRLIMIT below). [NOTE: current code always * logs layer #1 only unless -l option used on 'layers' * command.] */ #define LOGINDEFAULT 0 /* * USRLIMIT -- count all non-null entries in /etc/utmp before adding a * new entry. Some machine manufacturers (incorrectly) count * logins by counting non-null entries in /etc/utmp (instead * of counting non-null entries with no hostname and not on * a pseudo tty). Sequent does this, so you might reach your * limited user license early. */ #define USRLIMIT 64 /* * SOCKDIR -- If defined, this directory is where layers sockets will be * placed, (actually in a subdirectory by the user's loginid). * This is neccessary because NFS doesn't support socket * operations, and many people's homes are on NFS mounted * partitions. Layers will create this directory if it needs * to. */ #define SOCKDIR "/tmp/layers" /* NFS doesn't support named sockets */ /* * End of User Configuration Section */ #include <stdio.h> #include <sgtty.h> #include <signal.h> #include <errno.h> #include <ctype.h> #include <utmp.h> #include <pwd.h> #include <nlist.h> #include <fcntl.h> #include <sys/types.h> #include <sys/time.h> #include <sys/file.h> #include <sys/wait.h> #include <sys/socket.h> #include <sys/un.h> #include <sys/stat.h> #include <sys/dir.h> #include <sys/ioctl.h> #include "layers.h" #ifdef GETTTYENT #include <ttyent.h> #else static struct ttyent { char *ty_name; } *getttyent(); static char *tt, *ttnext; static char ttys[] = "/etc/ttys"; #endif #ifndef FSCALE #define FSCALE 1000.0 /* Sequent doesn't define FSCALE...grrrr */ #endif #ifdef USRLIMIT struct utmp utmpbuf; int UserCount; #endif #define Ctrl(c) ((c)&037) /* C library items */ extern char **environ; extern errno; extern sys_nerr; extern char *sys_errlist[]; extern char *index(), *rindex(), *malloc(), *getenv(); extern char *getlogin(), *ttyname(); /* Local items */ static void FAbort(), SigHup(), SigChld(), AddCap(), FinitTerm(); static char *MakeTermcap(), *Filename(), **SaveArgs(), *GetTtyName(); static void InitWorld(), ClearShape(), BuildTitle(), KillPG(); static void SetWindowSize(), WriteUtmp(); static int ReadUtmp(), FindUtmp(), SetUtmp(); static int loginflag = -1; static char PtyName[32], TtyName[32]; static char *ShellProg; static char *ShellArgs[2]; static inlen; static ESCseen; static GotSignal; static char DefaultShell[] = "/bin/sh"; static char DefaultPath[] = ":/usr/ucb:/bin:/usr/bin"; static char PtyProto[] = "/dev/ptyXY"; static char TtyProto[] = "/dev/ttyXY"; static int TtyMode = 0622; static struct stat RealTtyStat; /* Real tty stat */ static char RealTtyName[32] = ""; /* Real tty name */ static int RealSlot = 0; /* Real tty logon slot */ static struct utmp RealUtmp; /* Real tty logon utmp entry */ static int RealTtyMode = 0; /* Real tty mode */ static int Oumask; /* Original user's umask */ static char SockPath[512]; #ifdef SOCKDIR static char SockDir[] = SOCKDIR; #else static char SockDir[] = ".layers"; #endif static char *SockNamePtr, *SockName; static ServerSocket; static char *NewEnv[MAXARGS]; static char Esc = Ctrl('a'); static char MetaEsc = 'a'; static char *home; static Abortonmsg = 0; static Resetenvonmsg = 0; static utmp, utmpf; static char UtmpName[] = "/etc/utmp"; static char *LoginName; static mflag, nflag, fflag, rflag; static char HostName[MAXSTR]; static char *myname; static DevTty; static struct mode { struct sgttyb m_ttyb; struct tchars m_tchars; struct ltchars m_ltchars; int m_ldisc; int m_lmode; } OldMode, NewMode; #define MSG_CREATE 0 #define MSG_ERROR 1 struct msg { int type; union { struct { int lflag; /* login flag */ struct Shape shape; /* window shape */ int nargs; char line[MAXLINE]; char dir[1024]; } create; char message[MAXLINE]; } m; }; /* dynamic keyboard input buffer definition */ struct Kbuff { struct Kbuff * next; /* next buffer in chain (or NULL) */ int size; /* size of data in this buffer */ int offset; /* start of first data in buffer */ unsigned char text[IOSIZE]; /* text buffer itself */ }; /* World layer definition */ struct Layer { int chan; /* channel represented by this layer */ int allocated; /* layer allocated */ int ptyfd; /* psuedo tty */ int ptymask; /* mask for pty descriptor */ int lpid; /* layer head process ID */ int slot; /* utmp slot number */ struct Kbuff *kbuff; /* keyboard input buffers */ struct Shape shape; /* Shape structure to/from MacLayers */ char cmd[MAXSTR]; /* command to execute */ char tty[MAXSTR]; /* psuedo tty ID */ }; static struct Layer World[MAXPCHAN]; /* all layer structures */ static int rows = 24; /* default window height in lines */ static int cols = 80; /* default window width in chars */ static char Termcap[1024]; static char Term[MAXSTR] = "TERM="; /* window's terminal type */ static char *UserTerm; /* terminal ID we are mimmicing */ static int flowctl; static tcLineLen = 100; #define EXITNORMAL 0 /* normal exit - connection succeeded */ #define EXITNOMACLAYERS 1 /* not running to MacLayers terminal */ #define EXITABNORMAL 2 /* something wrong generic error */ #define EXITSIGQUIT 3 /* TERM or QUIT signal got us */ #define EXITSIGHUP 4 /* HUP signal got us */ static exitcode = EXITABNORMAL; /* default to abnormal exit */ /* co#80 and li$24 added dynamically for proper window size */ static char TermcapConst1[] = "TERMCAP=SC|"; static char TermcapConst3[] = "|MacLayers virtual terminal|\\\n\ :cr=^M:do=^J:nl=^J:bl=^G:cl=\\E[;H\\E[2J:\\\n\ :le=^H:bs:am:cm=\\E[%i%d;%dH:nd=\\E[C:up=\\E[A:\\\n\ :ce=\\E[K:cd=\\E[J:so=\\E[7m:se=\\E[m:us=\\E[4m:ue=\\E[m:\\\n\ :md=\\E[1m:mr=\\E[7m:mb=\\E[5m:me=\\E[m:is=\\E[1;24r\\E[24;1H:\\\n\ :rf=/usr/lib/tabset/vt100:\\\n\ :rs=\\E>\\E[?3l\\E[?4l\\E[?5l\\E[?7h\\E[?8h:ks=\\E[?1h\\E=:ke=\\E[?1l\\E>:\\\n\ :ku=\\EOA:kd=\\EOB:kr=\\EOC:kl=\\EOD:kb=^H:\\\n\ :ho=\\E[H:k1=\\EOP:k2=\\EOQ:k3=\\EOR:k4=\\EOS:ta=^I:pt:sr=\\EM:vt#3:xn:\\\n\ :sc=\\E7:rc=\\E8:cs=\\E[%i%d;%dr:\\\n\ :"; /* remove illegal "dc" which is ANSI/vt-100 reverse index V1.81 */ #if 0 :dc=\\ED:ic=\\EI:"; /* NOTE: the above two cababilities are beyond vt100 - unique to MacLayers */ #endif /* The following supported by MacLayers versions 1.1 and later */ /* ANSI insert characters and delete characters */ static char TermcapConst101[] = "ic=\\E[@:dc=\\E[P:IC=\\E[%d@:DC=\\E[%dP:"; int Dflag; /* debug dump flag */ int Clientlevel = 1; /* client protocol (Default to V1.00) */ int Protocollevel = 1; /* effective protocol (default 1) */ /* main() */ int main(ac, av) char **av; { register n; register len; register struct Layer *layer; char *ap; struct passwd *ppp; int s; int r; /* read fd test bits */ int w; /* write fd test bits */ int stall; /* stall flag for priority channel */ int fderr; /* error fd test bits */ struct timeval tv; struct Shape shape; /* window shape */ time_t now; char buf[IOSIZE]; char rc[256]; struct stat st; struct Kbuff *kbptr; /* input keyboard buffer pointer */ Abortonmsg = 1; /* quit if message generated */ ClearShape(&shape); /* initialize shape structure */ myname = (ac == 0) ? "layers" : av[0]; InitWorld(); /* clear World array structures */ while (ac > 0) { ap = *++av; if (--ac > 0 && *ap == '-') { switch (ap[1]) { case 'l': /* login this command line */ loginflag = 1; break; case 'd': /* dump debugging flag */ Dflag = 1; (void) freopen("layers.dump", "a", stderr); /* append mode */ #ifdef SEQUENT setlinebuf(stderr); #else setvbuf(stderr, NULL, _IOLBF, 0); #endif break; case 'u': /* do not login this command line */ loginflag = 0; break; case 'm': /* force this to be master and not a client */ mflag = 1; break; case 'n': /* no flow control */ nflag = 1; break; case 'f': /* flow control on */ fflag = 1; break; case 'v': /* do nothing but issue layers version */ printf("%s\n", LayersVersion); exit(EXITNORMAL); default: help: Msg (0,"Use: %s [-f] [-l | -u] [-m] [-n] [cmd args]\n", myname); } /* end switch on '-' option */ } /* end if '-' */ else break; } /* end while parameters */ if (nflag && fflag) Msg (0, "-f and -n are conflicting options."); if ((ShellProg = getenv ("SHELL")) == 0) ShellProg = DefaultShell; DO DEBUG("ShellProg %s\n", ShellProg); /* we mimmic the user's $TERM ID */ if ((UserTerm = getenv("TERM")) == 0) UserTerm = "layers"; /* use "layers" if none */ (void) strcat(Term, UserTerm); DO DEBUG("%s\n", Term); ShellArgs[0] = ShellProg; if (ac == 0) { ac = 1; av = ShellArgs; shape.wattr |= Wa_shell; /* indicate a shell window */ } if ((home = getenv ("HOME")) == 0) Msg (0, "$HOME is undefined."); DO DEBUG("home %s\n", home); if ((LoginName = getlogin ()) == 0 || LoginName[0] == '\0') { if ((ppp = getpwuid (getuid ())) == 0) return; LoginName = ppp->pw_name; } DO DEBUG("LoginName %s\n", LoginName); if ((Oumask=umask(0)) == -1) Msg (errno, "Cannot change umask to zero"); DO DEBUG("Original umask o%o\n", Oumask); #ifdef SOCKDIR if (stat (SOCKDIR, &st) == -1) { if (errno == ENOENT) { if (mkdir (SOCKDIR, 0777) == -1) Msg (errno, "Cannot make directory %s", SOCKDIR); (void) chown (SOCKDIR, 0, 0); } else Msg (errno, "Cannot get status of %s", SOCKDIR); } else { if ((st.st_mode & S_IFMT) != S_IFDIR) Msg (0, "%s is not a directory.", SOCKDIR); if ((st.st_mode & 0777) != 0777) Msg (0, "Directory %s must have mode 777.", SOCKDIR); } sprintf (SockPath, "%s/%s", SockDir, LoginName); #else sprintf (SockPath, "%s/%s", home, SockDir); #endif DO DEBUG("SockPath %s\n", SockPath); if (stat (SockPath, &st) == -1) { if (errno == ENOENT) { if (mkdir (SockPath, 0700) == -1) Msg (errno, "Cannot make directory %s", SockPath); (void) chown (SockPath, getuid (), getgid ()); DO DEBUG("SockPath directory made\n"); } else Msg (errno, "Cannot get status of %s", SockPath); } else { if ((st.st_mode & S_IFMT) != S_IFDIR) Msg (0, "%s is not a directory.", SockPath); if ((st.st_mode & 0777) != 0700) { if (chmod(SockPath, 0700) == -1) Msg(0, "Cannot change directory %s to mode 700.", SockPath); } if (st.st_uid != getuid ()) Msg (0, "You are not the owner of %s.", SockPath); } (void) strcpy(RealTtyName, GetTtyName()); /* real tty name */ if (stat(RealTtyName, &RealTtyStat) == -1) /* get current mode */ Msg(errno, "Cannot get status of %s", RealTtyName); DO DEBUG("Mode of %s is %#o\n", RealTtyName, RealTtyStat.st_mode); RealTtyMode = RealTtyStat.st_mode; /* save mode for later restore */ (void) gethostname (HostName, MAXSTR); HostName[MAXSTR-1] = '\0'; DO DEBUG("HostName %s\n", HostName); if (ap = index (HostName, '.')) *ap = '\0'; if (ap) DO DEBUG("*ap %s\n", *ap); strcat (SockPath, "/"); SockNamePtr = SockPath + strlen (SockPath); /* if we are a client send create message to startup window and exit */ if (GetSockName ()) { DO DEBUG("GetSockName says that we are client\n"); DO DEBUG("SockName '%s'\n", SockName); s = MakeClientSocket (1); DO DEBUG("Client socket is %d\n", s); DO DEBUG("SendCreateMsg()\n"); SendCreateMsg (s, ac, av, loginflag, &shape); close (s); DO DEBUG("after SendCreateMsg(), now exit(EXITNORMAL)\n"); exit (EXITNORMAL); } /* we are the server */ DO DEBUG("SockName '%s'\n", SockName); DO DEBUG("We are server\n"); if ((DevTty = open ("/dev/tty", O_RDWR|O_NDELAY)) == -1) Msg (errno, "/dev/tty"); DO DEBUG("opened /dev/tty fd %d\n", DevTty); ServerSocket = MakeServerSocket (); DO DEBUG("ServerSocket %d\n", ServerSocket); s = ServerSocket; if (fflag) flowctl = 1; else if (nflag) flowctl = 0; if (loginflag == -1) loginflag = LOGINDEFAULT; MakeNewEnv (); Resetenvonmsg = 1; /* cleanup Utmp and tty if abort */ GetTTY (0, &OldMode); SetMode (&OldMode, &NewMode); SetTTY (0, &NewMode); InitUtmp (); RealSlot = FindUtmp(RealTtyName); /* find current logon slot */ if (RealSlot) { if (ReadUtmp(RealSlot, &RealUtmp) > 0) /* read real login utmp */ RemoveUtmp(RealSlot); /* remove original logon slot */ else RealSlot = 0; /* something's wrong */ } signal (SIGHUP, SigHup); signal (SIGINT, SIG_IGN); /* we should never see this */ signal (SIGQUIT, FAbort); /* quit layers on these 2 signals */ signal (SIGTERM, FAbort); signal (SIGTTIN, SIG_IGN); signal (SIGTTOU, SIG_IGN); signal (SIGALRM, SIG_IGN); /* alarm clock used by protocol.c */ if (Initlink() == 0) { exitcode = EXITNOMACLAYERS; /* return special status code */ Msg (0, "\n\n You are not running under MacLayers."); } sprintf (rc, "%.*s/.layersrc", 245, home); #if 0 /* NOT YET SUPPORTED */ /* if no window list start up a default shell window */ if (ReadRc(rc) == 0) #endif { n = MakeWindow (0, *av, av, (char *)0, loginflag, &shape); if (n == -1) { SetTTY (0, &OldMode); FQuit(EXITABNORMAL); /* NOT REACHED */ } } (void) chmod(RealTtyName, 0600); /* lock out broadcasts */ Abortonmsg = 0; /* don't abort on msg from now on */ Resetenvonmsg = 0; /* no cleanup - messages normal */ signal (SIGCHLD, SigChld); tv.tv_usec = 0; tv.tv_sec = 0; /* default no timer wanted */ /* client/Maclayers processing loop */ /* poll .20 secs for new startups */ #define WUSSTARTUP 200000 /* stall nonpriority windows .5 seconds when top window I/O active */ #define WUSSTALL 500000 stall = 0; /* startout no stalled channels */ fderr = 0; /* startout no error fd's */ while (1) { int priochan; /* the top window channel */ priochan = TopChannel(); /* find highest priority channel */ /* check for I/O on all available I/O descriptors */ r = 1<<0; /* always read MacLayers stream */ r |= 1<<s; /* always read server socket */ w = 0; /* initalize to no write tests */ tv.tv_usec = 0; /* default no startup poll */ /* for all active layers set read and write bits as appropriate */ if (stall) stall = 1; /* start counting output layers */ for (n=0; n<MAXPCHAN; n++) if ((layer = &World[n])->allocated) /* if layer exists ... */ { /* if not yet started or has just terminated ... */ if (layer->ptymask & fderr) tv.tv_usec = WUSSTARTUP; /* don't spinloop but wait-a-bit */ else { if (layer->kbuff && layer->kbuff->size) /* keyboard input for layer */ w |= layer->ptymask; /* try write to it */ /* read layer output unless we're being nice to top window */ if (!stall) r |= layer->ptymask; /* read output from layer */ else if (layer->chan == priochan) r |= layer->ptymask; /* read top priority output */ else stall++; /* indicate something to stall */ } } if (stall > 1) if (!tv.tv_usec) tv.tv_usec = WUSSTALL; /* set stall timout */ /* process signals before trying select */ if (GotSignal) { SigHandler (); continue; } DO DEBUG("Select(r %x, w %x, fderr %x, stall %d, prio %d, us %d)\n", r, w, fderr, stall, priochan, tv.tv_usec); switch ( select(32, &r, &w, NULL, tv.tv_usec ? &tv : NULL) ) { case -1: /* errno has report */ if (errno == EINTR) /* signal delivered or timout */ { errno = 0; tv.tv_usec = 0; /* clear timer wait value */ fderr = 0; /* turn off error stall */ stall = 0; /* turn off output priority stall */ DO DEBUG("select errno EINTR\n"); continue; /* re-loop */ } Abortonmsg = 1; Resetenvonmsg = 1; /* clean up tty settings */ DO DEBUG("select errno %d\n", errno); Msg (errno, "select"); /*NOTREACHED*/ case 0: /* timeout reached */ tv.tv_usec = 0; /* clear timer wait value */ stall = 0; /* turn off stall */ fderr = 0; /* turn off error stall */ continue; /* re-loop */ default: /* a channel has read/write status pending */ break; } DO DEBUG("after select r %x w %x\n", r, w); /* handle any signal arriving up during select wait */ if (GotSignal) { SigHandler (); continue; } /* if server socket has command process that now */ if (r & 1 << s) { ReceiveMsg(s); /* process client control packet */ continue; /* status may have changed */ } /* next process input stream from MacLayers */ if (r & 1 << 0) { ProcessStreamin(); /* key input and control packets */ continue; /* status may have changed */ } /* process keyboard input first so output doesn't hold up ** keyboard echo and break/interrupt processing */ priochan = TopChannel(); /* find top priority channel */ stall = 0; /* assume no stall needed */ for (n=0; n<MAXPCHAN; n++) if ((layer = &World[n])->allocated) if (w & layer->ptymask) while ((kbptr=layer->kbuff)->size) { /* pass as much keyboard as possible */ if (layer->chan == priochan) stall = 1; /* stall lower priority channels */ len = write(layer->ptyfd, &kbptr->text[kbptr->offset], kbptr->size); DO DEBUG("keyin len %d to chan %d\n", len, layer->chan); if (len <= 0) /* if no data accepted ... */ { if (errno == EIO) /* if I/O error indicated ... */ fderr |= layer->ptymask; /* wait-a-bit on this */ errno = 0; /* clear errno */ break; /* try again later */ } /* some of buffer accepted */ kbptr->size -= len; /* length processed */ kbptr->offset += len; /* bump up offset */ if (kbptr->size > 0) /* not all buffer accepted ... */ break; /* try feed again later */ /* see if another buffer chained */ if (kbptr->next) { /* yes, free up current buffer and queue next */ layer->kbuff = kbptr->next; /* to next buffer */ free(kbptr); /* free this buffer up */ } else { /* otherwise leave this for next input */ kbptr->size = 0; /* empty buffer */ kbptr->offset = 0; /* refill from the start */ } } /* first process the highest priority channel (top window) */ if (priochan > 0 && priochan <= MAXPCHAN) /* if valid ... */ if ((layer = &World[priochan-1])->allocated) if (r & layer->ptymask) { /* output to send to top MacLayers window */ len = read(layer->ptyfd, buf, IOSIZE); if (len >= 0) /* if no error ... */ { DO DEBUG("read output len %d chan %d\n", len, layer->chan); } else { /* We expect EIO error if socket not yet open on other end ** or if process using socket has terminated. We expect ** EWOULDBLOCK also after process terminates. **/ DO DEBUG("read output err chan %d errno %d len %d\n", layer->chan, errno, len); if (errno == EIO || errno == EWOULDBLOCK) DO DEBUG(" ...anticipated\n"); /* layer not ready or just terminated so wait-a-bit */ fderr |= layer->ptymask; r &= ~layer->ptymask; /* don't read it again below */ errno = 0; /* clear errno */ } if (len > 0) SendData(layer->chan, buf, len); if (len >= 0) /* To keep lower priority channels from hogging the line ** we delay any output from them until the primary window ** has no more data to be sent to it. */ stall = 1; /* stall output from others */ } /* now pass all available output to MacLayers */ if (!stall) for (n=0; n<MAXPCHAN; n++) if ((layer = &World[n])->allocated) if (r & layer->ptymask) { /* output to send to MacLayers window */ len = read(layer->ptyfd, buf, IOSIZE); if (len >= 0) /* if no error ... */ { DO DEBUG("output chan %d len %d\n", layer->chan, len); } else { /* We expect EIO error if socket not yet open on other end ** or if process using socket has terminated. We expect ** EWOULDBLOCK also after process terminates. **/ DO DEBUG("read output err chan %d errno %d len %d\n", layer->chan, errno, len); if (errno == EIO || errno == EWOULDBLOCK) { DO DEBUG(" ...anticipated\n"); } /* layer not ready or just terminated so wait-a-bit */ fderr |= layer->ptymask; errno = 0; /* clear errno */ } if (len > 0) SendData(layer->chan, buf, len); } /* handle signals again */ if (GotSignal) SigHandler (); } /* end while (1) */ /* NOT REACHED */ } /* main() */ /* ReceiveQuit() - MacLayers sends Quit packet */ void ReceiveQuit() { /* We completely quit layers cancelling all active processes */ DO DEBUG("ReceiveQuit()\n"); FQuit(EXITNORMAL); /* normal termination */ /* NOT REACHED */ } /* ReceiveQuit() */ /* ReceiveNew() - MacLayers requests a new shell layer */ void ReceiveNew(chanid, shape) int chanid; /* channel for new shell layer */ struct Shape *shape; /* shape for new channel */ { DO DEBUG("ReceiveNew(%d)\n", chanid); (void) MakeWindow (chanid, *ShellArgs, ShellArgs, (char *) 0, loginflag, shape); } /* ReceiveNew() */ /* ReceiveDelete() - MacLayers has removed a layer */ void ReceiveDelete(chanid) int chanid; /* channel which was deleted */ { struct Layer *layer; /* layer pointer */ /* validate channel */ DO DEBUG("ReceiveDelete(%d)\n", chanid); if (chanid <= 0 || chanid > MAXPCHAN) return; /* ignore invalid channel */ /* if this layer active then kill it off, else ignore request */ layer = &World[chanid-1]; /* locate target layer */ if (layer->allocated) KillWindow(layer); } /* ReceiveDelete() */ /* ReceiveSignal() - send signal to layer's process group */ void ReceiveSignal(chanid, signal) int chanid; /* layer's channel */ int signal; /* signal.h signal ID */ { struct Layer *layer; /* layer pointer */ DO DEBUG("ReceiveSignal(%d,%d)\n", chanid, signal); /* verify channel */ if (chanid <= 0 || chanid > MAXPCHAN) return; /* ignore invalid channel */ /* if this layer is active send the signal to the process group */ layer = &World[chanid-1]; /* locate target layer */ if (layer->allocated && layer->lpid) KillPG(layer, signal); } /* ReceiveSignal() */ /* ReceiveReshape() - windowsize and location updated */ void ReceiveReshape(chanid, shape) int chanid; /* channel having shape */ struct Shape *shape; /* shape structure */ { struct Layer *layer; /* layer pointer */ DO DEBUG("ReceiveReshape(%d)\n", chanid); /* verify channel */ if (chanid <= 0 || chanid > MAXPCHAN) return; /* ignore invalid channel */ /* if this layer is active then reshape it's window */ layer = &World[chanid-1]; /* locate target layer */ if (layer->allocated && layer->lpid) { layer->shape = *shape; /* install as our new shape */ SetWindowSize(layer); /* udpate the O/S window info */ } } /* ReceiveReshape() */ /* SetWindowSize() - tell O/S about new window size */ static void SetWindowSize(layer) struct Layer *layer; /* layer to resize */ { #ifdef TIOCSWINSZ struct winsize wsize; /* window size structure */ int retcode; /* ioctl return code */ wsize.ws_col = layer->shape.wchars; /* character width */ wsize.ws_row = layer->shape.wlines; /* window height */ wsize.ws_xpixel = 0; /* necessary? */ wsize.ws_ypixel = 0; /* update O/S window state */ retcode = ioctl(layer->ptyfd, TIOCSWINSZ, &wsize); DO DEBUG("SetWindowSize(chan %d) col %d, row %d iotcl() = %d\n", layer->chan, layer->shape.wchars, layer->shape.wlines, retcode); retcode = ioctl(layer->ptyfd, TIOCGWINSZ, &wsize); DO DEBUG("TIOCGWINSZ: col %d, row %d iotcl() = %d\n", wsize.ws_col, wsize.ws_row, retcode); #endif } /* SetWindowSize() */ /* ReceiveData() - received keyboard input for layer */ void ReceiveData(chanid, buff, cnt) int chanid; /* channel receiving input */ char *buff; /* buffer containing data */ int cnt; /* count of data bytes */ { struct Layer *layer; /* layer pointer */ struct Kbuff *kb; /* keybuff pointer */ DO DEBUG("ReceiveData(%d, '%.*s')\n", chanid, cnt, buff); /* verify channel */ if (chanid <= 0 || chanid > MAXPCHAN) return; /* ignore invalid channel */ layer = &World[chanid-1]; /* locate target layer */ /* add character stream to layer's input buffers for main loop processing */ for (kb=layer->kbuff; kb->next; kb=kb->next); /* find oldest buffer */ while (cnt--) { /* if current buffer full then chain in a new one */ if (kb->offset+kb->size >= IOSIZE) { kb->next = (struct Kbuff *) malloc(sizeof(struct Kbuff)); kb = kb->next; /* base new keybuff */ kb->next = NULL; /* no next yet */ kb->size = 0; /* this character is first */ kb->offset = 0; /* at zero offset */ } /* add new character to the end of this buffer */ kb->text[kb->offset+kb->size++] = *buff++; /* insert at end of data */ } } /* ReceiveData() */ /* InitWorld() - initialize layer structures */ static void InitWorld() { struct Layer *layer; /* layer pointer */ struct Kbuff *kb; /* keybuff pointer */ int i; /* work variable */ for (i=0; i<MAXPCHAN; i++) { layer = &World[i]; /* point to layer */ layer->chan = i+1; /* channel ID */ layer->allocated = 0; /* does not exist yet */ layer->lpid = 0; /* head process */ layer->ptyfd = 0; /* no pseduo pty yet */ layer->ptymask = 0; /* no pty mask yet */ layer->slot = 0; /* no Utmp slot */ ClearShape(&layer->shape); /* clear shape structure */ /* allocate the primary input keybuffer for this layer */ layer->kbuff = (struct Kbuff *) malloc(sizeof(struct Kbuff)); layer->kbuff->next = NULL; /* no next buffer */ layer->kbuff->size = 0; /* no data in buffer */ layer->kbuff->offset = 0; /* start filling at front */ } /* end for layer scan */ } /* InitWorld() */ /* clearshape() - initialize shape structure */ static void ClearShape(shape) struct Shape *shape; /* shape structure pointer */ { shape->worigv = 0; /* default window position */ shape->worigh = 0; shape->wlines = 0; /* default size */ shape->wchars = 0; shape->wfont = 0; /* default font size */ shape->wattr = 0; /* no attributes */ } /* clearshape() */ /* SigHandler() - process signals */ SigHandler () { DO DEBUG("GotSignal()\n"); while (GotSignal) { GotSignal = 0; DoWait (); /* handle dead or stopped children processes */ } } static void SigChld () { DO DEBUG("SigChld()\n"); /* flag child process is stopped or dead */ GotSignal = 1; } static void SigHup () { DO DEBUG("SigHup()\n"); /* Detach (0); */ FQuit(EXITSIGHUP); /* stop all processes */ /* NOT REACHED */ } /* DoWait() - send SIGCONT to stopped windows, Free dead process windows */ static DoWait() { register pid; register struct Layer *layer; union wait wstat; int i; DO DEBUG("DoWait()\n"); while ((pid = wait3 (&wstat, WNOHANG|WUNTRACED, NULL)) > 0) /* dead or stopped child process found */ for (i=0; i<MAXPCHAN; i++) if ((layer = &World[i])->lpid == pid) { if (WIFSTOPPED (wstat)) { /* stopped process so restart it */ /*** DO WE REALLY NEED TO DO THIS? ***/ DO DEBUG("killpg(, SIGCONT)\n"); KillPG(layer, SIGCONT); } else { /* remove dead process's layer */ DO DEBUG("kill dead process window %d\n", layer->chan); KillWindow (layer); /* tell MacLayers layer is dead */ SendDelete(layer->chan); } } } /* DoWait() */ /* KillPG() - send signal to layer's process group */ static void KillPG(layer, signal) struct Layer *layer; /* layer to signal */ int signal; /* signal to send */ { int retcode; /* work variable */ int pgrp; /* process group for layer */ int tgrp; /* terminal control process group */ DO DEBUG("KillPG(%d, sig %d)\n", layer->chan, signal); if (layer->lpid) { pgrp = getpgrp(layer->lpid); /* get process group */ DO DEBUG("getpgrp() = %d\n", pgrp); if (pgrp != -1) { retcode = killpg(pgrp, signal); /* signal it */ DO DEBUG("killpg() = %d\n", retcode); } /* After a lot of experimenting it was determined that csh ** creates a new terminal control group when it runs a command. ** Thus the above code failed to forward SIGINT to such commands. ** (Csh would get it but just ignore it.) Thus the following code. */ /* forward SIGINT to the terminal control group also */ if (signal == SIGINT) { retcode = ioctl(layer->ptyfd, TIOCGPGRP, &tgrp); DO DEBUG("ioctl(ptyfd,TIOCGPGRP) termcntlgrp %d, retcode = %d\n", tgrp, retcode); /* but only if not the same process group */ if (retcode != -1 && tgrp != pgrp) { retcode = killpg(tgrp, signal); /* signal it */ DO DEBUG("killpg(%d) = %d\n", tgrp, retcode); } } } } /* KillPG() */ /* KillWindow() - remove a layer from the system */ /* Note: This function does NOT tell MacLayers about the dead layer */ static KillWindow (layer) struct Layer *layer; { struct Kbuff *kb; /* work buffer free pointer */ if (layer->allocated) { /* SHOULD THIS BE SIGKILL ??? */ if (layer->lpid) /* if layer process started ... */ { KillPG(layer, SIGHUP); /* kill processes */ layer->lpid = 0; /* clear pid field */ } RemoveUtmp(layer->slot); (void) chmod(layer->tty, 0666); (void) chown(layer->tty, 0, 0); close(layer->ptyfd); DO DEBUG("chmod/chown %s, SendDelete(%d)\n",layer->tty, layer->chan); ClearShape(&layer->shape); /* reset the shape structure */ /* free all keybuffers but one and reprime it */ for (kb=layer->kbuff; kb->next; kb=kb->next) free(kb); /* free input buffers */ kb->size = 0; /* empty buffer */ kb->offset = 0; /* start refill from front */ layer->allocated = 0; /* window no longer allocated */ } } /* KillWindow() */ /* FAbort() - signal catcher for quitting */ static void FAbort() { DO DEBUG("FAbort()\n"); FQuit (EXITSIGQUIT); /* quit with error exit */ } /* FAbort() */ /* FQuit() - terminate layers */ void FQuit(exitcode) int exitcode; { int i; DO DEBUG("FQuit(%d)\n",exitcode); for (i=0; i<MAXPCHAN; i++) KillWindow(&World[i]); /* kill all windows */ DO DEBUG("SendQuit()\n"); SendQuit(); /* tell MacLayers to exit layers mode */ SetTTY (0, &OldMode); if (RealTtyMode) (void) chmod(RealTtyName, RealTtyMode); /* restore mode */ if (RealSlot) WriteUtmp(RealSlot, &RealUtmp); /* restore original login */ FinitTerm (); sleep(2); /* wait for port to reset */ printf ("[layers terminated]\n"); exit (exitcode); } /* FQuit() */ /* MakeWindow() - create new layer */ static MakeWindow (chan, prog, args, dir, lflag, shape) int chan; /* zero or channel to use for window */ char *prog; char **args; char *dir; int lflag; /* one if this to be logged in */ struct Shape *shape; /* shape to use for window */ { register struct Layer *layer; register char **cp; register f, j; int tf; int mypid; char ebuf[10]; DO DEBUG("MakeWindow(%d, %s, %s, dir %s, ", chan, prog, args[0], dir ? dir : "(none)"); DO DEBUG("login %d\n", lflag); DO DEBUG(" origv %d, origh %d, lines %d, chars %d, ", shape->worigv, shape->worigh, shape->wlines, shape->wchars); DO DEBUG("font %d, attr 0x%x\n", shape->wfont, shape->wattr); if ((f = OpenPTY ()) == -1) { Msg (0, "No more PTYs."); return ( -1 ); } /* if channel not given obtain one from MacLayers */ if (chan == 0) { chan = SendNew(shape); /* try to get free window */ if (chan == 0) { Msg (0, "No more windows."); return ( -1 ); } DO DEBUG("SendNew() == %d\n", chan); } /* verify channel */ if (chan <= 0 || chan > MAXPCHAN) { Msg(0, "Invalid channel %d.", chan); return ( -1 ); } /* login this window if it's layer #1 */ if (chan == 1) lflag = 1; if (lflag == -1) lflag = loginflag; #ifdef USRLIMIT /* * Count current number of users, and if logging windows in, */ if (lflag == 1) { (void) lseek (utmpf, 0, 0); UserCount = 0; while (read(utmpf, &utmpbuf, sizeof(struct utmp)) > 0) { if (utmpbuf.ut_name[0] != '\0') UserCount++; } if (UserCount >= USRLIMIT) { /* MUST NOT ISSUE MESSAGE SINCE IT GETS INTERSPERSED WITH ** PACKET SENT INDICATING CURRENT INPUT WINDOW!! THIS ** CAUSES IT TO BE CORRUPTED. (1.00 Bug Fix 001) */ /* Msg (0, "User limit reached. Window will not be logged in."); */ lflag = 0; } } #endif USRLIMIT layer = &World[chan-1]; /* find layer structure */ layer->shape = *shape; /* install new window shape */ /* ??? What do we do if layer is still active as far as we're concerned? */ if (layer->allocated) { DO DEBUG("??? newlayer not free !!!\n"); KillWindow(layer); /* kill off old layer */ SendDelete(layer->chan); /* kill window back off */ Msg (0, "Makewindow error: Duplicate active layer %d.", chan); return ( -1 ); /* return failed */ } layer->allocated = 1; /* show layer now in use */ BuildTitle(chan, prog, args); /* install window title */ (void) fcntl (f, F_SETFL, FNDELAY); layer->ptyfd = f; /* pseudo pty for task's I/O */ layer->ptymask = 1<<f; /* set pty device mask */ strncpy (layer->cmd, Filename (args[0]), MAXSTR-1); layer->cmd[MAXSTR-1] = '\0'; strncpy (layer->tty, TtyName, MAXSTR-1); DO DEBUG("forking %s, tty %s, ptyfd %d, mask %x\n", layer->cmd, layer->tty, layer->ptyfd, layer->ptymask); (void) chown (TtyName, getuid (), getgid ()); if (lflag == 1) { layer->slot = SetUtmp(TtyName, chan == 1); if (chan == 1 && RealTtyMode) /* set to original tty umask */ (void) chmod(TtyName, RealTtyMode); else /* force to this mode */ (void) chmod(TtyName, TtyMode); } else { layer->slot = -1; /* do not allow any other user access to this device */ (void) chmod (TtyName, 0600); } switch (layer->lpid = fork ()) { case -1: Msg (errno, "fork"); layer->lpid = 0; /* clear pid field */ return ( -1 ); /* return failed */ case 0: signal (SIGHUP, SIG_DFL); signal (SIGINT, SIG_DFL); signal (SIGQUIT, SIG_DFL); signal (SIGTERM, SIG_DFL); signal (SIGTTIN, SIG_DFL); signal (SIGTTOU, SIG_DFL); signal (SIGALRM, SIG_DFL); setuid (getuid ()); setgid (getgid ()); if (dir && chdir (dir) == -1) { SendErrorMsg ("Cannot chdir to %s: %s", dir, sys_errlist[errno]); exit (EXITABNORMAL); } mypid = getpid (); ioctl (DevTty, TIOCNOTTY, (char *)0); if ((tf = open (TtyName, O_RDWR)) == -1) { SendErrorMsg ("Cannot open %s: %s", TtyName, sys_errlist[errno]); exit (EXITABNORMAL); } DO DEBUG("Now in new process\n"); (void) dup2 (tf, 0); (void) dup2 (tf, 1); (void) dup2 (tf, 2); for (f = getdtablesize () - 1; f > 2; f--) close (f); ioctl (0, TIOCSPGRP, &mypid); (void) setpgrp (0, mypid); SetTTY (0, &OldMode); { struct winsize wsize; /* window size structure */ int retcode; /* ioctl return code */ wsize.ws_col = layer->shape.wchars; /* character width */ wsize.ws_row = layer->shape.wlines; /* window height */ wsize.ws_xpixel = 0; /* necessary? */ wsize.ws_ypixel = 0; /* update O/S window state */ retcode = ioctl(0, TIOCSWINSZ, &wsize); } (void) umask(Oumask); /* restore user's original umask */ NewEnv[2] = MakeTermcap(layer->shape.wlines, layer->shape.wchars); sprintf (ebuf, "LAYER=%d", chan); NewEnv[3] = ebuf; execvpe (prog, args, NewEnv); printf("%s: cannot exec %s: %s", myname, prog, sys_errlist[errno]); exit (EXITABNORMAL); } return ( chan ); } /* MakeWindow() */ static execvpe (prog, args, env) char *prog, **args, **env; { register char *path, *p; char buf[1024]; char *shargs[MAXARGS+1]; register i; register eaccess = 0; if (prog[0] == '/') path = ""; else if ((path = getenv ("PATH")) == 0) path = DefaultPath; do { p = buf; while (*path && *path != ':') *p++ = *path++; if (p > buf) *p++ = '/'; strcpy (p, prog); if (*path) ++path; execve (buf, args, env); switch (errno) { case ENOEXEC: shargs[0] = DefaultShell; shargs[1] = buf; for (i = 1; shargs[i+1] = args[i]; ++i); execve (DefaultShell, shargs, env); return; case EACCES: eaccess = 1; break; case ENOMEM: case E2BIG: case ETXTBSY: return; } /* end switch */ } while (*path); if (eaccess) errno = EACCES; } /* execvpe() */ /* BuildTitle() - create and install window title */ static void BuildTitle(chan, prog, args) int chan; /* channel for title */ char *prog; /* program being executed */ char **args; /* arg list */ { int i; /* arg scan index */ char buff[1024]; /* super huge title buffer */ /* skip any leading "/bin/" */ if (strncmp(prog, "/bin/", 5) == 0) strcpy(buff, prog+5); /* leave /bin off */ else strcpy(buff, prog); /* start with program name */ /* add all aguments but stop if option ("-") seen */ for (i=1; args[i] && args[i][0] != '-'; i++) { strcat(buff, " "); /* delimiter */ strcat(buff, args[i]); /* add next parameter */ } SendTitle(chan, buff, strlen(buff)); /* set new window title */ } /* BuildTitle() */ #ifdef sequent static OpenPTY () { char *m, *s; register f; f = getpseudotty (&s, &m); strncpy (PtyName, m, sizeof (PtyName)); strncpy (TtyName, s, sizeof (TtyName)); ioctl (f, TIOCFLUSH, (char *)0); return (f); } #else static OpenPTY () { register char *p, *l, *d; register i, f, tf; strcpy (PtyName, PtyProto); strcpy (TtyName, TtyProto); for (p = PtyName, i = 0; *p != 'X'; ++p, ++i); #ifndef SEQUENT for (l = "pqrstuvwxyzPQRST"; *p = *l; ++l) { for (d = "0123456789abcdef"; p[1] = *d; ++d) #else for (l = "pqrstuvwPQRSTUVW"; *p = *l; ++l) { for (d = "0123456789abcdef"; p[1] = *d; ++d) #endif { if ((f = open (PtyName, O_RDWR)) != -1) { TtyName[i] = p[0]; TtyName[i+1] = p[1]; if ((tf = open (TtyName, O_RDWR)) != -1) { close (tf); return f; } close (f); } } } return -1; } /* OpenPTY() */ #endif static SetTTY (fd, mp) struct mode *mp; { ioctl (fd, TIOCSETP, &mp->m_ttyb); ioctl (fd, TIOCSETC, &mp->m_tchars); ioctl (fd, TIOCSLTC, &mp->m_ltchars); ioctl (fd, TIOCLSET, &mp->m_lmode); ioctl (fd, TIOCSETD, &mp->m_ldisc); } /* SetTTY() */ static GetTTY (fd, mp) struct mode *mp; { ioctl (fd, TIOCGETP, &mp->m_ttyb); ioctl (fd, TIOCGETC, &mp->m_tchars); ioctl (fd, TIOCGLTC, &mp->m_ltchars); ioctl (fd, TIOCLGET, &mp->m_lmode); ioctl (fd, TIOCGETD, &mp->m_ldisc); } /* GetTTY() */ static SetMode (op, np) struct mode *op, *np; { *np = *op; #if 1 if (flowctl) { np->m_ttyb.sg_flags &= ~(CRMOD|ECHO); np->m_ttyb.sg_flags |= CBREAK | ANYP; } else np->m_ttyb.sg_flags = RAW | ANYP; #else np->m_ttyb.sg_flags &= ~(CRMOD|ECHO); np->m_ttyb.sg_flags |= CBREAK | ANYP; #endif np->m_tchars.t_intrc = -1; np->m_tchars.t_quitc = -1; if (!flowctl) { np->m_tchars.t_startc = -1; np->m_tchars.t_stopc = -1; } np->m_ltchars.t_suspc = -1; np->m_ltchars.t_dsuspc = -1; np->m_ltchars.t_flushc = -1; np->m_ltchars.t_lnextc = -1; } /* SetMode() */ static char * GetTtyName () { int n; char *p; for (p = 0, n = 0; n <= 2 && !(p = ttyname (n)); n++); if (!p || *p == '\0') Msg (0, "layers must run on a tty."); return ( p ); } /* GetTtyName() */ static Kill (pid, sig) { if (pid != 0) (void) kill (pid, sig); } /* GetSockName() - set SockName; if LTY env return 1 else 0 */ static GetSockName () { register client; static char buf[2*MAXSTR]; if (!mflag && (SockName = getenv ("LTY")) != 0 && *SockName != '\0') { client = 1; setuid (getuid ()); setgid (getgid ()); } else { sprintf (buf, "%s.%s", HostName, Filename (RealTtyName)); SockName = buf; client = 0; } return client; } /* GetSockName() */ static MakeServerSocket () { register s; struct sockaddr_un a; char *p; if ((s = socket (AF_UNIX, SOCK_STREAM, 0)) == -1) Msg (errno, "socket"); a.sun_family = AF_UNIX; strcpy (SockNamePtr, SockName); strcpy (a.sun_path, SockPath); if (connect (s, (struct sockaddr *)&a, strlen (SockPath)+2) != -1) { p = Filename (SockPath); Msg (0, "You already have a layers running on %s.", p); /*NOTREACHED*/ } DO DEBUG("MakeServerSocket: unlink(SockPath)/bind()/chown/listen\n"); (void) unlink (SockPath); if (bind (s, (struct sockaddr *)&a, strlen (SockPath)+2) == -1) Msg (errno, "bind"); (void) chown (SockPath, getuid (), getgid ()); if (listen (s, 5) == -1) Msg (errno, "listen"); return s; } /* MakeServerSocket() */ static MakeClientSocket (err) { register s; struct sockaddr_un a; if ((s = socket (AF_UNIX, SOCK_STREAM, 0)) == -1) Msg (errno, "socket"); a.sun_family = AF_UNIX; strcpy (SockNamePtr, SockName); strcpy (a.sun_path, SockPath); if (connect (s, (struct sockaddr *)&a, strlen (SockPath)+2) == -1) { if (err) { Msg (errno, "connect: %s", SockPath); } else { close (s); return -1; } } return s; } /* MakeClientSocket() */ static SendCreateMsg (s, ac, av, lflag, shape) char **av; struct Shape *shape; { struct msg m; register char *p; register len, n; char *pwd; /* PWD environment string */ DO DEBUG("SendCreateMsg(%d, ac %d, lflag %d\n", s, ac, lflag); m.type = MSG_CREATE; p = m.m.create.line; for (n = 0; ac > 0 && n < MAXARGS-1; ++av, --ac, ++n) { len = strlen (*av) + 1; if (p + len >= m.m.create.line+MAXLINE) break; strcpy (p, *av); p += len; } DO DEBUG(" nargs %d, create line = '%s'\n", n, m.m.create.line); m.m.create.nargs = n; m.m.create.lflag = lflag; m.m.create.shape = *shape; /* pass window shape */ /* Since Suns can hang up on getwd() [damn their stupid networking] ** we try to get the current working directory first from the PWD ** environment variable. */ if ((pwd=getenv("PWD")) && strlen(pwd) < 1024) (void) strcpy(m.m.create.dir, pwd); else if (getwd (m.m.create.dir) == 0) { DO DEBUG("getwd() failed!!\n"); Msg (0, "%s", m.m.create.dir); } DO DEBUG(" create.dir = '%s'\n", m.m.create.dir); if (write (s, (char *)&m, sizeof (m)) != sizeof (m)) { DO DEBUG(" write failed!!\n"); Msg (errno, "write"); } DO DEBUG("SendCreateMsg() done\n"); } /* SendCreateMsg() */ /*VARARGS1*/ static SendErrorMsg (fmt, p1, p2, p3, p4, p5, p6) char *fmt; { register s; struct msg m; s = MakeClientSocket (1); m.type = MSG_ERROR; sprintf (m.m.message, fmt, p1, p2, p3, p4, p5, p6); (void) write (s, (char *)&m, sizeof (m)); close (s); sleep (2); } static ReceiveMsg (s) { register ns; struct sockaddr_un a; int left, len = sizeof (a); struct msg m; char *p; DO DEBUG ("ReceiveMsg()\n"); if ((ns = accept (s, (struct sockaddr *)&a, &len)) == -1) { Msg (errno, "accept"); return; } p = (char *)&m; left = sizeof (m); while (left > 0 && (len = read (ns, p, left)) > 0) { p += len; left -= len; } close (ns); if (len == -1) Msg (errno, "read"); if (left > 0) return; switch (m.type) { case MSG_CREATE: DO DEBUG("MSG_CREATE:\n"); ExecCreate (&m); break; case MSG_ERROR: DO DEBUG("MSG_ERROR:\n"); Msg (0, "%s", m.m.message); break; default: Msg (0, "Invalid message (type %d).", m.type); } /* end switch */ } /* ReceiveMsg() */ static ExecCreate (mp) struct msg *mp; { char *args[MAXARGS]; register n; register char **pp = args, *p = mp->m.create.line; for (n = mp->m.create.nargs; n > 0; --n) { *pp++ = p; p += strlen (p) + 1; } *pp = 0; n = MakeWindow (0, mp->m.create.line, args, mp->m.create.dir, mp->m.create.lflag, &mp->m.create.shape); } /* ExecCreate() */ #if 0 static ReadRc (fn) char *fn; { FILE *f; register char *p, **pp, **ap; register argc, num, c; char buf[256]; char *args[MAXARGS]; int key; struct Shape shape; /* shape for new window */ ClearShape(&shape); /* initialize shape */ ap = args; if (access (fn, R_OK) == -1) return; if ((f = fopen (fn, "r")) == NULL) return; while (fgets (buf, 256, f) != NULL) { if (p = rindex (buf, '\n')) *p = '\0'; if ((argc = Parse (fn, buf, ap)) == 0) continue; if (strcmp (ap[0], "escape") == 0) { p = ap[1]; if (argc < 2 || strlen (p) != 2) Msg (0, "%s: two characters required after escape.", fn); Esc = *p++; MetaEsc = *p; ktab[Esc].type = KEY_OTHER; } else if (strcmp (ap[0], "login") == 0) { loginflag = 1; } else if (strcmp (ap[0], "unlogin") == 0) { loginflag = 0; } else if (strcmp (ap[0], "nologin") == 0) { loginflag = 0; } else if (strcmp (ap[0], "chdir") == 0) { p = argc < 2 ? home : ap[1]; if (chdir (p) == -1) Msg (errno, "%s", p); } else if (strcmp (ap[0], "mode") == 0) { if (argc != 2) { Msg (0, "%s: mode: one argument required.", fn); } else if (!IsNum (ap[1], 7)) { Msg (0, "%s: mode: octal number expected.", fn); } else (void) sscanf (ap[1], "%o", &TtyMode); } else if (strcmp (ap[0], "bell") == 0) { if (argc != 2) { Msg (0, "%s: bell: one argument required.", fn); } else { if ((BellString = malloc (strlen (ap[1]) + 1)) == 0) Msg (0, "Out of memory."); istrcpy (BellString, ap[1]); } } else if (strcmp (ap[0], "screen") == 0) { num = 0; if (argc > 1 && IsNum (ap[1], 10)) { num = atoi (ap[1]); if (num < 0 || num > MAXWIN-1) Msg (0, "%s: illegal screen number %d.", fn, num); --argc; ++ap; } if (argc < 2) { ap[1] = ShellProg; argc = 2; } ap[argc] = 0; (void) MakeWindow (0, ap[1], ap+1, (char *)0, loginflag, &shape); } else if (strcmp (ap[0], "bind") == 0) { p = ap[1]; if (argc < 2 || *p == '\0') Msg (0, "%s: key expected after bind.", fn); if (p[1] == '\0') { key = *p; } else if (p[0] == '^' && p[1] != '\0' && p[2] == '\0') { c = p[1]; if (isupper (c)) p[1] = tolower (c); key = Ctrl(c); } else if (IsNum (p, 7)) { (void) sscanf (p, "%o", &key); } else { Msg (0, "%s: bind: character, ^x, or octal number expected.", fn); } ktab[key].lflag = loginflag; if (argc < 3) { ktab[key].type = 0; } else { for (pp = KeyNames; *pp; ++pp) if (strcmp (ap[2], *pp) == 0) break; if (*pp) { ktab[key].type = pp-KeyNames+1; } else { ktab[key].type = KEY_CREATE; ktab[key].args = SaveArgs (argc-2, ap+2); } } } else Msg (0, "%s: unknown keyword \"%s\".", fn, ap[0]); } (void) fclose (f); } /* ReadRc() */ static Parse (fn, buf, args) char *fn, *buf, **args; { register char *p, **ap; register delim, argc; p = buf; ap = args; argc = 0; for (;;) { while (*p && (*p == ' ' || *p == '\t')) ++p; if (*p == '\0' || *p == '#') return argc; if (argc > MAXARGS-1) Msg (0, "%s: too many tokens.", fn); delim = 0; if (*p == '"' || *p == '\'') { delim = *p; *p = '\0'; ++p; } ++argc; *ap = p; ++ap; while (*p && !(delim ? *p == delim : (*p == ' ' || *p == '\t'))) ++p; if (*p == '\0') { if (delim) Msg (0, "%s: Missing quote.", fn); else return argc; } *p++ = '\0'; } } /* Parse() */ static char ** SaveArgs (argc, argv) register argc; register char **argv; { register char **ap, **pp; if ((pp = ap = (char **)malloc ((argc+1) * sizeof (char **))) == 0) Msg (0, "Out of memory."); while (argc--) { if ((*pp = malloc (strlen (*argv)+1)) == 0) Msg (0, "Out of memory."); strcpy (*pp, *argv); ++pp; ++argv; } *pp = 0; return ap; } /* SaveArgs() */ #endif static MakeNewEnv () { register char **op, **np = NewEnv; static char buf[MAXSTR]; if (strlen (SockName) > MAXSTR-5) SockName = "?"; sprintf (buf, "LTY=%s", SockName); *np++ = buf; *np++ = Term; np += 2; for (op = environ; *op; ++op) { if (np == NewEnv + MAXARGS - 1) break; if ( !IsSymbol (*op, "TERM") && !IsSymbol (*op, "TERMCAP") && !IsSymbol (*op, "LTY") ) *np++ = *op; } *np = 0; } /* MakeNewEnv() */ static IsSymbol (e, s) register char *e, *s; { register char *p; register n; for (p = e; *p && *p != '='; ++p); if (*p) { *p = '\0'; n = strcmp (e, s); *p = '='; return n == 0; } return 0; } /* IsSymbol() */ /*VARARGS2*/ Msg (err, fmt, p1, p2, p3, p4, p5, p6) char *fmt; { char buf[1024]; register char *p = buf; sprintf (p, fmt, p1, p2, p3, p4, p5, p6); if (err) { p += strlen (p); if (err > 0 && err < sys_nerr) sprintf (p, ": %s", sys_errlist[err]); else sprintf (p, ": Error %d", err); } if (!Abortonmsg) { /* MakeStatus (buf, curr);*/ printf("%s\r\n", buf); } else { printf ("%s\r\n", buf); if (Resetenvonmsg) {SetTTY (0, &OldMode); if (RealTtyMode) (void) chmod(RealTtyName, RealTtyMode); /* restore mode */ if (RealSlot) WriteUtmp(RealSlot, &RealUtmp); /* restore original login */ FinitTerm (); } exit(exitcode); } } /* Msg() */ static char * Filename (s) char *s; { register char *p; p = s + strlen (s) - 1; while (p >= s && *p != '/') --p; return ++p; } /* Filename() */ static IsNum (s, base) register char *s; register base; { for (base += '0'; *s; ++s) if (*s < '0' || *s > base) return 0; return 1; } /* IsNum() */ static InitUtmp () { if ((utmpf = open (UtmpName, O_RDWR)) == -1) { if (errno != EACCES) Msg (errno, UtmpName); return; } utmp = 1; } /* InitUtmp() */ static int FindUtmp(name) char *name; { register char *p; register struct ttyent *tp; register slot; DO DEBUG("FindUtmp(%s)\n", name); slot = 1; if (!utmp) return 0; if (p = rindex (name, '/')) ++p; else p = name; setttyent (); while ( (tp = getttyent ()) != NULL && strcmp (p, tp->ty_name) != 0 ) ++slot; if (tp == NULL) return 0; DO DEBUG(" slot %d\n", slot); return slot; } /* FindUtmp() */ static int SetUtmp (name, mainlogin) char *name; /* tty name */ int mainlogin; /* this is primary login */ { register char *p; register slot; struct utmp u; if ((slot=FindUtmp(name)) == 0) return ( 0 ); if (p = rindex (name, '/')) ++p; else p = name; strncpy (u.ut_line, p, 8); strncpy (u.ut_name, LoginName, 8); #if 1 strncpy(u.ut_host, Filename (RealTtyName), 16); /* host is real tty */ #else u.ut_host[0] = '\0'; #endif if (RealSlot && mainlogin) u.ut_time = RealUtmp.ut_time; /* use original login time */ else time (&u.ut_time); (void) lseek (utmpf, (long)(slot * sizeof (u)), 0); (void) write (utmpf, (char *)&u, sizeof (u)); return ( slot ); } /* SetUtmp() */ static int ReadUtmp(slot, entry) int slot; /* slot to read */ struct utmp *entry; /* entry to read into */ { int cnt; /* return count */ if (!utmp) return; /* no utmp access */ (void) lseek(utmpf, (long)(slot * sizeof(struct utmp)), 0); cnt = read(utmpf, (char *)entry, sizeof(struct utmp)); DO DEBUG("ReadUtmp cnt %d, errno %d, line %.8s, name %.8s, host %.16s\n", cnt, errno, entry->ut_line, entry->ut_name, entry->ut_host); return ( cnt ); } /* ReadUtmp() */ static void WriteUtmp(slot, entry) int slot; /* slot to write */ struct utmp *entry; /* entry to write from */ { int cnt; /* write return code */ if (!utmp) return; /* no utmp access */ (void) lseek(utmpf, (long)(slot * sizeof(struct utmp)), 0); cnt = write(utmpf, (char *)entry, sizeof(struct utmp)); DO DEBUG("WriteUtmp() slot %d cnt %d line %.8s name %.8s host %.16s\n", slot, cnt, entry->ut_line, entry->ut_name, entry->ut_host); } /* WriteUtmp() */ static RemoveUtmp (slot) { struct utmp u; if (slot) { bzero ((char *)&u, sizeof (u)); (void) lseek (utmpf, (long)(slot * sizeof (u)), 0); (void) write (utmpf, (char *)&u, sizeof (u)); } } /* RemoveUtmp() */ #ifndef GETTTYENT static setttyent () { struct stat s; register f; register char *p, *ep; if (ttnext) { ttnext = tt; return; } if ((f = open (ttys, O_RDONLY)) == -1 || fstat (f, &s) == -1) Msg (errno, ttys); if ((tt = malloc (s.st_size + 1)) == 0) Msg (0, "Out of memory."); if (read (f, tt, s.st_size) != s.st_size) Msg (errno, ttys); close (f); for (p = tt, ep = p + s.st_size; p < ep; ++p) if (*p == '\n') *p = '\0'; *p = '\0'; ttnext = tt; } /* setttyent() */ static struct ttyent * getttyent () { static struct ttyent t; if (*ttnext == '\0') return NULL; t.ty_name = ttnext + 2; ttnext += strlen (ttnext) + 1; return &t; } /* getttyend() */ #endif /* FinitTerm() - reset vt100 terminal */ static void FinitTerm () { /* print out termcap 'is' string to reset terminal */ #if 0 /* This string sets scroll region 1-24 and puts cursor at bottom line */ printf("\033[1;24r\033[24;1H"); #endif fflush(stdout); } static void AddCap (s) char *s; { register n; if (tcLineLen + (n = strlen (s)) > 55) { strcat (Termcap, "\\\n\t:"); tcLineLen = 0; } strcat (Termcap, s); tcLineLen += n; } static char * MakeTermcap(lines, chars) int lines; /* default window lines */ int chars; /* default window chars */ { char buf[1024]; register char **pp, *p; strcpy(Termcap, TermcapConst1); /* start TERMCAP build */ strcat(Termcap, UserTerm); /* fill in User's terminal type */ strcat(Termcap, TermcapConst3); /* finish our own definition */ /* add more capabilities depending upon client version */ if (Protocollevel > 1) strcat(Termcap, TermcapConst101); if (lines <= 0 || lines > 200) lines = rows; /* force default if none or invalid */ if (chars <= 0 || chars > 300) chars = cols; /* force default if none or invalid */ sprintf(buf, "li#%d:co#%d:", lines, chars); AddCap(buf); return ( Termcap ); } /* MakeTermcap() */ /* DEBUG() - dump output routine */ void DEBUG(format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) char *format; int arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8; { fprintf(stderr, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); } @//E*O*F layers_1.10/layers.c// chmod u=rw,g=r,o=r layers_1.10/layers.c echo x - layers_1.10/layers.h sed 's/^@//' > "layers_1.10/layers.h" <<'@//E*O*F layers_1.10/layers.h//' /* Copyright (C) 1989 by David W. Trissel * * Not derived from licensed software. * * Permission is granted to freely use, copy, modify, and redistribute * this software, provided that no attempt is made to gain profit from it, * the author is not construed to be liable for any results of using the * software, alterations are clearly marked as such, and this notice is * not modified. * */ #define HOSTPROTOCOL 2 /* current host protocol */ #define MAXPCHAN 7 /* maximum layers supported */ #define MAXSTR 200 #define MAXARGS 64 #define MAXLINE 1024 #define IOSIZE 800 /* data gulp handling size */ /* WARNING - packet sizes must be insured to never match the ESCAPE char */ #define ESCAPE '}' /* datalink escape character */ #define DO if (Dflag) /* for debugging */ /* miscelaneous common data */ extern int Dflag; /* debug dump indicator flag */ extern int Clientversion; /* client version number */ extern int Protocollevel; /* effective protocol level */ extern int Clientlevel; /* client's protocol level */ /* Shape structure passed between MacLayers and ourselves */ struct Shape { short worigv; /* verical window bit origin */ short worigh; /* horizontal window bit origin */ short wlines; /* window height */ short wchars; /* window width */ short wfont; /* window font size */ short wattr; /* window attributes */ }; #define Wa_shell 0x01 /* window is a shell */ /* The following modules define the complete protocol/server interface */ /* layers.c */ extern void FQuit(/* exitcode */); extern void ReceiveQuit(); extern void ReceiveNew(/* chanid, shape */); extern void ReceiveDelete(/* chanid */); extern void ReceiveSignal(/* chanid, signal */); extern void ReceiveData(/* chanid, buff, cnt */); extern void ReceiveReshape(/*chanid, shape */); extern void DEBUG(/* format, arg1, arg2, arg3, arg4 */); /* protocol.c */ extern int InitLink(); extern int TopChannel(); extern int SendNew(/* shape */); extern void SendTitle(/* chan, buff, cnt */); extern void SendDelete(/* chan */); extern void SendQuit(); extern void SendReshape(/* chan, shape */); extern void SendData(/* chan, buff, cnt */); extern void ProcessStreamin(); @//E*O*F layers_1.10/layers.h// chmod u=rw,g=r,o=r layers_1.10/layers.h echo x - layers_1.10/layersize.c sed 's/^@//' > "layers_1.10/layersize.c" <<'@//E*O*F layers_1.10/layersize.c//' /* Copyright (C) 1989 by David W. Trissel * * Not derived from licensed software. * * Permission is granted to freely use, copy, modify, and redistribute * this software, provided that no attempt is made to gain profit from it, * the author is not construed to be liable for any results of using the * software, alterations are clearly marked as such, and this notice is * not modified. * */ /* All rights reserved. */ /* layersize - update BSD Sun Unix with window size information */ #include <stdio.h> #include <errno.h> #include <sys/ioctl.h> extern int sys_nerr; extern char *sys_errlist[]; static void gotsyserr(/* char * */); static void goterr(/* char * */); static int getnumber(/* char * */); /* main() - update BSD window size */ main(ac, av) int ac; /* argument count */ char **av; /* argument vector */ { struct winsize wsize; /* window size structure for ioctl() */ char *ap; /* argument scan pointer */ int lines; /* new lines value */ int cols; /* new columns value */ if (--ac != 2) goterr("Missing lines and column options"); /* get window size (actually do this to set xpixel and ypixel values) */ if (ioctl(0, TIOCGWINSZ, &wsize) == -1) gotsyserr("No window support in host"); /* terminate with message */ /* scan looking for -l and -c line and column numeric sizes */ lines = cols = 0; /* reset values */ while (ac > 0) { ap = *++av; /* point to next argument string */ if (ac-- > 0 && *ap == '-') /* if option ... */ switch (ap[1]) { case 'l': /* lines */ lines = getnumber(&ap[2]); break; case 'c': /* columns */ cols = getnumber(&ap[2]); break; default: goterr("Usupported option"); /* unsupported option */ break; } /* end '-' argument */ else goterr("Unsupported parameter"); /* unsupported parameter */ } /* end while argument vector scan */ /* must have both lines and columns */ if (lines == 0 || cols == 0) goterr("Must specify both lines and columns"); wsize.ws_col = cols; /* set columns */ wsize.ws_row = lines; /* set lines */ /* update the kernel */ if (ioctl(0, TIOCSWINSZ, &wsize) == -1) gotsyserr("Failed to update window size"); /* didn't go */ } /* goterr() - issue error and terminate */ static void goterr(msg) char *msg; /* error message string */ { printf("%s\n", msg); /* send error message to user */ exit(1); /* terminate with error */ } /* goterr() */ /* gotsyserror() - system error return */ static void gotsyserr(msg) char *msg; /* error string */ { if (errno > 0 && errno < sys_nerr) printf("%s: %s\n", msg, sys_errlist[errno]); else printf("%s: Error %d\n", msg, errno); exit(1); /* exit with failure */ } /* gotsyserr() */ /* getnumber() - parse option number */ static int getnumber(str) char *str; /* start of option string */ { int n; /* number being built */ if (str == NULL) goterr("Invalid numeric in option"); /* skip any leading delimiters */ while (*str && (*str == ' ' || *str == '\t')) str++; for(n=0; *str && *str >= '0' && *str <= '9'; str++) n = n*10 + *str - '0'; /* add next digit in */ /* make sure number terminates legally */ switch (*str) { case '\0': case ' ': case '\t': case '\n': if (n <= 0 || n > 200) goterr("Number out of range"); break; /* these are OK */ default: goterr("Invalid numeric in option"); } /* end switch */ return ( n ); /* return the number */ } /* getnumber() */ @//E*O*F layers_1.10/layersize.c// chmod u=rw,g=r,o=r layers_1.10/layersize.c echo x - layers_1.10/layertitle.c sed 's/^@//' > "layers_1.10/layertitle.c" <<'@//E*O*F layers_1.10/layertitle.c//' /* Copyright (C) 1989 by David W. Trissel * * Not derived from licensed software. * * Permission is granted to freely use, copy, modify, and redistribute * this software, provided that no attempt is made to gain profit from it, * the author is not construed to be liable for any results of using the * software, alterations are clearly marked as such, and this notice is * not modified. * */ /* All rights reserved. */ /* layertitle - utility to specify window title */ #include <stdio.h> #define ESC 0x1b /* main() - send string designating layers window title */ main(ac, av) int ac; /* argument count */ char **av; /* argument vector */ { char *ap; /* argument scan pointer */ if (--ac != 1) { printf("usage: layertitle \"new window title\"\n"); exit(1); } ap = *++av; /* point to argument string */ /* Transmit the title string in the ANSI Private Message format ** which is ** ESC '^' message ESC '\' */ printf("%c%c%s%c%c", ESC, '^', ap, ESC, '\\'); } /* main() */ @//E*O*F layers_1.10/layertitle.c// chmod u=rw,g=r,o=r layers_1.10/layertitle.c echo x - layers_1.10/macbput.1 sed 's/^@//' > "layers_1.10/macbput.1" <<'@//E*O*F layers_1.10/macbput.1//' @.TH MACBPUT local "17 Mar 1990" @.UC 4 @.SH NAME macbput \- send file to macintosh via macbinary protocol @.SH SYNOPSIS @.B macbput file @.br @.B macbput [ @.B \-rdu ] file [ @.B \-t type ] [ @.B \-c owner ] [ @.B \-n name ] @.SH DESCRIPTION @.I Macbput sends a file to a Macintosh running MacTerminal. The File Transfer Protocol settings should specify the "MacBinary" transfer method. Macbput will also work with other communications programs for the Mac (e.g. MacLayers); consult the user's manual for your macbinary-compatable communications program for more information. This manual page will only address the use of macbput with MacTerminal. @.PP To use this program, log into the unix system using MacTerminal, and run macbput specifying the desired options and one file to be sent. If MacTerminal is properly configured, it will recognize that a file is arriving on the serial line and put up an indicator showing how much of the file has been sent. Several Control-X's may be used to force macbput to give up if the transfer fails. @.PP If none of the @.B \-rdu flags are specified, @.I macbput sends three unix files to the Mac: @.IB file .info , @.IB file .data , and @.IB file .rsrc . These specify the three parts of one Mac file: the .data file becomes the data fork, the .rsrc file becomes the resource fork, and the .info file specifies the sizes of the two forks, as well as the file name, file type, creation date, and other information. This is useful for returning files to the Mac which were stored using macget or macbget. @.PP The @.B \-r flag specifies @.I resource mode. Either @.IB file .rsrc or @.I file will be sent to the Mac, along with a forged @.B .info file and an empty @.B .data file. The file sent becomes the resource fork of the Mac file. @.PP The @.B \-d flag specifies @.I data mode. Either @.IB file .data , @.IB file .text or @.I file will be sent to the Mac, along with a forged @.B .info file and an empty @.B .rsrc file. The file sent becomes the data fork of the Mac file. @.PP The @.B \-u flag requests @.I unix mode, which is the same as @.I data mode except unix newline characters are converted into carriage returns. Human-readable unix text files sent to the Mac using this option will be compatible with applications which expect "text only" files. @.PP The remaining options serve to override the default file type, owner, and file name to be used on the Mac. The default type and owner for @.I resource and @.I data mode defaults are "????", "????", and @.I unix mode defaults are "TEXT" and "MACA". @.SH SEE ALSO macput(local), macget(local), xbin(local), macbin(local), mcvert(local) @.SH BUGS The macbinary protocol may not work over flow controlled communication lines, some terminal concentrators, or when using rlogin. @.SH FEATURES Properly initializes the Creation Date. @.SH AUTHOR Original base code: Dave Johnson, Brown 7/31/84 @//E*O*F layers_1.10/macbput.1// chmod u=rw,g=r,o=r layers_1.10/macbput.1 echo x - layers_1.10/macbput.c sed 's/^@//' > "layers_1.10/macbput.c" <<'@//E*O*F layers_1.10/macbput.c//' /* * macbput.c * * This is a modified version of Dave Johnson's macput (see Copyright * below) intended for use with MacLayers v1.20 */ /* * (originally macput) -- send file to Macintosh using MacBinary XMODEM protocol * Dave Johnson, Brown University Computer Science * * (c) 1984 Brown University * may be used but not sold without permission * */ /* To compile: cc -O -o macbput macbput.c (Sun 4.2 BSD) cc -O -DSUNBSD42 -o macbput macbput.c (System V) cc -O -DSYSV -o macbput macbput.c Latest modification 1/93 by Eric Rosen to be used with MacLayers v1.20 1. inserted pad byte to be compatible with THINK C's word alignment requirements in header; also added identification character as temporary hack for version control 2. inserted helpful examples in the usage string when no arguments are given Latest modification 4/27/91 by Trissel- 1. Original code was absolutely pathetic recovering from noise on the line. In many cases noise on the line would cause program to either hang indefinetly or abort instantly. The code to examine responses has been enhanced to overcome these limitations. Modification 4/17/91 by Trissel - 1. ??? XMODEM block count not fixed in official MacLayers Release??? Point 4 below claims that it was, however the code obviously does NOT increment the last block count of the data fork if the data fork is not an even multiple of 128 bytes in size. Modifications 10/20/88 by Trissel - 1. General cleanup by removal of unused definitions and headers. 2. Added #ifdefs to support System V and BSD 4.2 Sun compilation. 3. Removed ancient Macterminal Beta 0.5X code. 4. Fixed bad bug where XMODEM block count was not bumped up after the first fork transfer. Dave Trissel Motorola Inc. ut-sally!oakhill!davet This code is fundamentally from two earlier programmers: Jon Hueras Symantec/THINK Technologies singer@endor.harvard.edu who added 2-Byte CRC capability to code from: Dave Johnson ddj%brown@csnet-relay.arpa Brown University Computer Science who did the initial MacTerminal 1.1 transfer protocol. */ /* If you have System V define the following: */ /* #define SYSV */ /* Sun BSD 4.2 systems should define the following: */ /* #define SUNBSD42 */ #include <stdio.h> #include <signal.h> #include <setjmp.h> #ifdef SYSV #include <termio.h> #else #include <sgtty.h> #endif #include <sys/types.h> #include <sys/stat.h> #ifdef SUNBSD42 /* RAW is no longer being found on latest Sun system (??) (Trissel) */ #define RAW 0x20 #endif #define RECORDBYTES 132 #define DATABYTES 128 #define NAMEBYTES 64 #define RETRIES 10 #define ACKTIMO 10 #define FLUSHTIMO 2 #define MAXRECNO 0xff #define BYTEMASK 0xff #define TMO -1 #define DUP '\000' #define SOH '\001' #define EOT '\004' #define ACK '\006' #define NAK '\025' #define CAN '\030' #define EEF '\032' #define ESC '\033' #define H_NLENOFF 1 #define H_NAMEOFF 2 /* 65 <-> 80 is the FInfo structure */ #define H_TYPEOFF 65 #define H_AUTHOFF 69 #define H_LOCKOFF 81 #define H_DLENOFF 83 #define H_RLENOFF 87 #define H_CTIMOFF 91 #define H_MTIMOFF 95 #define H_OLD_DLENOFF 81 #define H_OLD_RLENOFF 85 #define TEXT 0 #define DATA 1 #define RSRC 2 #define FULL 3 int mode, txtmode; struct macheader { char m_name[NAMEBYTES+1]; char m_type[4]; char m_author[4]; long m_datalen; long m_rsrclen; long m_createtime; long m_modifytime; } mh; struct filenames { char f_info[256]; char f_data[256]; char f_rsrc[256]; } files; int recno, crc; char buf[DATABYTES]; char usage[] = "macbput v1.20\nusage: \"macbput [-rdu] [-t type] [-c creator] [-n name] filename\"\n"; main(ac, av) char **av; { int n; char *filename; if (ac == 1) { fprintf(stderr, usage); fprintf(stderr, "Eg:\n\tTo send a binary file:\n"); fprintf(stderr, "\n\t\tmacbput -d filename\n"); fprintf(stderr, "\n\tTo send a text file:\n"); fprintf(stderr, "\n\t\tmacbput -d filename -t TEXT\n"); exit(1); } mode = FULL; ac--; av++; while (ac) { if (av[0][0] == '-') { switch (av[0][1]) { case 'r': mode = RSRC; strncpy(mh.m_type, "????", 4); strncpy(mh.m_author, "????", 4); break; case 'u': mode = TEXT; strncpy(mh.m_type, "TEXT", 4); strncpy(mh.m_author, "MACA", 4); break; case 'd': mode = DATA; strncpy(mh.m_type, "????", 4); strncpy(mh.m_author, "????", 4); break; case 'n': if (ac > 1) { ac--; av++; n = strlen(av[0]); if (n > NAMEBYTES) n = NAMEBYTES; strncpy(mh.m_name, av[0], n); mh.m_name[n] = '\0'; break; } else goto bad_usage; case 't': if (ac > 1) { ac--; av++; strncpy(mh.m_type, av[0], 4); break; } else goto bad_usage; case 'c': if (ac > 1) { ac--; av++; strncpy(mh.m_author, av[0], 4); break; } else goto bad_usage; default: bad_usage: fprintf(stderr, usage); exit(1); } } else { filename = av[0]; } ac--; av++; } setup_tty(); find_files(filename, mode); if (mode != FULL) forge_info(); if (send_sync()) { recno = 1; txtmode = 0; send_file(files.f_info, 1, '*'); if (mode != FULL) unlink(files.f_info); if (mode == TEXT) txtmode++; send_file(files.f_data, 1, '\0'); txtmode = 0; send_file(files.f_rsrc, 0, '\0'); } reset_tty(); } find_files(filename, mode) char *filename; { int n, tdiff; struct stat stbuf; sprintf(files.f_data, "%s.data", filename); sprintf(files.f_rsrc, "%s.rsrc", filename); if (mode == FULL) { sprintf(files.f_info, "%s.info", filename); if (stat(files.f_info, &stbuf) != 0) { perror(files.f_info); cleanup(-1); } return; } else { strcpy(files.f_info, "#machdrXXXXXX"); mktemp(files.f_info); } if (mode == RSRC) { strcpy(files.f_data, "/dev/null"); if (stat(files.f_rsrc, &stbuf) != 0) { strcpy(files.f_rsrc, filename); if (stat(files.f_rsrc, &stbuf) != 0) { perror(files.f_rsrc); cleanup(-1); } } mh.m_datalen = 0; mh.m_rsrclen = stbuf.st_size; } else { strcpy(files.f_rsrc, "/dev/null"); if (stat(files.f_data, &stbuf) != 0) { sprintf(files.f_data, "%s.text", filename); if (stat(files.f_data, &stbuf) != 0) { strcpy(files.f_data, filename); if (stat(files.f_data, &stbuf) != 0) { perror(files.f_data); cleanup(-1); } } } mh.m_datalen = stbuf.st_size; mh.m_rsrclen = 0; } if (mh.m_name[0] == '\0') { n = strlen(filename); if (n > NAMEBYTES) n = NAMEBYTES; strncpy(mh.m_name, filename, n); mh.m_name[n] = '\0'; } } forge_info() { int n; char *np; FILE *fp; for (np = mh.m_name; *np; np++) if (*np == '_') *np = ' '; buf[H_NLENOFF] = n = np - mh.m_name; strncpy(buf + H_NAMEOFF, mh.m_name, n); strncpy(buf + H_TYPEOFF, mh.m_type, 4); strncpy(buf + H_AUTHOFF, mh.m_author, 4); put4(buf + H_DLENOFF, mh.m_datalen); put4(buf + H_RLENOFF, mh.m_rsrclen); put4(buf + H_CTIMOFF, mh.m_createtime); put4(buf + H_MTIMOFF, mh.m_modifytime); fp = fopen(files.f_info, "w"); if (fp == NULL) { perror("temp file"); cleanup(-1); } fwrite(buf, 1, DATABYTES, fp); fclose(fp); } send_sync() { int c; tputc(ESC); tputc('b'); for (;;) { if ((c = tgetc(ACKTIMO)) == TMO) { return(0); } if (c == NAK) { return(1); } if (c == 'C') { crc++; return(1); } } } send_file(fname, more, id) char *fname; int more; char id; { register int status, i, n; FILE *inf; inf = fopen(fname, "r"); if (inf == NULL) { perror(fname); cleanup(-1); } for (;;) { n = fread(buf, 1, DATABYTES, inf); if (id == '*') { buf[0] = id; for (i=127; i>65; i--) buf[i] = buf[i-1]; /* hack */ } if (n > 0) { for (i = 0; i < RETRIES; i++) { send_rec(buf, DATABYTES); status = tgetc(ACKTIMO); if ( status != ACK && status != NAK && status != CAN && status != TMO && status != EOT ) { /* We got unknown response - assume noise on the line ** and flush it. Treat this as a timeout. */ /* Note: the FLUSHTIMO must be short, otherwise we ** would flush past the (presumed) noise burst and ** possibly miss a CAN which would leave the program ** timing out while the user wonders what happened. ** (Missing anything else is no big deal since we ** are going to retry anyway.) */ while ((status=tgetc(FLUSHTIMO)) != TMO) ; /* leave status == TMO */ } /* exit retry loop if not to resend */ if (status == ACK || status == EOT || status == CAN) break; } if (status != ACK) { /* we are to terminate */ fclose(inf); cleanup(-1); /* NOTREACHED */ } /* FIX (d.t.) recno always increments for any block sent out */ recno++; /* FIX d.t. */ recno &= MAXRECNO; /* FIX d.t. */ } if (n < DATABYTES) { if (!more) { tputc(EOT); tgetc(ACKTIMO); } return; } } } send_rec(buf, recsize) char buf[]; int recsize; { int i, cksum; char *bp; if (txtmode || !crc) { cksum = 0; bp = buf; for (i = 0; i < recsize; i++, bp++) { if (txtmode && *bp == '\n') *bp = '\r'; cksum += *bp; } } if (crc) cksum = calcrc(buf, recsize); tputc(SOH); tputc((char) recno); tputc((char) (MAXRECNO - recno)); tputrec(buf, recsize); if (crc) { tputc((char) (cksum >> 8)); tputc((char) cksum); } else tputc((char) cksum); } static int ttyfd; static FILE *ttyf; static jmp_buf timobuf; tgetc(timeout) int timeout; { int c; if (setjmp(timobuf)) return TMO; alarm(timeout); c = getc(ttyf); alarm(0); if (c == -1) /* probably hung up or logged off */ return EOT; else return c & BYTEMASK; } tputrec(buf, count) char *buf; int count; { write(ttyfd, buf, count); } tputc(c) char c; { write(ttyfd, &c, 1); } timedout() { signal(SIGALRM, timedout); /* for pre-4.2 systems */ longjmp(timobuf, 1); } #ifdef SYSV static struct termio otty, ntty; #else static struct sgttyb otty, ntty; #endif /* should turn messages off */ setup_tty() { int cleanup(); int timedout(); ttyf = stdin; ttyfd = fileno(stdout); #ifdef SYSV ioctl(ttyfd, TCGETA, &otty); /* get termio info */ #else ioctl(ttyfd, TIOCGETP, &otty); #endif signal(SIGHUP, cleanup); signal(SIGINT, cleanup); signal(SIGQUIT, cleanup); signal(SIGTERM, cleanup); signal(SIGALRM, timedout); ntty = otty; #ifdef SYSV ntty.c_iflag = BRKINT; /* only interrupt on break */ ntty.c_oflag = 0; /* no output processing */ ntty.c_cflag |= CS8; /* 8 bit characters */ ntty.c_lflag = 0; /* no echoing */ ntty.c_cc[VEOF] = 1; /* "MIN" minimum chars before input */ ntty.c_cc[VEOL] = 1; /* "TIME" maximum .1 secs before feed */ ioctl(ttyfd, TCSETAF, &ntty); /* set mode and flush input */ #else ntty.sg_flags = RAW; ioctl(ttyfd, TIOCSETP, &ntty); #endif } reset_tty() { if (ttyf != NULL) { #ifdef SYSV ioctl(ttyfd, TCSETAF, &otty); /* reset after output drains */ #else sleep (5); /* wait for output to drain */ ioctl(ttyfd, TIOCSETP, &otty); #endif } } cleanup(sig) int sig; { reset_tty(); exit(sig); } put4(bp, value) char *bp; long value; { register int i, c; for (i = 0; i < 4; i++) { c = (value >> 24) & BYTEMASK; value <<= 8; *bp++ = c; } } int calcrc(ptr, count) char *ptr; int count; { int crc, i; crc = 0; while (--count >= 0) { crc ^= ((int) *ptr++) << 8; for (i = 0; i < 8; ++i) if (crc & 0x8000) crc = crc << 1 ^ 0x1021; else crc <<= 1; } return (crc & 0xFFFF); } @//E*O*F layers_1.10/macbput.c// chmod u=rw,g=r,o=r layers_1.10/macbput.c echo x - layers_1.10/macbput.h sed 's/^@//' > "layers_1.10/macbput.h" <<'@//E*O*F layers_1.10/macbput.h//' /** *** Copyright (C) 1991 by David W. Trissel *** *** Not derived from licensed software. *** *** Permission is granted to freely use, copy, modify, and redistribute *** this software, provided that no attempt is made to gain profit from it, *** the author is not construed to be liable for any results of using the *** software, alterations are clearly marked as such, and this notice is *** not modified. *** #define RECORDBYTES 132 /* complete XMODEM block length */ #define DATABYTES 128 /* data portion of block length */ #define NAMEBYTES 63 /* max file name size in info block */ #define RETRIES 10 /* max number of error retries */ #define ACKTIMO 10 /* seconds timeout waiting for ACK */ #define FLUSHTIMO 2 /* Flush timeout when invalid response ** recieved, must be very short. Note: ** a one (1) may not work because timer ** will trip between 0 and 1 sec. */ #define MAXRECNO 0xff /* top modulo record number */ #define BYTEMASK 0xff /* single byte mask for integers */ #define TMO -1 /* timeout function return value */ #define SOH 0x01 /* ANSI Start Of Header */ #define EOT 0x04 /* ANSI End Of Transmission */ #define ACK 0x06 /* ANSI positive ACKnowledge */ #define NAK 0x15 /* ANSI Negative AcKnowledge */ #define CAN 0x18 /* ANSI CANcel */ #define ESC 0x1b /* ANSI ESCape */ /* Definition of the 128 byte Mac file info record */ #define H_VEROFF 0 /* Mac OS only supports version zero */ #define H_NLENOFF 1 /* Mac file name length (1..63) */ #define H_NAMEOFF 2 /* Mac file name */ /* 65 <-> 80 is the FInfo structure */ #define H_TYPEOFF 65 /* file type */ #define H_AUTHOFF 69 /* file application owner */ #define H_FDRFLG 73 /* first finder flag */ #define H_ZERO1 74 /* (should be zero) */ #define H_WVRT 75 /* folder vertical position */ #define H_WHRZ 77 /* folder horizontal position */ #define H_FOLDRID 79 /* folder ID */ #define H_PROT 81 /* file protected bit (0x01) */ #define H_ZERO2 82 /* (should be zero) */ #define H_DLENOFF 83 /* data fork size */ #define H_RLENOFF 87 /* resource fork size */ #define H_CTIMOFF 91 /* file creation time */ #define H_MTIMOFF 95 /* file last time modified time */ #define H_FDRINFO 99 /* Finder word */ #define H_FDRFLG2 101 /* second Finder flags */ #define H_ZERO3 102 /* 15 bytes (should be zero) */ #define H_UNPKSZ 116 /* hint for unpacking size (unused) */ #define H_HDR2 120 /* info header size (unused) */ #define H_UPBIIVER 122 /* highest MacBin II version (unused) */ #define H_MINBIIVER 123 /* minimum MacBin II version (unused) */ #define H_CRC 124 /* MacBin II CRC (unused) */ enum {TEXT, DATA, RSRC, FULL} mode; #define LOGLO (Log > 1) #define LOGMED (Log > 2) #define LOGHI (Log > 3) /* C library routines */ extern char *getenv(); struct Macheader { char m_name[NAMEBYTES+1]; char m_type[4]; char m_author[4]; long m_datalen; long m_rsrclen; long m_createtime; long m_modifytime; } Mh; struct Filenames { char f_info[256]; char f_data[256]; char f_rsrc[256]; } Files; @//E*O*F layers_1.10/macbput.h// chmod u=rw,g=r,o=r layers_1.10/macbput.h echo x - layers_1.10/makefile sed 's/^@//' > "layers_1.10/makefile" <<'@//E*O*F layers_1.10/makefile//' # Makefile for Layers 1.0 BIN = /usr/local MANDIR = /usr/local/manl PGM = layers PGM2 = layersize PGM3 = layertitle PGM4 = macbput MS = l CFLAGS = -O CFILES = layers.c protocol.c OFILES = layers.o protocol.o default: @echo "Type 'make all' to build all components but not install." @echo " Read the implications list below for non-setuid installation to" @echo " guide your decision on whether to setuid the layers command." @echo @echo "Type 'make installsetuid' to install setuid." @echo @echo "Type 'make installnosetuid' to install layers command non-setuid." @echo @echo " Implications of non setuid installation are:" @echo " 1) As with any socket allocating utility your system may be" @echo " *less* secure with this option if your kernel does not protect" @echo " virtual tty's from cross user allocation and access. One" @echo " hole is where the undead program of a disconnected user can be" @echo " left attached to a socket and a later user allocate the same" @echo " socket and receive continued output. Another is that any user" @echo " can read/write to the virtual control terminals of a" @echo " MacLayers user. (The reason for all this is that the layers" @echo " utility cannot set the pseudo tty owner to the user.)" @echo " 2) If your utmp file is protected from user writes then" @echo " users will not be able to do login shell windows (write and" @echo " talk-like facilities will be unavailable and who-like commands" @echo " may give inaccurate details.)" all: $(PGM) $(PGM2) $(PGM3) $(PGM4) $(PGM): $(OFILES) $(CC) $(CFLAGS) -o $(PGM) $(OFILES) # $(CC) $(CFLAGS) -o $(PGM) $(OFILES) -ltermcap layers.o: layers.c layers.h $(CC) $(CFLAGS) -c layers.c protocol.o: protocol.c layers.h $(CC) $(CFLAGS) -c protocol.c $(PGM2): layersize.o $(CC) $(CFLAGS) -o $(PGM2) layersize.o $(PGM3): layertitle.o $(CC) $(CFLAGS) -o $(PGM3) layertitle.o $(PGM4): macbput.o $(CC) $(CFLAGS) -o $(PGM4) macbput.o layersize.o: layersize.c $(CC) $(CFLAGS) -c layersize.c layertitle.o: layertitle.c $(CC) $(CFLAGS) -c layertitle.c macbput.o: macbput.c macbput.h $(CC) $(CFLAGS) -c macbput.c installsetuid: $(PGM) $(PGM2) $(PGM3) $(PGM4) rm -f $(BIN)/$(PGM) install -c -s -o root -g daemon -m 4711 $(PGM) $(BIN)/$(PGM) install -c -s $(PGM2) $(BIN)/$(PGM2) install -c -s $(PGM3) $(BIN)/$(PGM3) # install -c -s $(PGM4) $(BIN)/$(PGM4) installnosetuid: $(PGM) $(PGM2) $(PGM3) $(PGM4) rm -f $(BIN)/$(PGM) install -c -s $(PGM) $(BIN)/$(PGM) install -c -s $(PGM2) $(BIN)/$(PGM2) install -c -s $(PGM3) $(BIN)/$(PGM3) # install -c -s $(PGM4) $(BIN)/$(PGM4) manpage: layers.1 rm -f $(MANDIR)/$(PGM).$(MS) cp layers.1 $(MANDIR)/$(PGM).$(MS) chmod 664 $(MANDIR)/$(PGM).$(MS) clean: rm -f a.out core $(PGM) $(PGM2) $(PGM3) $(PGM4) *.o shar: makefile shar -V -c -L60 -o ship README README.V1.10 layers.1 makefile layers.h \ layers.c protocol.c layersize.c layertitle.c MacLayers.hqx \ MacLayers.doc macbput.c macbput.h macbput.1 @//E*O*F layers_1.10/makefile// chmod u=rw,g=r,o=r layers_1.10/makefile echo x - layers_1.10/protocol.c sed 's/^@//' > "layers_1.10/protocol.c" <<'@//E*O*F layers_1.10/protocol.c//' /* Copyright (C) 1989 by David W. Trissel * * Not derived from licensed software. * * Permission is granted to freely use, copy, modify, and redistribute * this software, provided that no attempt is made to gain profit from it, * the author is not construed to be liable for any results of using the * software, alterations are clearly marked as such, and this notice is * not modified. * */ #include <stdio.h> #include <signal.h> #include <errno.h> #include "layers.h" /* protocol.c - BSD MacLayers protocol driver */ /* This module handles all interaction with the Macintosh MacLayers ** program. Services provided are: ** ** InitLink() - initialize link to MacLayers ** ** TopChannel() - return highest prority channel ** ** SendNew() - request new layer channel of MacLayers ** ** SendTitle() - change window title to given string (NOT IMPLENTED YET) ** ** SendDelete()- tell MacLayers indicated layer has died ** ** SendQuit() - order MacLayers to terminate layers mode ** ** SendData() - send output to indicated channel's window ** ** SendReshape() - send Shape structure to MacLayers ** ** ProcessStreamin() - data is ready to be processed from MacLayers ** */ #define DUMPALL #undef DUMPALL /* C library calls */ unsigned alarm(); /* alarm system call */ static int Start_proto1(); static int Start_proto2(); static int Start_proto3(); static int Start_proto4(); static int Start_proto5(); static int Start_proto6(); static int Start_proto7(); static int Start_proto8(); static int GetData(); static void Packet(); static void Parse(); static void AwaitInput(); static void asciishape(); static void fill4(); static void fill2(); static void fill1(); static int parseshape(); static unsigned get4(); static unsigned get2(); static unsigned get1(); static int myscanf(); static int mygetchar(); static void myungetc(); static int myalarm(); static void dumptime(); static char inbuff[IOSIZE]; /* input buffer from MacLayers */ static char *inpos; /* current input position in buffer */ static int insize = 0; /* characters left to process */ static int settimeout = 0; /* alarm system call timeout value */ static int Outstream = -1; /* current output stream channel */ static int Instream = -1; /* current input stream channel */ static int Sevenbits = 0; /* communication channel is 7 bits */ static struct Shape SNshape; /* SendNew() shape response */ static int SNresp = 0; /* SendNew() reponse poll flag */ static int SNchan = 0; /* SendNew() channel return */ #define SN_WAITING -1000 /* SendNew() waiting response value */ #define ATTRSIZE 15 /* size of window attribute string */ #define FLUSH fflush(stdout) /* Initlink() - initialize link with MacLayers */ /* Returns: ** 0 - linkup failed ** 1 - linkup successful, Maclayers now in protocol mode */ int Initlink() { int Outstream = -1; /* no default stream yet */ int Instream = -1; /* no default stream yet */ int num1, num2, num3; /* scanf item result */ int err; /* error code */ int doversion = 0; /* version swapping flag */ #define WAITTIME 10 /* second wait response time */ #define INITTIME 2 /* wait time after succesful startup */ /*** This patch not yet tested or implemented. Sent to the MacLayers archive **** as: This patch fixes the "Encode request not from host" bug that occurs when running MacLayers 1.00 on a VaxStation 3100 and Ultrix 3.1d. I came across this patch rather accidentally, so take it with a grain of NaCl. ifndef vax /* we must non-buffer input since all input must be immediate setbuf(stdin, NULL); /* non-buffer all input endif Archive manager Peter Newton added the following comments: Don't have a VaxStation to try it one. Many people have had problems with Ultrix on VaxStations. We have been giving them uuencoded binaries made under BSD. *****************/ /* V1.1 Note: The latest BSD Sun system doesn't break reads from the ** terminal during signals. It appears that System V does do this ** as do earlier BSD systems. At this time no special hacks will ** be installed in just to support Unix versions not doing the breaks. ** Without alarm timeouts host layers will hang if inappropriate ** responses are received during protocol startup. But this should ** only happen when a user types the layers command when not running ** MacLayers. */ /* we must non-buffer input since all input must be immediate */ setbuf(stdin, NULL); /* non-buffer all input */ /* Host layers after V1.00 can receive protocol level information. ** Send our level as ESC <id>. Version 1.00 MacLayers will treat ** this as an illegal vt-100 sequence and issue a beep. Later versions ** accept our protocol level and return their own after receiving ** our protocol startup sequence sent here. */ DO DEBUG("write protocol level 2: ESC 2\n"); /* send our protocol level out */ if ((err=printf("\033%c", HOSTPROTOCOL + '0')) < 0) { DO DEBUG(" printf() error code %d\n", err); return ( 0 ); /* problem with stdout */ } FLUSH; /* send intitial request for terminal type and version number */ DO DEBUG("write: ESC [ c\n"); printf("\033[c"); FLUSH; /* force output buffer */ /* Attempt to read "ESC [ ? 8 ; typedigits ; versiondigits c" ** MacLayers in layers mode will return 8 ; 1 c ** MacLayers in startup mode returns 8 ; X c ** were X is the protocol level */ num1 = num2 = num3 = -1; /* default to unsupplied values */ DO DEBUG(" doing first scanf\n"); (void) myalarm(WAITTIME); /* set timeout */ (void) myscanf("\033[?%d;%d;%dc", &num1, &num2, &num3); (void) myalarm(0); /* cancel alarm */ DO DEBUG("read ESC [ ? %d ; %d; %d c\n", num1, num2, num3); if (num1 != 8 || num2 != 10) return ( 0 ); /* not correct response or layers term ID */ DO DEBUG("Client Protocol %d, Host Protocol %d\n", num3, HOSTPROTOCOL); /* Terminal type was valid, now verify protocol number */ switch (num3) { case 1: case 2: case 3: case 4: case 5: case 6: /* these are all valid levels */ break; /* break for valid levels */ case 9: num3 = 7; /* 9 represents level 7 */ break; case 0: num3 = 8; /* 0 represents level 8 */ break; default: /* invalid response */ return ( 0 ); /* incorrect protocol level */ } /* client protocol level switch */ Clientlevel = num3; /* set client protocol level offered */ /* final protocol level cannot be higher than our current support level */ if (num3 > HOSTPROTOCOL) { DO DEBUG("Protocol level forced down to host level\n"); num3 = HOSTPROTOCOL; /* force back down to the host level */ } Protocollevel = num3; /* set effective protocol level */ /* execute proper startup protocol level routine */ switch (Protocollevel) { case 1: return ( Start_proto1() ); /* handle protocol 1 startup sequence */ case 2: return ( Start_proto2() ); /* handle protocol 2 startup sequence */ case 3: return ( Start_proto3() ); /* handle protocol 3 startup sequence */ case 4: return ( Start_proto4() ); /* handle protocol 4 startup sequence */ case 5: return ( Start_proto5() ); /* handle protocol 5 startup sequence */ case 6: return ( Start_proto6() ); /* handle protocol 6 startup sequence */ case 7: return ( Start_proto7() ); /* handle protocol 7 startup sequence */ case 8: return ( Start_proto8() ); /* handle protocol 8 startup sequence */ } /* invalid - refuse startup */ return ( 0 ); /* SHOULD NOT OCCUR */ } /* Initlink() */ /* Start_proto1() - Protocol 1 startup sequence */ static int Start_proto1() { int num1; /* scanf item result */ /* This is the original MacLayers protocol for V1.00 */ /* ask terminal if ENC_ENABLE is to be forced */ DO DEBUG("write: ESC [ F\n"); (void) printf("\033[F"); FLUSH; /* force output buffer */ /* attempt to read "ESC [ flag F" (flag indicates ENC_ENABLE status) */ num1 = -1; /* default to invalid response */ (void) myalarm(WAITTIME); /* set timeout */ (void) myscanf("\033[%dF", &num1); (void) myalarm(0); /* cancel alarm */ DO DEBUG("read ESC [ %d F\n", num1); if (num1 != 1 && num1 != 0) return ( 0 ); /* something's wrong */ if (num1 == 1) Sevenbits = 1; /* we are processing 7-bit data */ /* now startup packet mode in non ENC_ENABLE processing */ DO DEBUG("write: ESC [ 2 ; 0 v\n"); (void) printf("\033[2;0v"); /* "ESC [ 2 ; 0 v" */ FLUSH; /* force output buffer */ #if 0 { int i,j; for (i=0; i<20; i++) { j = mygetchar(); DO DEBUG("fuck read 0x%lx == %c\n", j, j); } return (0 ); } #endif #if 0 /* We must reset buffer to reread this character. Note ** this won't work for triggering reads by layers.c unless ** client is sending more than 1 character because the select() ** system call is used. */ /* ungetc(num1, stdin); /* return character back for re-read */ myungetc(num1); /* re-insert this character */ #endif #if 0 insize = 1; /* preset buffer with one character */ inpos = inbuff; /* setup buffer pointer */ inbuff[0] = num1; /* setup to reread this character */ #endif Clientlevel = 1; /* assume protocol is level 1 */ /* we are now in packet mode */ sleep( INITTIME ); /* let Macintosh keep up with us */ return ( 1 ); /* return successful startup */ } /* Start_proto1() */ /* Start_proto2() - Protocol 2 startup sequence */ static int Start_proto2() { /* This is the second MacLayers protocol starting with V1.1 */ /* we are now in packet mode */ sleep( INITTIME ); /* let Macintosh keep up with us */ return ( 1 ); /* return successful startup */ } /* Start_proto2() */ /* Start_proto3() - Protocol 3 startup sequence */ static int Start_proto3() { /* not yet defined! */ return ( 0 ); /* SHOULD NOT OCCUR */ } /* Start_proto3() */ /* Start_proto4() - Protocol 4 startup sequence */ static int Start_proto4() { /* not yet defined! */ return ( 0 ); /* SHOULD NOT OCCUR */ } /* Start_proto4() */ /* Start_proto5() - Protocol 5 startup sequence */ static int Start_proto5() { /* not yet defined! */ return ( 0 ); /* SHOULD NOT OCCUR */ } /* Start_proto5() */ /* Start_proto6() - Protocol 6 startup sequence */ static int Start_proto6() { /* not yet defined! */ return ( 0 ); /* SHOULD NOT OCCUR */ } /* Start_proto6() */ /* Start_proto7() - Protocol 7 startup sequence */ static int Start_proto7() { /* not yet defined! */ return ( 0 ); /* SHOULD NOT OCCUR */ } /* Start_proto7() */ /* Start_proto8() - Protocol 8 startup sequence */ static int Start_proto8() { /* not yet defined! */ return ( 0 ); /* SHOULD NOT OCCUR */ } /* Start_proto8() */ /* TopChannel() - return highest prority channel */ int TopChannel() { return ( Instream ); } /* TopChannel() */ /* ** WARNING: Most of the following functions may be recursively called ** as control commands are processed from the input stream */ /* ProcessStreamin() - MacLayers has input to process */ void ProcessStreamin() { int c; /* input character being processed */ DO dumptime(); DO DEBUG("ProcessStreamin() insize %d\n", insize); GetData(); /* read some */ while (insize > 0) /* while more data to process ... */ Parse(); /* process next chuck of data */ } /* ProcessStreamin() */ /* SendNew() - request new layer channel from MacLayers */ /* This command is unique in that it returns a response from MacLayers. ** To do this we continue processing the input stream until we get ** our return. (This leads to recursive conditions.) The variables ** 'SNresp', 'SNshape' and 'SNchan' are set when our reply is received. */ int SendNew(shape) struct Shape *shape; /* shape to use for new window */ { int i; /* attribute count variable */ char astring[ATTRSIZE]; /* copy of attribute string */ DO dumptime(); DO DEBUG("SendNew() new layer requested: '~%cA'\n", '1'+ATTRSIZE); /* check for a recursive call */ if (SNresp == SN_WAITING) { DO DEBUG("return 0 - recursive call\n"); return ( 0 ); /* return failure */ } putchar(ESCAPE); /* send start of control packet char */ putchar('1' + ATTRSIZE); /* send command size */ putchar('A'); /* send command */ asciishape(shape, astring); /* convert shape to string */ for (i=0; i < ATTRSIZE; i++) putchar(astring[i]); /* send next attribute digit */ FLUSH; /* now stay here and process the input stream until we see our response */ /**** THIS SHOULD BE ENHANCED TO TIMEOUT WITH GetData() AND REISSUE REQUEST */ SNresp = SN_WAITING; /* indicate we are waiting a response */ while (SNresp == SN_WAITING) { DO DEBUG(" while (SNresp %d == %d)\n", SNresp, SN_WAITING); AwaitInput(); /* wait till input from MacLayers arrives */ ProcessStreamin(); /* process available input */ } if (SNresp == -1) /* if Maclayers rejected request */ SNchan = 0; /* return failure channel of zero */ else *shape = SNshape; /* else update shape structure */ DO DEBUG("SendNew() returning channel %d\n", SNchan); return ( SNchan ); /* return the indicated channel */ } /* SendNew() */ /* SendReshape() - send to shape to MacLayers */ void SendReshape(chan, shape) int chan; /* channel shape belongs to */ struct Shape *shape; /* shape to use for new window */ { int i; /* attribute count variable */ char astring[ATTRSIZE]; /* copy of attribute string */ DO dumptime(); DO DEBUG("SendReshape() reshape: '~%cA'\n", '2'+ATTRSIZE); if (chan <= 0 || chan > MAXPCHAN) { DO DEBUG("BAD CHANNEL!!!\n"); return; /* ignore request */ } putchar(ESCAPE); /* send start of control packet char */ putchar('2' + ATTRSIZE); /* send command size */ putchar('R'); /* send command */ putchar(chan + '0'); /* send channel */ asciishape(shape, astring); /* convert shape to string */ DO DEBUG("shape: %.*s\n", ATTRSIZE, astring); for (i=0; i < ATTRSIZE; i++) putchar(astring[i]); /* send next attribute digit */ FLUSH; } /* SendReshape() */ /* SendTitle() - set layer's window title */ void SendTitle(chan, buff, cnt) int chan; /* layer window ID */ char *buff; /* new title string */ int cnt; /* count of title length */ { int i; /* work variable */ DO DEBUG("SendTitle(chan%d, len %d, '%.*s')\n", chan, cnt, cnt, buff); if (chan <= 0 || chan > MAXPCHAN) { DO DEBUG("BAD CHANNEL!!!\n"); return; /* ignore request */ } if (cnt < 0) { DO DEBUG("BAD COUNT!!!\n"); return; /* ignore request */ } /* for now chop title size to 29 chars since that's MacLayer's limit */ if (cnt > 29) cnt = 29; /* due to packet size limit */ /* we must guarantee that the size will not appear to be another ESCAPE */ if ('2' + cnt == ESCAPE) cnt--; /* truncate to avoid ESCAPE ESCAPE */ putchar(ESCAPE); /* send start of control packet char */ putchar('2' + cnt); /* send size of packet */ putchar('T'); /* send command */ putchar(chan + '0'); /* send channel ID */ for (i=0; i<cnt; i++) putchar(buff[i]); /* send out title */ FLUSH; } /* SendTitle() */ /* SendDelete() - tell Maclayers layer died */ void SendDelete(chan) int chan; /* dead channel ID */ { DO DEBUG("SendDelete(%d) '~2D%d'\n", chan, chan); if (chan <= 0 || chan > MAXPCHAN) /* check channel ID */ { DO DEBUG("BAD CHANNEL!!!\n"); return; /* ignore request */ } putchar(ESCAPE); /* send control packet start char */ putchar('2'); /* send command size */ putchar('D'); /* send command character */ putchar(chan + '0'); /* channel ID in ascii */ FLUSH; } /* SendDelete() */ /* SendQuit() - order MacLayers to end layers mode */ void SendQuit(chan) int chan; /* dead channel ID */ { DO dumptime(); DO DEBUG("SendQuit() '~1E'\n"); putchar(ESCAPE); /* send control packet start char */ putchar('1'); /* send command size */ putchar('E'); /* send command */ FLUSH; } /* SendQuit() */ /* SendData() - send output to layer's window */ void SendData(chan, buff, cnt) int chan; /* layer window ID */ unsigned char *buff; /* new title string */ int cnt; /* count of title length */ { unsigned c; /* output character being sent */ DO { int dcnt; dumptime(); DEBUG("SendData(chan %d, len %d, '", chan, cnt, cnt, buff); for (dcnt=0; dcnt<cnt; dcnt++) DEBUG("%c", buff[dcnt]); /* dump each char so null doesn't stop */ DEBUG("')\n"); } if (chan <= 0 || chan > MAXPCHAN) { DO DEBUG("BAD CHANNEL!!!\n"); return; /* ignore request */ } /* if new output channel stream then prefix redirect command */ if (chan != Outstream) { DO DEBUG("Redirecting output to %d '~2O%d'\n", chan, chan); putchar(ESCAPE); /* start of command sequence */ putchar('2'); /* send command size */ putchar('O'); /* send command */ putchar(chan + '0'); /* put out channel in ASCII */ Outstream = chan; /* new output stream set */ } /* transmit the buffer converting the ESCAPE sequence to double ESCAPE */ while (cnt--) { c = *buff++; /* get next output character */ #ifdef DUMPALL DO DEBUG("outchar %c 0x%x\n", c, c); #endif if (c == ESCAPE || c == (ESCAPE + 0x80)) { putchar(c); /* put it out twice */ #ifdef DUMPALL DO DEBUG(" Doubled Escape!\n"); #endif } putchar(c); /* write character out */ } FLUSH; /* force out queued output characters */ } /* SendData() */ /* Parse() - process next chunk of input stream */ static void Parse() { #define ST_NULL 0 /* not primed for next state yet */ #define ST_STREAM 1 /* processing default stream input */ #define ST_PKT 2 /* processing packet data */ int c; /* input character being processed */ static int state = ST_NULL; /* current input state */ static int psize = 0; /* packet size */ static int rempsize = 0; /* remembered packet size */ static char pdata[MAXSTR]; /* area for packet data */ static char *ppos; /* packet read insert position */ static int escapemode = 0; /* processing escape character */ static int escapechar; /* escape character being processed */ static pchan = -1; /* packet input stream channel */ DO dumptime(); DO DEBUG("Parse() insize %d\n", insize); while (insize-- > 0) /* while more data */ { c = *inpos++; /* get next character */ switch (state) /* process according to state */ { case ST_NULL: /* prepare for new packet */ DO DEBUG("ST_NULL\n"); psize = 0; /* clear packet size */ ppos = pdata; /* start fill at data position */ pchan = Instream; /* packet channel is current input stream */ state = ST_STREAM; /* default is stream processing */ case ST_STREAM: /* stream keyboard input for layer */ /* check for escape char with possible high bit on */ #ifdef DUMPALL DO DEBUG("ST_STREAM %x/%x '%c' esc %d insz %d\n", c, c & 0x7f, c & 0x7f, escapemode, insize); #endif if (c == ESCAPE || c == (ESCAPE | 0x80)) { if (escapemode && c == escapechar) /* previous was ESCAPE */ /* this is really a single ESCAPE character */ escapemode = 0; /* back out of ESCAPE mode */ else /* what do we do with back to back esc esc+0x80 ? */ { /* flag in escape mode */ escapemode++; escapechar = c; /* remember character used for escape */ continue; /* and continue scan */ } } else if (escapemode) { /* this is the start of a control packet */ if (psize) /* if we have previous data packet */ Packet(pchan, psize, pdata); /* finish up previous pkt */ /* process packet size */ psize = (c & 0x7f) - '0'; /* save size byte */ if (psize <= 0 || psize > MAXSTR) { /* bad size */ DO DEBUG("Bad pkt size %d\n", psize); break; /* trash this packet */ } rempsize = psize; /* remember this size for later */ #if 0 ptimo = rtimo; /* start receive timeout */ #endif escapemode = 0; /* escape mode now off */ ppos = pdata; /* initialize data store pointer */ state = ST_PKT; /* expect packet data next */ continue; /* continue scan */ } /* process standard data output character for current stream */ *ppos++ = c; /* save next data character */ if (++psize >= MAXSTR) /* if packet full ... */ { Packet(pchan, psize, pdata); /* process this packet */ break; /* end packet processing */ } continue; /* continue scan */ case ST_PKT: /* process next paket data byte */ *ppos++ = c & 0x7f; /* store next data byte */ #ifdef DUMPALL DO DEBUG("ST_PKT: %x '%c' sz %d\n", c & 0x7f, c & 0x7f, psize); #endif if (--psize != 0) continue; #if 0 if (crc((unsigned char *) &rpkt, rpkt.pkt.HEADER_DSIZE+2)) STATS(Scrcerr); /* communications error */ else #endif Packet(0, rempsize, pdata); /* process it */ } /* end build packet switch */ #if 0 ptimo = 0; /* no more receive timeout */ #endif state = ST_NULL; /* no more receive packet in progress */ } /* end while (insize) */ if (state == ST_STREAM && psize ) /* if we have some data ... */ { Packet(Instream, psize, pdata); /* process this data */ #if 0 ptimo = 0; /* no more receive timeout */ #endif state = ST_NULL; /* no more receive packet in progress */ } } /* Parse() */ /* Packet() - prcess next input data string or control packet */ static void Packet(chan, size, buff) int chan; /* channel (0 if control packet) */ int size; /* amount of data */ char *buff; /* pointer to packet data */ { static struct Shape shape; /* Shape structure */ DO dumptime(); DO DEBUG("Packet(chan %d, size %d, '%.*s')\n", chan, size, size, buff); /* verify channel */ if (chan < 0 || chan > MAXPCHAN) { DO DEBUG("BAD CHANNEL!!\n"); return; /* ignore bad channel */ } /* if data packet (chan>0) feed data to server */ if (chan > 0) { ReceiveData(chan, buff, size); return; /* we are through */ } /* control packet (channel 0) */ chan = buff[1] - '0'; /* assume channel specified */ if (chan < 0 || chan > MAXPCHAN) /* if invalid ... */ chan = 0; /* set to zero */ switch (buff[0]) { case 'I': /* redirect stream */ DO DEBUG("CMD 'I' redirect stream to %c\n", buff[1]); if (size != 2) /* verify size */ break; /* break if bad */ if (chan == 0) /* verify channel */ break; /* break if bad */ Instream = chan; /* new instream channel */ return; /* we are through */ case 'A': /* returned A_NEWLAYER packet */ DO DEBUG("CMD 'A' A_NEWLAYER response %c newchan %c SNresp %d\n", buff[2], buff[1], SNresp); if (size != 3 + ATTRSIZE) break; /* break if bad */ /* if SendNew() not waiting for a response this is invalid */ if (SNresp != SN_WAITING) break; /* break if bad */ if (buff[2] == '1') /* if response is "failed" ... */ SNresp = -1; /* show -1 response */ else if (buff[2] == '0') /* if response is "success" ... */ { if (chan == 0) /* if invalid channel */ break; /* break if bad */ /* build shape structure for SendNew() */ if (parseshape(&SNshape, &buff[3]) == -1) break; /* if invalid data then bad packet */ SNresp = 0; /* show good response */ SNchan = chan; /* indicate channel returned */ } else break; /* break if bad */ DO DEBUG("SNresp = %d, SNchan = %d\n", SNresp, SNchan); return; /* we are through */ case 'N': /* new layer creation */ DO DEBUG("CMD 'N' new layer creation newchan %c\n", buff[1]); if (size != 2 + ATTRSIZE) /* verify size */ break; /* break if bad */ if (chan == 0) /* verify channel */ break; /* break if bad */ /* build shape structure */ if (parseshape(&shape, &buff[2]) == -1) break; /* if invalid data then bad packet */ ReceiveNew(chan, &shape); /* pass to server */ return; /* packet is done */ case 'D': /* deleted layer */ DO DEBUG("CMD 'D' deleted layer %c\n", buff[1]); if (size != 2) /* verify size */ break; /* break if bad */ if (chan == 0) /* verify channel */ break; /* break if bad */ ReceiveDelete(chan); /* pass on to server */ return; /* packet is done */ case 'E': /* exit - awaiting shutdown */ DO DEBUG("CMD 'E' exit MacLayers awaiting shutdown msg\n"); if (size != 1) /* verify size */ break; /* break if bad */ ReceiveQuit(); /* pass to server */ /* NOT REACHED*/ return; /* ?? should never reach here */ case 'R': /* reshaped */ DO DEBUG("CMD 'R' reshape chan %c\n", buff[1]); if (size != 2 + ATTRSIZE) /* verify size */ break; /* break if bad */ if (chan == 0) /* verify channel */ break; /* break if bad */ /* build shape structure */ if (parseshape(&shape, &buff[2]) == -1) break; /* if invalid data then bad packet */ ReceiveReshape(chan, &shape); /* tell server about shape */ return; /* packet processed */ case 'S': /* signal */ DO DEBUG("CMD 'S' SIGNAL chan %c sig %c\n", buff[1], buff[2]); if (size != 3) /* verify size */ break; /* break if bad */ if (chan == 0) break; /* break if bad */ if (buff[2] == '0') /* if SIGINT */ size = SIGINT; /* yes */ else if (buff[2] == '1') /* if SIGHUP */ size = SIGHUP; /* yes */ else break; /* invalid signal */ ReceiveSignal(chan, size); /* pass to server */ return; /* packet processed */ default: DO DEBUG("ILLEGAL CONTROL PACKET!!!\n"); return; /* ignore bad packet */ } /* end command packet switch */ /* switch falls out if bad size or channel for given command */ DO DEBUG("Invalid size or channel!!!\n"); /* dump error */ return; /* ignore packet */ } /* Packet() */ /* GetData() - read next input from MacLayers stream */ /* The settimeout variable indicates if we return when nothing ** is read within a certain amount of seconds. The return code is: ** ** 0 - timeout occured and no data was read ** ** 1 - no timeout occured, data read */ static int GetData() { int result; /* return from read() */ int i; /* work counter */ char *ptr; /* work pointer */ DO dumptime(); DO DEBUG("GetData()\n"); /* if buffer still has data simply return (SHOULD NOT OCCUR?) */ if (insize > 0) { DO DEBUG("early return insize %d\n", insize); return ( 1 ); /* act as through data read */ } inpos = inbuff; /* next get will start at beginning */ insize = 0; /* default insize back to zero */ /* set timeout if we are to do so */ if (settimeout) { DO DEBUG("alarm(%d)\n", settimeout); (void) alarm(settimeout); /* set timeout in seconds */ } /* do the read from stdin */ result = read(0, inbuff, IOSIZE); /* if alarm was set cancel it now */ if (settimeout) { DO DEBUG("alarm(0)\n"); (void) myalarm(0); /* cancel alarm */ } /* check for timeout or error */ /* EWOULDBLOCK for no data avail -(but we should not see this) */ /* EINTR if signal stopped the read -(rare but could happen) */ if (result <= 0) { DO DEBUG(" ?? no data result %d\n", result); return ( 0 ); /* return nothing read */ } /* return with fresh buffer data */ insize = result; /* if 7-bit communication channel then strip all high bits */ if (Sevenbits) for (i=result,ptr = inbuff; i>0; --i) *ptr++ &= 0x7f; /* strip high bit */ DO DEBUG("read %d bytes\n", insize); return ( 1 ); /* return OK code */ } /* GetData() */ /* AwaitInput() - wait for more input from MacLayers */ static void AwaitInput() { int r; /* read descriptor bits */ DO dumptime(); DO DEBUG("AwaitInput() insize %d\n", insize); /* if buffer has data then don't wait */ if (insize > 0) { DO DEBUG("Return early insize %d\n", insize); return; } do { r = 1<<0; /* wait for read from input device */ if (select(32, &r, NULL, NULL, NULL) == -1) /* if problem waiting ... */ { if (errno != EINTR) /* if not simply signal taken ... */ { /* SHOULD NOT OCCUR - shutdown layers */ DO DEBUG("AwaitInput: select error %d\n", errno); printf("layers: AwaitInput: bad select %d\n", errno); FQuit(); /* shutdown layers */ /* NOT REACHED */ } } } while ((r & 1<<0) == 0); } /* AwaitInput() */ /* asciishape() - convert Shape structure to ASCII */ static void asciishape(shape, loc) struct Shape *shape; /* Shape structure for channel */ char *loc; /* location to start filling result */ { char *origloc; /* (for debuggin) */ origloc = loc; /* remember start of string */ fill4(&loc, shape->worigh); /* origin h */ fill4(&loc, shape->worigv); /* origin v */ fill2(&loc, shape->wlines); /* lines high */ fill2(&loc, shape->wchars); /* chars wide */ fill1(&loc, shape->wfont); /* font size */ fill2(&loc, shape->wattr); /* attributes */ DO DEBUG("asciishape(): %.*s\n", ATTRSIZE, origloc); } /* asciishape() */ /* fill4() - convert parameter to ASCII */ static void fill4(loc, valu) char **loc; /* pointer to fill area pointer */ unsigned valu; /* value to use */ { fill2(loc, valu>>8); /* fill high half word */ fill2(loc, valu & 0xff); /* fill low half word */ } /* fill4() */ /* fill2() - convert parameter to ASCII */ static void fill2(loc, valu) char **loc; /* pointer to fill area pointer */ unsigned valu; /* value to use */ { fill1(loc, valu>>4); /* fill high byte */ fill1(loc, valu & 0xf); /* fill low byte */ } /* fill2() */ /* fill1() - convert parameter to ASCII */ static void fill1(loc, valu) char **loc; /* pointer to fill area pointer */ unsigned valu; /* value to use */ { *(*loc)++ = "0123456789ABCDEF"[valu & 0xf]; /* return hex value */ } /* fill1() */ /* parseshape() - convert ASCII image to Shape structure */ static int Badconvert; /* indicates bad conversion */ static int parseshape(shape, loc) struct Shape *shape; /* Shape structure for channel */ char *loc; /* location to start parsing */ { Badconvert = 0; /* clear bad characters indicator */ shape->worigh = get4(&loc); /* origin h */ shape->worigv = get4(&loc); /* origin v */ shape->wlines = get2(&loc); /* lines high */ shape->wchars = get2(&loc); /* chars wide */ shape->wfont = get1(&loc); /* font size */ shape->wattr = get2(&loc); /* attributes */ DO DEBUG("ParseShape(): origv %d, origh %d, lines %d, chars %d\n", shape->worigv, shape->worigh, shape->wlines, shape->wchars); DO DEBUG(" font %d, attr 0x%x, badconv %d\n", shape->wfont, shape->wattr, Badconvert); return ( Badconvert ? -1 : 0 ); /* return conversion code */ } /* parseshape() */ /* get4() - convert ASCII to parameter */ static unsigned get4(loc) char **loc; /* pointer to fill area pointer */ { unsigned hi; /* high portion */ unsigned low; /* low portion */ hi = get2(loc); /* get high byte */ low = get2(loc); /* get low byte */ return ( (hi<<8) + low ); /* return word value */ } /* get4() */ /* get2() - convert ASCII to parameter */ static unsigned get2(loc) char **loc; /* pointer to fill area pointer */ { unsigned hi; /* high portion */ unsigned low; /* low portion */ hi = get1(loc); /* get high half */ low = get1(loc); /* get low half */ return ( (hi<<4) + low ); /* return byte value */ } /* get2() */ /* get1() - convert ASCII to parameter */ /* This function sets 'Badconvert' if an invalid character is detected */ static unsigned get1(loc) char **loc; /* pointer to fill area pointer */ { int c; /* character to convert */ c = *(*loc)++; /* fetch character */ if (c >= '0' && c <= '9') /* zero through nine */ return ( c - '0' ); /* return it's binary value */ if (c >= 'a' && c <= 'f') /* lower case hex */ return ( c - 'a' + 10); /* return it's binary value */ if (c >= 'A' && c <= 'F') /* upper case hex */ return ( c - 'A' + 10); /* return it's binary value */ /* invalid digit! */ Badconvert++; /* set bad character flag */ return ( 0 ); /* return a zero */ } /* get1() */ /* myscanf() - RAW mode scanf routine */ /** This routine is required because once our terminal is set into RAW mode *** the standard scanf routine fails on 7-bit lines whenever a high bit *** occurs in an input character. Only the %d, %c and exact input character *** images are supported in the input format string. All value parameters *** must be integer data type for both %d and %c. */ static int myscanf(str, arg1, arg2, arg3) unsigned char *str; /* scanf input string */ int *arg1,*arg2,*arg3; /* integer input pointer arguments */ { int numscan = 0; /* items filled in */ int build; /* %d build integer value */ int c; /* integer input character */ DO dumptime(); DO DEBUG("myscanf(%s)\n", str); /* scan string processing formats */ while (*str) switch (*str) { case EOF: /* Error: probably our alarm timed out */ return ( numscan ); /* NOT REACHED */ case '%': /* format specifier */ switch (*++str) { case 'c': /* return next character as is */ if ((build=mygetchar()) == -1) return ( numscan ); /* no input, return count */ break; case 'd': /* build input decimal value */ build = 0; while ((c=mygetchar()) >= '0' && c <= '9') { build = build * 10; /* next base ten digit */ build += c - '0'; /* add this digit */ } myungetc(c); /* return character ending number */ break; default: return ( numscan ); /* return if error */ /* NOT REACHED */ } /* return value to correct input parameter */ switch (numscan) { case 0: *arg1 = build; break; case 1: *arg2 = build; break; case 2: *arg3 = build; break; default: /* SHOULD NOT OCCUR */ return ( numscan ); /* NOT REACHED */ } numscan++; /* count items scanned */ str++; /* bump scan string past 'd' */ break; /* continue scan */ default: /* input character must match exactly */ c = mygetchar(); if (c != *str) return ( numscan ); /* return - we don't match string */ str++; /* to next character */ } /* end scan string char switch */ /* return number of items scanned */ return ( numscan ); } /* myscanf() */ /* mygetchar() - get character stripped to 7 bits */ /* Return: -1 if timeout ** char stripped to 7 bits */ static int mygetchar() { int c; /* next input character */ int result; /* read return value */ DO dumptime(); /* if character still in buffer return it */ if (insize > 0) { insize--; /* count down */ c = (*inpos++) & 0x7f; /* fetch next char */ DO DEBUG(" 0x%lx '%c' ", c, c); return ( c ); /* return 7-bit character */ } /* attempt to read the buffer */ result = GetData(); /* no timeout (caller may have set) */ /* return -1 if timeout */ if (result == 0) { DO DEBUG(" mygetchar ret -1\n"); return ( -1 ); } return ( mygetchar() ); /* return next character */ #if 0 c = getchar() & 0x7f; /* insure 7-bit only */ DO DEBUG(" 0x%lx '%c' ", c, c); return ( c ); #endif } /* mygetchar() */ /* myungetc() - unget a character */ /* Must only be called to return when a character was previously fetched */ static void myungetc(c) int c; /* character to unget */ { DO dumptime(); DO DEBUG("myungetc(x%lx/%c)\n", c, c); insize++; /* count back up by one */ *--inpos = c; /* restore previous character */ } /* myungetc() */ /* myalarm() - setup alarm timeout for next read */ static int myalarm(time) int time; /* time in seconds (or zero) */ { DO dumptime(); DO DEBUG("myalarm(%d)\n", time); settimeout = time; /* set for next read */ } /* myalarm() */ /* dumptime() - print out time in seconds */ /* THIS MAY BE SYSTEM DEPENDENT SO IS BASED UPON DEBUG IFDEF */ #ifdef DUMPALL #include <sys/types.h> #include <sys/time.h> #endif static void dumptime() { #ifdef DUMPALL time_t seconds; static time_t base; if (base == 0) { seconds = 0; base = time(NULL); } else seconds = time(NULL) - base; DO DEBUG("[%3d] ", seconds); #endif } /* dumptime() */ @//E*O*F layers_1.10/protocol.c// chmod u=rw,g=r,o=r layers_1.10/protocol.c echo Inspecting for damage in transit... temp=/tmp/shar$$; dtemp=/tmp/.shar$$ trap "rm -f $temp $dtemp; exit" 0 1 2 3 15 cat > $temp <<\!!! 38 198 1488 INFO 76 318 2408 README 123 795 4858 layers.1 2279 8911 59804 layers.c 69 343 2282 layers.h 146 589 3586 layersize.c 41 176 1242 layertitle.c 122 506 2849 macbput.1 598 1696 11595 macbput.c 85 520 3151 macbput.h 95 437 3077 makefile 1351 6047 36857 protocol.c 5023 20536 133197 total !!! wc layers_1.10/INFO layers_1.10/README layers_1.10/layers.1 layers_1.10/layers.c layers_1.10/layers.h layers_1.10/layersize.c layers_1.10/layertitle.c layers_1.10/macbput.1 layers_1.10/macbput.c layers_1.10/macbput.h layers_1.10/makefile layers_1.10/protocol.c | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp if [ -s $dtemp ] then echo "Ouch [diff of wc output]:" ; cat $dtemp else echo "No problems found." fi exit 0