This is screen.c in view mode; [Download] [Up]
/* Copyright (c) 1991 * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) * Copyright (c) 1987 Oliver Laumann * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 1, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program (see the file COPYING); if not, write to the * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Noteworthy contributors to screen's design and implementation: * Wayne Davison (davison@borland.com) * Patrick Wolfe (pat@kai.com, kailand!pat) * Bart Schaefer (schaefer@cse.ogi.edu) * Nathan Glasser (nathan@brokaw.lcs.mit.edu) * Larry W. Virden (lwv27%cas.BITNET@CUNYVM.CUNY.Edu) * Howard Chu (hyc@hanauma.jpl.nasa.gov) * Tim MacKenzie (tym@dibbler.cs.monash.edu.au) * Markku Jarvinen (mta@{cc,cs,ee}.tut.fi) * Marc Boucher (marc@CAM.ORG) * **************************************************************** */ #ifndef lint static char rcs_id[] = "$Id: screen.c,v 1.2 92/02/03 02:28:05 jnweiger Exp $ FAU"; #endif #include <sys/param.h> /* #include <signal.h> */ #include <ctype.h> #include <pwd.h> #include <fcntl.h> #ifdef sgi # include <sys/sysmacros.h> #endif /* sgi */ #if !defined(sun) && !defined(B43) && !defined(ISC) # include <time.h> #endif /* * Gee!! We should reverse that #if! */ #if defined(sun) || defined(_AIX) || defined(sysV68) || defined(MIPS) || defined(GOULD_NP1) || defined(B43) || defined(ISC) || defined(apollo) || defined(BSDI) || defined(sgi) # include <sys/time.h> #endif #if defined(M_XENIX) || defined(M_UNIX) #include <sys/select.h> /* for timeval */ #endif #include <sys/types.h> #ifdef ISC # include <sys/bsdtypes.h> #endif #if !defined(sysV68) && !defined(M_XENIX) # include <sys/wait.h> #endif #include <sys/stat.h> #ifndef sgi # include <sys/file.h> #endif /* sgi */ #ifndef sun # include <sys/ioctl.h> #endif /* sun */ #include <signal.h> #include "config.h" #ifdef SHADOWPW # include <shadow.h> #endif /* SHADOWPW */ #ifdef SVR4 # include <sys/stropts.h> #endif #ifdef SYSV # include <sys/utsname.h> #endif #if defined(_SEQUENT_) /* for the FD.. stuff */ # include <sys/select.h> #endif #if defined(sequent) || defined(SVR4) # include <sys/resource.h> #endif /* sequent || SVR4 */ #ifdef ISC # include <sys/tty.h> # include <sys/sioctl.h> # include <sys/pty.h> #endif #include "screen.h" #include "patchlevel.h" #if defined(xelos) || defined(sysV68) || defined(M_XENIX) struct passwd *getpwuid __P((uid_t)); struct passwd *getpwnam __P((char *)); #endif #ifdef USEVARARGS # if defined(__STDC__) # include <stdarg.h> # else # include <varargs.h> # endif #endif #ifdef DEBUG FILE *dfp; #endif #ifdef COPY_PASTE extern char *copybuffer; /* def in mark.c jw. */ extern copylen; #endif /* COPY_PASTE */ extern char *blank, *null, Term[], screenterm[], **environ, *Termcap; int force_vt = 1, assume_LP = 0; extern int in_ovl; extern int ovl_blockfore; extern void (*ovl_process)(); extern int help_page; extern int screenwidth, screenheight; extern char display_tty[]; extern int default_width, default_height; extern int Z0width, Z1width; extern int ISO2022; extern int status, HS; extern char *Z0, *WS, *LastMsg; extern time_t TimeDisplayed; int BellDisplayed; int VBellWait, MsgWait, MsgMinWait; /* tputs uses that: jw */ extern short ospeed; extern int flow, default_flow, wrap, visual_bell, default_monitor; extern int errno; extern sys_nerr; extern char *sys_errlist[]; extern char mark_key_tab[]; #if defined(TIOCSWINSZ) || defined(TIOCGWINSZ) extern struct winsize glwz; #endif static char *MakeWinMsg __P((char *, int)); static void MakeNewEnv __P((void)); static int Attach __P((int)); static void Attacher __P((void)); static void SigHandler __P((void)); static sig_t AttacherSigInt __P(SIGPROTOARG); static sig_t SigChld __P(SIGPROTOARG); static sig_t SigInt __P(SIGPROTOARG); static sig_t CoreDump __P((int)); static void DoWait __P((void)); static sig_t Finit __P((int)); static void InitKeytab __P((void)); static void SetForeWindow __P((int)); static int NextWindow __P((void)); static int PreviousWindow __P((void)); static int MoreWindows __P((void)); static void FreeWindow __P((struct win *)); static void execvpe __P((char *, char **, char **)); static void LogToggle __P((void)); static void ShowWindows __P((void)); static void ShowTime __P((void)); static void ShowInfo __P((void)); static int OpenPTY __P((void)); #ifdef PASSWORD static void trysend __P((int, struct msg *, char *)); #endif #if defined(SIGWINCH) && defined(TIOCGWINSZ) static sig_t SigAttWinch __P(SIGPROTOARG); #endif static void fgtty __P((void)); static void freetty __P((void)); static void brktty __P((void)); #if defined(LOCK) static sig_t DoLock __P(SIGPROTOARG); static void LockTerminal __P((void)); #endif #ifdef COPY_PASTE static pastelen; static char *pastebuffer; #endif #ifdef PASSWORD extern char Password[]; #endif static struct passwd *ppp; /* used for opening a new pty-pair: */ static char PtyName[32], TtyName[32]; /* used for the attacher's tty: */ static char *attach_tty; char *ShellProg; char *ShellArgs[2]; static char inbuf[MAXWIN][IOSIZE]; static inlen[MAXWIN]; static inbuf_ct; static ESCseen; static GotSignal; static char DefaultShell[] = "/bin/sh"; static char DefaultPath[] = ":/usr/ucb:/bin:/usr/bin"; #ifdef hpux char PtyProto[] = "/dev/ptym/ptyXY"; char TtyProto[] = "/dev/pty/ttyXY"; #else # if !(defined(sequent) || defined(_SEQUENT_) || defined(SVR4)) static char PtyProto[] = "/dev/ptyXY"; static char TtyProto[] = "/dev/ttyXY"; # endif #endif /* hpux */ int TtyMode = 0622; #ifdef SOCKDIR char *SockDir = SOCKDIR; #else char *SockDir = ".iscreen"; #endif extern char SockPath[], *SockNamePtr, *SockName; int ServerSocket = -1; static char **NewEnv; char *RcFileName = NULL; char Esc = Ctrl('a'); char MetaEsc = 'a'; char *home; int HasWindow; char *LoginName; char *BellString; char *VisualBellString; char *ActivityString; char *BufferFile; char *PowDetachString; int auto_detach = 1; int iflag, rflag, dflag, lsflag, quietflag, wipeflag; int adaptflag, loginflag = -1, allflag; static intrc, startc, stopc; char HostName[MAXSTR]; int Detached, Suspended; int DeadlyMsg = 1; int AttacherPid; /* Non-Zero in child if we have an attacher */ int MasterPid; int real_uid, real_gid, eff_uid, eff_gid; int default_histheight; int default_startup; int slowpaste; #if defined(BSDJOBS) && !(defined(POSIX) || defined(SYSV)) int DevTty = -1; #endif #ifdef NETHACK int nethackflag = 0; #endif struct mode OldMode, NewMode; struct win *fore = NULL; int WinList = -1; int ForeNum; struct win *wtab[MAXWIN]; struct key ktab[256]; #ifndef FD_SET typedef struct fd_set { int fd_bits[1]; } fd_set; # define FD_ZERO(fd) ((fd)->fd_bits[0] = 0) # define FD_SET(b, fd) ((fd)->fd_bits[0] |= 1 << (b)) # define FD_ISSET(b, fd) ((fd)->fd_bits[0] & 1 << (b)) # define FD_SETSIZE 32 #endif #ifndef WTERMSIG # ifndef BSDWAIT /* if wait is NOT a union: */ # define WTERMSIG(status) (status & 0177) # else # define WTERMSIG(status) status.w_T.w_Termsig # endif #endif #ifndef WIFCORESIG # ifndef BSDWAIT /* if wait is NOT a union: */ # define WIFCORESIG(status) (status & 0200) # else # define WIFCORESIG(status) status.w_T.w_Coredump # endif #endif #ifndef WEXITSTATUS # ifndef BSDWAIT /* if wait is NOT a union: */ # define WEXITSTATUS(status) ((status >> 8) & 0377) # else # define WEXITSTATUS(status) status.w_T.w_Retcode # endif #endif char *shellaka = NULL; /* * Do this last */ #include "extern.h" /* * XXX: Missing system header files. */ #ifdef USEVARARGS # ifndef VPRNT_DECLARED int vsprintf __P((char *, char *, va_list)); # endif /* VPRNT_DECLARED */ #endif int select __P((int, fd_set *, fd_set *, fd_set *, struct timeval *)); static void brktty() { #ifdef POSIX setsid(); /* will break terminal affiliation */ # ifdef BSD ioctl(0, TIOCSCTTY, 0); # endif /* BSD */ #else # ifdef SYSV setpgrp(); /* will break terminal affiliation */ # else # ifdef BSDJOBS if (DevTty) if (ioctl(DevTty, TIOCNOTTY, (char *) 0) != 0) debug2("brktty: ioctl(DevTty=%d, TIOCNOTTY, 0) = %d\n", DevTty, errno); # endif # endif #endif } static void freetty() { brktty(); #if defined(BSDJOBS) && !(defined(POSIX) || defined(SYSV)) if (DevTty >= 0) { close(DevTty); DevTty = -1; } #endif close(0); close(1); close(2); debug("did freetty\n"); } static void fgtty() { #ifdef BSDJOBS int mypid; mypid = getpid(); # ifdef BSDI setsid(); ioctl(0, TIOCSCTTY, 0); # endif /* BSDI */ # ifdef POSIX if (tcsetpgrp(0, mypid)) { debug1("fgtty: tcsetpgrp: %d\n", errno); /* error is likely to have side-effects -- better to warn our user */ SendErrorMsg("fgtty: Could not set process group id in tty"); } # else if (ioctl(0, TIOCSPGRP, &mypid) != 0) debug1("fgtty: TIOSETPGRP: %d\n", errno); /* posix setsid() in brktty() from freetty() already made us leader */ if (setpgrp(0, mypid)) debug1("fgtty: setpgrp: %d\n", errno); # endif /* POSIX */ #endif /* BSDJOBS */ } #ifdef hpux /* * hpux has berkeley signal semantics if we use sigvector, * but not, if we use signal, so we define our own signal() routine. * (jw) */ void (*signal(sig, func)) () int sig; void (*func) (); { struct sigvec osv, sv; sv.sv_handler = func; sv.sv_mask = sigmask(sig); sv.sv_flags = SV_BSDSIG; if (sigvector(sig, &sv, &osv) < 0) return (BADSIG); return (osv.sv_handler); } #endif /* hpux */ #ifndef USEBCOPY void bcopy(s1, s2, len) register char *s1, *s2; register int len; { if (s1 < s2 && s2 < s1 + len) { s1 += len; s2 += len; while (len-- > 0) *--s2 = *--s1; } else while (len-- > 0) *s2++ = *s1++; } #endif /* USEBCOPY */ void bclear(p, n) int n; char *p; { bcopy(blank, p, n); } static void closeallfiles() { int f; #ifdef SVR4 struct rlimit rl; if ((getrlimit(RLIMIT_NOFILE, &rl) == 0) && rl.rlim_max != RLIM_INFINITY) f = rl.rlim_max; else #endif /* SVR4 */ #if defined(SYSV) && !defined(ISC) f = NOFILE; #else /* SYSV && !ISC */ f = getdtablesize(); #endif /* SYSV && !ISC */ while (--f > 2) close(f); } static int InterruptPlease = 0; void main(ac, av) int ac; char **av; { register int n, len; register struct win *p; char *ap, *aka = NULL; char *av0; char socknamebuf[2 * MAXSTR]; int s = 0; fd_set r, w, e; int mflag = 0; struct timeval tv; int nsel; char buf[IOSIZE], *bufp, *myname = (ac == 0) ? "screen" : av[0]; struct stat st; int buflen, tmp; #ifdef _MODE_T /* (jw) */ mode_t oumask; #else int oumask; #endif #ifdef SYSV struct utsname utsnam; #endif /* * First, close all unused descriptors * (otherwise, we might have problems with the select() call) */ closeallfiles(); #ifdef DEBUG (void) mkdir("/tmp/debug", 0777); if ((dfp = fopen("/tmp/debug/screen.front", "w")) == NULL) dfp = stderr; else (void) chmod("/tmp/debug/screen.front", 0666); #endif debug1("-- screen debug started %s\n", *av); #ifdef POSIX debug("POSIX\n"); #endif #ifdef TERMIO debug("TERMIO\n"); #endif #ifdef SYSV debug("SYSV\n"); #endif #ifdef NAMEDPIPE debug("NAMEDPIPE\n"); #endif #if defined(SIGWINCH) && defined(TIOCGWINSZ) debug("Window changing enabled\n"); #endif #ifdef NOREUID debug("NOREUID\n"); #endif #ifdef hpux debug("hpux\n"); #endif #ifdef USEBCOPY debug("USEBCOPY\n"); #endif #ifdef UTMPOK debug("UTMPOK\n"); #endif #ifdef LOADAV debug("LOADAV\n"); #endif #ifdef NETHACK debug("NETHACK\n"); #endif #ifdef TERMINFO debug("TERMINFO\n"); #endif #ifdef NAME_MAX debug1("NAME_MAX = %d\n", NAME_MAX); #endif BellString = SaveStr("Bell in window %"); VisualBellString = SaveStr(" Wuff, Wuff!! "); ActivityString = SaveStr("Activity in window %"); BufferFile = SaveStr("/tmp/screen-exchange"); PowDetachString = 0; default_histheight = DEFAULTHISTHEIGHT; default_startup = (ac > 1) ? 0 : 1; adaptflag = 0; slowpaste = 0; VBellWait = VBELLWAIT; MsgWait = MSGWAIT; MsgMinWait = MSGMINWAIT; CompileKeys((char *)NULL, mark_key_tab); av0 = *av; while (ac > 0) { ap = *++av; if (--ac > 0 && *ap == '-') { switch (ap[1]) { case 'a': allflag = 1; break; case 'A': adaptflag = 1; break; case 'c': if (ap[2]) RcFileName = ap + 2; else { if (--ac == 0) exit_with_usage(myname); RcFileName = *++av; } break; case 'e': if (ap[2]) ap += 2; else { if (--ac == 0) exit_with_usage(myname); ap = *++av; } if (!ParseEscape(ap)) Msg(0, "Two characters are required with -e option."); break; case 'f': switch (ap[2]) { case 'n': case '0': default_flow = FLOW_NOW * 0; break; case 'y': case '1': case '\0': default_flow = FLOW_NOW * 1; break; case 'a': default_flow = FLOW_AUTOFLAG; break; default: exit_with_usage(myname); } break; case 'h': if (ap[2]) default_histheight = atoi(ap + 2); else { if (--ac == 0) exit_with_usage(myname); default_histheight = atoi(*++av); } if (default_histheight < 0) default_histheight = 0; break; case 'i': iflag = 1; break; case 't': /* title is a synonym for AkA */ case 'k': if (ap[2]) aka = ap + 2; else { if (--ac == 0) exit_with_usage(myname); aka = *++av; } break; case 'l': switch (ap[2]) { case 'n': case '0': loginflag = 0; break; case 'y': case '1': case '\0': loginflag = 1; break; case 's': case 'i': lsflag = 1; break; default: exit_with_usage(myname); } break; case 'w': lsflag = 1; wipeflag = 1; break; case 'L': assume_LP = 1; break; case 'm': mflag = 1; break; case 'O': force_vt = 0; break; case 'T': if (ap[2]) { if (strlen(ap+2) < 20) strcpy(screenterm, ap + 2); } else { if (--ac == 0) exit_with_usage(myname); if (strlen(*++av) < 20) strcpy(screenterm, *av); } break; case 'q': quietflag = 1; break; case 'r': case 'R': if (ap[2]) { SockName = ap + 2; if (ac != 1) exit_with_usage(myname); } else if (ac > 1 && *av[1] != '-') { SockName = *++av; ac--; } rflag = (ap[1] == 'r') ? 1 : 2; break; #ifdef REMOTE_DETACH case 'd': dflag = 1; /* FALLTHRU */ case 'D': if (!dflag) dflag = 2; if (ap[2]) SockName = ap + 2; if (ac == 2) { if (*av[1] != '-') { SockName = *++av; ac--; } } break; #endif case 's': if (ap[2]) ShellProg = ap + 2; else { if (--ac == 0) exit_with_usage(myname); ShellProg = *++av; } break; default: exit_with_usage(myname); } } else break; } real_uid = getuid(); real_gid = getgid(); eff_uid = geteuid(); eff_gid = getegid(); if (eff_uid != real_uid) { /* if running with s-bit, we must install a special signal * handler routine that resets the s-bit, so that we get a * core file anyway. */ signal(SIGBUS, CoreDump); signal(SIGSEGV, CoreDump); } if (!ShellProg && (ShellProg = getenv("SHELL")) == 0) ShellProg = DefaultShell; ShellArgs[0] = ShellProg; #ifdef NETHACK nethackflag = (getenv("NETHACKOPTIONS") != NULL); #endif home = getenv("HOME"); /* may or may not return a result. jw. */ if ((LoginName = getlogin()) && LoginName[0] != '\0') { if ((ppp = getpwnam(LoginName)) != (struct passwd *) 0) if (ppp->pw_uid != real_uid) ppp = (struct passwd *) 0; } if (ppp == 0) { if ((ppp = getpwuid(real_uid)) == 0) { #ifdef NETHACK if (nethackflag) Msg(0, "An alarm sounds through the dungeon...\nWarning, the kops are coming."); else #endif Msg(0, "getpwuid() can't identify your account!"); exit(1); } LoginName = ppp->pw_name; } if (home == 0 || *home == '\0') home = ppp->pw_dir; if (strlen(LoginName) > 20) Msg(0, "LoginName too long - sorry."); if (strlen(home) > MAXPATH - 25) Msg(0, "$HOME too long - sorry."); #ifdef PASSWORD strcpy(Password, ppp->pw_passwd); #endif /* ttyname implies isatty */ if (!(attach_tty = ttyname(0))) { #ifdef NETHACK if (nethackflag) Msg(0, "You must play from a terminal."); else #endif Msg(0, "Must be connected to a terminal."); exit(1); } if (strlen(attach_tty) >= MAXPATH) Msg(0, "TtyName too long - sorry."); if ((n = secopen(attach_tty, O_RDWR, 0)) < 0) Msg(0, "Cannot open '%s' - please check.", attach_tty); close(n); debug1("attach_tty is %s\n", attach_tty); #ifdef _MODE_T oumask = umask(0); /* well, unsigned never fails? jw. */ #else if ((oumask = umask(0)) == -1) Msg(errno, "Cannot change umask to zero"); #endif if ((SockDir = getenv("ISCREENDIR")) == NULL) SockDir = getenv("SCREENDIR"); if (SockDir && strlen(SockDir) >= MAXPATH - 1) Msg(0, "ridiculous long $(I)SCREENDIR - try again."); #ifndef SOCKDIR if (SockDir == 0) { sprintf(SockPath, "%s/.iscreen", home); SockDir = SockPath; } #endif if (SockDir) { if (access(SockDir, F_OK)) { if (UserContext() > 0) { if (mkdir(SockDir, 0700)) UserReturn(0); UserReturn(1); } if (UserStatus() <= 0) Msg(0, "Cannot make directory '%s'", SockDir); } if (SockDir != SockPath) strcpy(SockPath, SockDir); } #ifdef SOCKDIR else { SockDir = SOCKDIR; if (stat(SockDir, &st)) { if (mkdir(SockDir, eff_uid ? 0777 : 0755) == -1) Msg(errno, "Cannot make directory '%s'", SockDir); } else { n = eff_uid ? 0777 : 0755; if ((st.st_mode & 0777) != n) Msg(0, "Directory '%s' must have mode %03o.", SockDir, n); } sprintf(SockPath, "%s/S-%s", SockDir, LoginName); if (access(SockPath, F_OK)) { if (mkdir(SockPath, 0700) == -1) Msg(errno, "Cannot make directory '%s'", SockPath); (void) chown(SockPath, real_uid, real_gid); } } #endif if (stat(SockPath, &st) == -1) { Msg(errno, "Cannot access %s", SockPath); } else { #ifdef _POSIX_SOURCE if (S_ISDIR(st.st_mode) == 0) #else if ((st.st_mode & S_IFMT) != S_IFDIR) #endif Msg(0, "%s is not a directory.", SockPath); if (st.st_uid != real_uid) Msg(0, "You are not the owner of %s.", SockPath); if ((st.st_mode & 0777) != 0700) Msg(0, "Directory %s must have mode 700.", SockPath); } strcat(SockPath, "/"); SockNamePtr = SockPath + strlen(SockPath); (void) umask(oumask); #if defined(SYSV) && !defined(ISC) if (uname(&utsnam) == -1) Msg(0, "uname() failed, errno = %d", errno); else { strncpy(HostName, utsnam.nodename, MAXSTR); HostName[(sizeof(utsnam.nodename) <= MAXSTR) ? sizeof(utsnam.nodename) : MAXSTR] = '\0'; } #else (void) gethostname(HostName, MAXSTR); #endif HostName[MAXSTR - 1] = '\0'; if ((ap = index(HostName, '.')) != NULL) *ap = '\0'; GetTTY(0, &OldMode); #ifdef POSIX ospeed = (short) cfgetospeed(&OldMode.tio); #else # ifndef TERMIO ospeed = (short) OldMode.m_ttyb.sg_ospeed; # endif #endif debug1("...setting extern short ospeed = %d\n", ospeed); if (lsflag) { int i; i = FindSocket(0, (int *)NULL); /* MakeClientSocket appended the last (Sock)Name there: */ *SockNamePtr = '\0'; if (i == 0) { #ifdef NETHACK if (nethackflag) Msg(0, "This room is empty (%s)\n", SockPath); else #endif /* NETHACK */ Msg(0, "No Sockets found in %s\n", SockPath); } else Msg(0, "%d Socket%s in %s.\n", i, i > 1 ? "s" : "", SockPath); /* NOTREACHED */ } if (rflag) { debug("screen -r: - is there anybody out there?\n"); #ifdef SHADOWPW setspent(); /* open shadow file while we are still root */ #endif /* SHADOWPW */ if (Attach(MSG_ATTACH)) { Attacher(); /* NOTREACHED */ } debug("screen -r: backend not responding -- still crying\n"); } else if (dflag) { (void) Attach(MSG_DETACH); DeadlyMsg = 0; Msg(0, "[%s %sdetached.]\n", SockName, (dflag > 1 ? "power " : "")); eexit(0); /* NOTREACHED */ } if (!mflag && (SockName = getenv("STY")) != 0 && *SockName != '\0') { setuid(real_uid); setgid(real_gid); s = MakeClientSocket(1, SockName); if (ac == 0) { ac = 1; av = ShellArgs; } av[ac] = aka; SendCreateMsg(s, ac, av, allflag, default_flow, loginflag, default_histheight, screenterm); close(s); exit(0); } #if defined(BSDJOBS) && !(defined(POSIX) || defined(SYSV)) if ((DevTty = open("/dev/tty", O_RDWR | O_NDELAY)) == -1) Msg(errno, "/dev/tty"); #endif switch (MasterPid = fork()) { case -1: Msg(errno, "fork"); /* NOTREACHED */ case 0: break; default: sprintf(socknamebuf, "%d.%s.%s", MasterPid, stripdev(attach_tty), HostName); for (ap = socknamebuf; *ap; ap++) if (*ap == '/') *ap = '-'; SockName = socknamebuf; #ifdef SHADOWPW setspent(); /* open shadow file while we are still root */ #endif /* SHADOWPW */ Attacher(); /* NOTREACHED */ } #ifdef DEBUG if (dfp != stderr) fclose(dfp); if ((dfp = fopen("/tmp/debug/screen.back", "w")) == NULL) dfp = stderr; else (void) chmod("/tmp/debug/screen.back", 0666); #endif debug("-- screen.back debug started\n"); ap = av0 + strlen(av0) - 1; while (ap >= av0) { if (!strncmp("screen", ap, 6)) { strncpy(ap, "SCREEN", 6); /* name this process "SCREEN-BACKEND" */ break; } ap--; } if (ap < av0) *av0 = 'S'; AttacherPid = getppid(); sprintf(socknamebuf, "%d.%s.%s", getpid(), stripdev(attach_tty), HostName); for (ap = socknamebuf; *ap; ap++) if (*ap == '/') *ap = '-'; SockName = socknamebuf; ServerSocket = s = MakeServerSocket(); #ifdef ETCSCREENRC if ((ap = getenv("SYSSCREENRC")) == NULL) StartRc(ETCSCREENRC); else StartRc(ap); #endif StartRc(RcFileName); InitTermcap(); InitTerm(0); MakeNewEnv(); strcpy(display_tty, attach_tty); #ifdef UTMPOK # ifdef apollo ReInitUtmp(); # else InitUtmp(); # endif /* apollo */ #endif #ifdef LOADAV # ifdef NeXT InitNeXTLoadAvg(); /* NeXT load average */ # else InitKmem(); # endif /* !NeXT */ #endif /* LOADAV */ signal(SIGHUP, SigHup); signal(SIGINT, Finit); signal(SIGQUIT, Finit); signal(SIGTERM, Finit); #ifdef BSDJOBS signal(SIGTTIN, SIG_IGN); signal(SIGTTOU, SIG_IGN); #endif InitKeytab(); #ifdef ETCSCREENRC if ((ap = getenv("SYSSCREENRC")) == NULL) FinishRc(ETCSCREENRC); else FinishRc(ap); #endif FinishRc(RcFileName); /* Note: SetMode must be called _after_ FinishRc (flow is set there). */ SetMode(&OldMode, &NewMode); SetTTY(0, &NewMode); if (loginflag == -1) loginflag = LOGINDEFAULT; if (ac == 0) { ac = 1; av = ShellArgs; if (!aka) aka = shellaka; } if (!HasWindow) { debug("We open one default window, as screenrc did not specify one.\n"); if (MakeWindow(aka, av, allflag, default_flow, 0, (char *)0, loginflag, -1, (char *)0) == -1) { Finit(1); /* NOTREACHED */ } } if (default_startup) display_copyright(); #ifdef SYSV signal(SIGCLD, SigChld); #else signal(SIGCHLD, SigChld); #endif signal(SIGINT, SigInt); tv.tv_usec = 0; if (rflag == 2) { #ifdef NETHACK if (nethackflag) Msg(0, "I can't seem to find a... Hey, wait a minute! Here comes a screen now."); else #endif Msg(0, "New screen..."); rflag = 0; } brktty(); for (;;) { /* * check to see if message line should be removed */ if (status) { int time_left; debug("checking status...\n"); time_left = TimeDisplayed + (BellDisplayed ? VBellWait : MsgWait) - time((time_t *)0); if (time_left > 0) { tv.tv_sec = time_left; debug(" not yet.\n"); } else { debug(" removing now.\n"); RemoveStatus(); } } /* * check for I/O on all available I/O descriptors */ FD_ZERO(&r); FD_ZERO(&w); FD_ZERO(&e); if (inbuf_ct > 0) for (n = 0; n < MAXWIN; n++) #ifdef COPY_PASTE /* wrong here? jw. */ if (inlen[n] > 0 || (pastelen > 0 && n == ForeNum)) #else if (inlen[n] > 0) #endif FD_SET(wtab[n]->ptyfd, &w); if (!Detached) FD_SET(0, &r); for (n = WinList; n != -1; n = p->WinLink) { p = wtab[n]; if (p->active && status && !BellDisplayed && !HS) continue; if (p->outlen > 0) continue; if (in_ovl && ovl_blockfore && n == ForeNum) continue; FD_SET(p->ptyfd, &r); } FD_SET(s, &r); (void) fflush(stdout); if (GotSignal && !status) { SigHandler(); continue; } if ((nsel = select(FD_SETSIZE, &r, &w, &e, (status) ? &tv : (struct timeval *) 0)) < 0) { debug1("Bad select - errno %d\n", errno); if (errno != EINTR) { perror("select"); Finit(1); } else { errno = 0; if ((!GotSignal || status) && !InterruptPlease) continue; } } if (InterruptPlease) { char buf[1]; debug("Backend received interrupt\n"); *buf = intrc; write(wtab[ForeNum]->ptyfd, buf, 1); debug1("Backend wrote interrupt to %d\n", ForeNum); InterruptPlease = 0; continue; } if (GotSignal && !status) { SigHandler(); continue; } /* Process a client connect attempt and message */ if (nsel && FD_ISSET(s, &r)) { nsel--; if (!HS) RemoveStatus(); if (in_ovl) { SetOvlCurr(); (*ovl_process)(0, 0); /* We have to abort first!! */ CheckScreenSize(1); /* Change fore */ DeadlyMsg = 0; #ifdef NETHACK if (nethackflag) Msg(0, "KAABLAMM!!! You triggered a land mine!"); else #endif Msg(0, "Aborted because of window change or message."); } else CheckScreenSize(1); /* Change fore */ ReceiveMsg(s); continue; } /* * Write the stored user input to the window descriptors first. * We do not want to choke, if he types fast. */ if (nsel && inbuf_ct > 0) { for (n = 0; n < MAXWIN ; n++) { if (inlen[n] <= 0) continue; tmp = wtab[n]->ptyfd; if (FD_ISSET(tmp, &w)) { if ((len = write(tmp, inbuf[n], inlen[n])) > 0) { if ((inlen[n] -= len) == 0) inbuf_ct--; bcopy(inbuf[n] + len, inbuf[n], inlen[n]); } if (--nsel == 0) break; } } } /* Read, process, and store the user input */ if (nsel && FD_ISSET(0, &r)) { nsel--; if (!HS) RemoveStatus(); if (ESCseen) { buf[0] = Esc; buflen = read(0, buf + 1, IOSIZE - 1) + 1; ESCseen = 0; } else buflen = read(0, buf, IOSIZE); if (buflen < 0) { debug1("Read error: %d - SigHup()ing!\n", errno); SigHup(SIGARG); continue; } if (buflen == 0) { debug("Found EOF - SigHup()ing!\n"); SigHup(SIGARG); continue; } bufp = buf; if (in_ovl) { SetOvlCurr(); (*ovl_process)(&bufp, &buflen); } while (buflen > 0) { n = ForeNum; len = inlen[n]; bufp = ProcessInput(bufp, &buflen, inbuf[n], &inlen[n], sizeof *inbuf); if (inlen[n] > 0 && len == 0) inbuf_ct++; } if (inbuf_ct > 0) continue; } if (GotSignal && !status) { SigHandler(); continue; } #ifdef COPY_PASTE /* Write the copybuffer contents first, if any. jw. */ if (pastelen > 0) { n = ForeNum; debug1("writing pastebuffer (%d)\n", pastelen); tmp = wtab[n]->ptyfd; if ( /* FD_ISSET(tmp, &w) && */ (len = write(tmp, pastebuffer, pastelen > IOSIZE ? IOSIZE : pastelen)) > 0) { pastebuffer += len; pastelen -= len; debug1("%d bytes pasted\n", len); if (slowpaste > 0) { struct timeval t; debug1("slowpaste %d\n", slowpaste); t.tv_usec = (long) (slowpaste * 1000); t.tv_sec = 0; select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &t); } else continue; } /* * We could not paste? Let's see if the pty did echo the lot. * Then continue by processing some pty output. */ } #endif if (GotSignal && !status) { SigHandler(); continue; } /* Read and process the output from the window descriptors */ for (n = WinList; n != -1; n = p->WinLink) { p = wtab[n]; if (in_ovl && ovl_blockfore && n == ForeNum) continue; if (p->outlen) WriteString(p, p->outbuf, p->outlen); else if (nsel && FD_ISSET(p->ptyfd, &r)) { nsel--; if ((len = read(p->ptyfd, buf, IOSIZE)) == -1) { #ifdef EWOULDBLOCK if (errno == EWOULDBLOCK) len = 0; #endif } #if defined(TIOCPKT) && !defined(sgi) if (buf[0]) { debug1("PAKET %x\n", buf[0]); if (buf[0] & TIOCPKT_NOSTOP) { NewAutoFlow(p, 0); } if (buf[0] & TIOCPKT_DOSTOP) { NewAutoFlow(p, 1); } } if (len > 1) WriteString(p, buf + 1, len - 1); #else /* TIOCPKT && !sgi */ if (len > 0) WriteString(p, buf, len); #endif /* TIOCPKT && !sgi */ } if (p->bell == BELL_ON) { p->bell = BELL_DONE; Msg(0, MakeWinMsg(BellString, n)); if (p->monitor == MON_FOUND) p->monitor = MON_DONE; } else if (p->bell == BELL_VISUAL) { if (!BellDisplayed) { p->bell = BELL_DONE; Msg(0, VisualBellString); BellDisplayed = 1; } } else if (p->monitor == MON_FOUND) { p->monitor = MON_DONE; Msg(0, MakeWinMsg(ActivityString, n)); } } if (GotSignal && !status) SigHandler(); #ifdef DEBUG if (nsel) debug1("Left over nsel: %d\n", nsel); #endif } /* NOTREACHED */ } static void SigHandler() { struct stat st; while (GotSignal) { GotSignal = 0; DoWait(); #ifdef SYSV signal(SIGCLD, SigChld); #endif } if (stat(SockPath, &st) == -1) { debug1("SigHandler: Yuck! cannot stat '%s'\n", SockPath); if (!RecoverSocket()) { debug("SCREEN cannot recover from corrupt Socket, bye\n"); Finit(1); } else debug1("'%s' reconstructed\n", SockPath); } else debug2("SigHandler: stat '%s' o.k. (%03o)\n", SockPath, st.st_mode); } #ifdef DEBUG int FEpanic; sig_t FEChld(SIGDEFARG) { FEpanic=1; #ifndef SIGVOID return((sig_t) 0); #endif } #endif static sig_t SigChld(SIGDEFARG) { debug("SigChld()\n"); GotSignal = 1; #ifndef SIGVOID return((sig_t) 0); #endif } sig_t SigHup(SIGDEFARG) { debug("SigHup()\n"); if (auto_detach) Detach(D_DETACH); else Finit(0); #ifndef SIGVOID return((sig_t) 0); #endif } /* * the frontend's Interrupt handler * we forward SIGINT to the backend */ static sig_t AttacherSigInt(SIGDEFARG) { Kill(MasterPid, SIGINT); signal(SIGINT, AttacherSigInt); # ifndef SIGVOID return (sig_t) 0; # endif } /* * the backend's Interrupt handler * we cannot insert the intrc directly, as we never know * if fore and ForeNum are valid. */ static sig_t SigInt(SIGDEFARG) { #if HAZARDOUS char buf[1]; debug("SigInt()\n"); *buf = (char) intrc; inlen[ForeNum] = 0; if (fore && !in_ovl) write(fore->ptyfd, buf, 1); #else debug("SigInt() careful\n"); InterruptPlease = 1; signal(SIGINT, SigInt); #endif #ifndef SIGVOID return((sig_t) 0); #endif } static sig_t CoreDump(sig) int sig; { setgid(getgid()); setuid(getuid()); unlink("core"); fprintf(stderr, "\r\n[screen caught signal %d.%s]\r\n", sig, #ifdef SHADOWPW "" #else /* SHADOWPW */ " (core dumped)" #endif /* SHADOWPW */ ); fflush(stderr); Kill(AttacherPid, SIG_BYE); #ifdef SHADOWPW eexit(sig); #else /* SHADOWPW */ abort(); #endif /* SHADOWPW */ #ifndef SIGVOID return((sig_t) 0); #endif } static void DoWait() { register int n, next, pid; #ifdef BSDWAIT union wait wstat; #else int wstat; #endif #ifdef BSDJOBS # ifndef BSDWAIT while ((pid = waitpid(-1, &wstat, WNOHANG | WUNTRACED)) > 0) # else while ((pid = wait3(&wstat, WNOHANG | WUNTRACED, (struct rusage *) 0)) > 0) # endif #else /* BSDJOBS */ while ((pid = wait(&wstat)) < 0) if (errno != EINTR) break; if (pid >= 0) #endif /* BSDJOBS */ { for (n = WinList; n != -1; n = next) { next = wtab[n]->WinLink; if (pid == wtab[n]->wpid) { #ifdef BSDJOBS if (WIFSTOPPED(wstat)) { # ifdef NETHACK if (nethackflag) Msg(0, "You regain consciousness."); else # endif /* NETHACK */ Msg(0, "Child has been stopped, restarting."); debug1("WIFSTOPPED: %d SIGCONT\n", wtab[n]->wpid); if (killpg(wtab[n]->wpid, SIGCONT)) kill(wtab[n]->wpid, SIGCONT); } else #endif KillWindow(n); } } } } void KillWindow(n) int n; { register int i; /* * Remove window from linked list. */ if (n == WinList) /* WinList = ForeNum */ { RemoveStatus(); WinList = fore->WinLink; fore = 0; } else { i = WinList; while (wtab[i]->WinLink != n) i = wtab[i]->WinLink; wtab[i]->WinLink = wtab[n]->WinLink; } FreeWindow(wtab[n]); wtab[n] = 0; if (inlen[n] > 0) { inlen[n] = 0; inbuf_ct--; } /* * If the foreground window disappeared check the head of the linked list * of windows for the most recently used window. If no window is alive at * all, exit. */ if (WinList == -1) Finit(0); if (!fore) SwitchWindow(WinList); } static sig_t Finit(i) int i; { register int n, next; #ifdef SYSV signal(SIGCLD, SIG_IGN); #else signal(SIGCHLD, SIG_IGN); #endif signal(SIGHUP, SIG_IGN); debug1("Finit(%d);\n", i); for (n = WinList; n != -1; n = next) { next = wtab[n]->WinLink; FreeWindow(wtab[n]); } FinitTerm(); SetTTY(0, &OldMode); #ifdef UTMPOK RestoreLoginSlot(); #endif printf("\n[screen is terminating]\n"); freetty(); if (ServerSocket != -1) { debug1("we unlink(%s)\n", SockPath); (void) unlink(SockPath); } Kill(AttacherPid, SIG_BYE); exit(i); #ifndef SIGVOID return((sig_t) 0); #endif } void eexit(e) int e; { if (ServerSocket != -1) { debug1("we unlink(%s)\n", SockPath); (void) unlink(SockPath); } exit(e); } static void InitKeytab() { register unsigned int i; for (i = 0; i < sizeof(ktab)/sizeof(*ktab); i++) ktab[i].type = KEY_IGNORE; ktab['h'].type = ktab[Ctrl('h')].type = KEY_HARDCOPY; #ifdef BSDJOBS ktab['z'].type = ktab[Ctrl('z')].type = KEY_SUSPEND; #endif ktab['c'].type = ktab[Ctrl('c')].type = KEY_SHELL; ktab[' '].type = ktab[Ctrl(' ')].type = ktab['n'].type = ktab[Ctrl('n')].type = KEY_NEXT; ktab['-'].type = ktab['p'].type = ktab[Ctrl('p')].type = KEY_PREV; ktab['k'].type = ktab[Ctrl('k')].type = KEY_KILL; ktab['l'].type = ktab[Ctrl('l')].type = KEY_REDISPLAY; ktab['w'].type = ktab[Ctrl('w')].type = KEY_WINDOWS; ktab['v'].type = ktab[Ctrl('v')].type = KEY_VERSION; ktab['q'].type = ktab[Ctrl('q')].type = KEY_XON; ktab['s'].type = ktab[Ctrl('s')].type = KEY_XOFF; ktab['t'].type = ktab[Ctrl('t')].type = KEY_TIME; ktab['i'].type = ktab[Ctrl('i')].type = KEY_INFO; ktab['m'].type = ktab[Ctrl('m')].type = KEY_LASTMSG; ktab['A'].type = KEY_AKA, ktab['A'].args = NULL; ktab['L'].type = KEY_LOGIN; ktab[','].type = KEY_LICENSE; ktab['W'].type = KEY_WIDTH; ktab['.'].type = KEY_TERMCAP; ktab[Ctrl('\\')].type = KEY_QUIT; ktab['d'].type = ktab[Ctrl('d')].type = KEY_DETACH; ktab['r'].type = ktab[Ctrl('r')].type = KEY_WRAP; ktab['f'].type = ktab[Ctrl('f')].type = KEY_FLOW; ktab['C'].type = KEY_CLEAR; ktab['Z'].type = KEY_RESET; ktab['H'].type = KEY_LOGTOGGLE; if (Esc != MetaEsc) ktab[Esc].type = KEY_OTHER; else ktab[Esc].type = KEY_IGNORE; ktab['M'].type = KEY_MONITOR; ktab['?'].type = KEY_HELP; for (i = 0; i <= 9; i++) ktab['0' + i].type = (enum keytype) (i + (int)KEY_0); ktab[Ctrl('G')].type = KEY_VBELL; ktab[':'].type = KEY_COLON; #ifdef COPY_PASTE ktab['['].type = ktab[Ctrl('[')].type = KEY_COPY; ktab[']'].type = ktab[Ctrl(']')].type = KEY_PASTE; ktab['{'].type = KEY_HISTORY; ktab['}'].type = KEY_HISTNEXT; ktab['>'].type = KEY_WRITE_BUFFER; ktab['<'].type = KEY_READ_BUFFER; ktab['='].type = KEY_REMOVE_BUFFERS; #endif #ifdef POW_DETACH ktab['D'].type = KEY_POW_DETACH; #endif #ifdef LOCK ktab['x'].type = ktab[Ctrl('x')].type = KEY_LOCK; #endif } /* * this is a braindamaged hack: if (obuf == NULL) then we provided * a key_type as a second char in ibuf. not a key. */ char *ProcessInput(ibuf, pilen, obuf, polen, obuf_size) char *ibuf, *obuf; register int *pilen, *polen, obuf_size; { register int n; register enum keytype k; register char *s, *p; char buf[2]; int newwidth; if (!obuf) obuf_size = 0; for (s = ibuf, p = obuf + *polen; *pilen > 0; --*pilen, s++) { if (*s == Esc) { debug2("'%c %c ", s[0], s[1]); debug2("%c %c' ", s[2], s[3]); if (*pilen > 1) { --*pilen; s++; #if defined(GOULD_NP1) k = (obuf)?(ktab[*s].type):(enum keytype)(int)(*s); #else k = (obuf)?(ktab[*s].type):(enum keytype)(*s); #endif debug2("Processinput C-A %02x '%c' ", k, k); debug1("%s\n", (obuf)?"std":"NOOBUF"); if (*s == MetaEsc) { if (*polen < obuf_size) { *p++ = Esc; ++*polen; } } else if ((int)k >= (int)KEY_0 && (int)k <= (int)KEY_9) SwitchWindow((int)k - (int)KEY_0); else switch (k) { case KEY_TERMCAP: WriteFile(DUMP_TERMCAP); break; case KEY_HARDCOPY: WriteFile(DUMP_HARDCOPY); break; case KEY_LOGTOGGLE: LogToggle(); break; #ifdef BSDJOBS case KEY_SUSPEND: *pilen = 0; Detach(D_STOP); break; #endif case KEY_SHELL: debug("calling MakeWindow with shell\n"); MakeWindow(shellaka, ShellArgs, allflag, default_flow, 0, (char *) 0, loginflag, -1, (char *)0); break; case KEY_NEXT: if (MoreWindows()) SwitchWindow(NextWindow()); break; case KEY_PREV: if (MoreWindows()) SwitchWindow(PreviousWindow()); break; case KEY_KILL: KillWindow(n = ForeNum); #ifdef NETHACK if (nethackflag) Msg(0, "You destroy poor window %d", n); #endif break; case KEY_QUIT: Finit(0); /* NOTREACHED */ case KEY_DETACH: *pilen = 0; Detach(D_DETACH); break; #ifdef POW_DETACH case KEY_POW_DETACH: *pilen = 0; if (obuf) { buf[0] = *s; buf[1] = '\0'; Msg(0, buf); read(0, buf, 1); if (*buf != *s) { write(1, "\007", 1); RemoveStatus(); #ifdef NETHACK if (nethackflag) Msg(0, "The blast of disintegration whizzes by you!"); #endif break; } } Detach(D_POWER); /* detach and kill Attacher's * parent */ break; #endif case KEY_REDISPLAY: Activate(0); break; case KEY_WINDOWS: ShowWindows(); break; case KEY_VERSION: Msg(0, "screen %d.%.2d.%.2d%s (%s) %s", REV, VERS, PATCHLEVEL, STATE, ORIGIN, DATE); break; case KEY_TIME: ShowTime(); break; case KEY_INFO: ShowInfo(); break; case KEY_OTHER: if (MoreWindows()) SwitchWindow(fore->WinLink); break; case KEY_XON: if (*polen < obuf_size) { *p++ = Ctrl('q'); ++*polen; } break; case KEY_XOFF: if (*polen < obuf_size) { *p++ = Ctrl('s'); ++*polen; } break; #ifdef LOCK case KEY_LOCK: Detach(D_LOCK); /* do it micha's way */ break; #endif case KEY_WIDTH: if (Z0 || WS) { if (fore->width == Z0width) newwidth = Z1width; else if (fore->width == Z1width) newwidth = Z0width; else if (fore->width > (Z0width+Z1width)/2) newwidth = Z0width; else newwidth = Z1width; ChangeWindowSize(fore, newwidth, fore->height); Activate(fore->norefresh); } else Msg(0, "Your termcap does not specify how to change the terminal's width."); break; case KEY_LOGIN: SlotToggle(0); break; case KEY_AKA: if (!ktab[*s].args) InputAKA(); else strncpy(fore->cmd + fore->akapos, ktab[*s].args[0], 20); break; case KEY_COLON: InputColon(); break; case KEY_LASTMSG: Msg(0, "%s", LastMsg); break; case KEY_SET: DoSet(ktab[*s].args); break; case KEY_SCREEN: debug3("KEY_SCREEN DoSc(, ktab[%d].args(='%s','%s')...)\n", *s, ktab[*s].args[0], ktab[*s].args[1]); DoScreen("key", ktab[*s].args); break; case KEY_CREATE: debug2("KEY_CREATE MaWi(0, ktab[%d].args(='%s')...)\n", *s, ktab[*s].args); MakeWindow((char *) 0, ktab[*s].args, allflag, default_flow, 0, (char *) 0, loginflag, -1, (char *)0); break; case KEY_WRAP: fore->wrap = !fore->wrap; Msg(0, "%cwrap", fore->wrap ? '+' : '-'); break; case KEY_FLOW: if (fore->flow & FLOW_AUTOFLAG) fore->flow = (fore->flow & FLOW_AUTO) | FLOW_NOW; else if (fore->flow & FLOW_NOW) fore->flow &= ~FLOW_NOW; else fore->flow = fore->flow ? FLOW_AUTOFLAG|FLOW_AUTO|FLOW_NOW : FLOW_AUTOFLAG; SetFlow(fore->flow & FLOW_NOW); Msg(0, "%cflow%s", (fore->flow & FLOW_NOW) ? '+' : '-', (fore->flow & FLOW_AUTOFLAG) ? "(auto)" : ""); break; case KEY_CLEAR: if (fore->state == LIT) WriteString(fore, "\033[H\033[J", 6); break; case KEY_RESET: if (fore->state == LIT) WriteString(fore, "\033c", 2); break; case KEY_MONITOR: if (fore->monitor == MON_OFF) { fore->monitor = MON_ON; Msg(0, "Window %d is now being monitored for all activity.", ForeNum); } else { fore->monitor = MON_OFF; Msg(0, "Window %d is no longer being monitored for activity.", ForeNum); } break; case KEY_HELP: display_help(); break; case KEY_LICENSE: display_copyright(); break; #ifdef COPY_PASTE case KEY_COPY: (void) MarkRoutine(PLAIN); break; case KEY_HISTNEXT: if (MarkRoutine(CRAZY)) if (copybuffer != NULL) { pastelen = copylen; pastebuffer = copybuffer; debug("histnext\n"); } break; case KEY_HISTORY: if (MarkRoutine(TRICKY)) if (copybuffer != NULL) { pastelen = copylen; pastebuffer = copybuffer; debug1("history new copylen: %d\n", pastelen); } break; case KEY_PASTE: if (copybuffer == NULL) { #ifdef NETHACK if (nethackflag) Msg(0, "Nothing happens."); else #endif Msg(0, "empty buffer"); copylen = 0; break; } pastelen = copylen; pastebuffer = copybuffer; break; case KEY_WRITE_BUFFER: if (copybuffer == NULL) { #ifdef NETHACK if (nethackflag) Msg(0, "Nothing happens."); else #endif Msg(0, "empty buffer"); copylen = 0; break; } WriteFile(DUMP_EXCHANGE); break; case KEY_READ_BUFFER: ReadFile(); break; case KEY_REMOVE_BUFFERS: KillBuffers(); break; #endif /* COPY_PASTE */ case KEY_VBELL: if (visual_bell) { visual_bell = 0; Msg(0, "switched to audible bell"); } else { visual_bell = 1; Msg(0, "switched to visual bell"); } break; default: break; } } else ESCseen = 1; --*pilen; s++; break; } else if (*polen < obuf_size) { *p++ = *s; ++*polen; } } return (s); } /* Send a terminal report as if it were typed. */ void Report(wp, fmt, n1, n2) struct win *wp; char *fmt; int n1, n2; { register int n, len; char rbuf[40]; sprintf(rbuf, fmt, n1, n2); len = strlen(rbuf); for (n = 0; n < MAXWIN; n++) { if (wp == wtab[n]) { if ((unsigned)(inlen[n] + len) <= sizeof *inbuf) { bcopy(rbuf, inbuf[n] + inlen[n], len); if (inlen[n] == 0) inbuf_ct++; inlen[n] += len; } break; } }/* for */ } void SwitchWindow(n) int n; { debug1("SwitchWindow %d\n", n); if (!wtab[n]) { ShowWindows(); return; } if (wtab[n] == fore) { Msg(0, "This IS window %d.", n); return; } SetForeWindow(n); if (!Detached && !in_ovl) Activate(fore->norefresh); } static void SetForeWindow(n) int n; { /* * If we come from another window, make it inactive. */ if (fore) fore->active = 0; ForeNum = n; fore = wtab[n]; if (!Detached && !in_ovl) fore->active = 1; /* * Place the window at the head of the most-recently-used list. */ if ((n = WinList) != ForeNum) { /* * we had a bug here. we sometimes ran into n = -1; and crashed. * (this is not the perfect fix. "if (...) break;" inserted. jw.) */ while (wtab[n]->WinLink != ForeNum) { if (wtab[n]->WinLink == -1) break; n = wtab[n]->WinLink; } wtab[n]->WinLink = fore->WinLink; fore->WinLink = WinList; WinList = ForeNum; } } static int NextWindow() { register struct win **pp; for (pp = wtab + ForeNum + 1; pp != wtab + ForeNum; ++pp) { if (pp == wtab + MAXWIN) pp = wtab; if (*pp) break; } return pp - wtab; } static int PreviousWindow() { register struct win **pp; for (pp = wtab + ForeNum - 1; pp != wtab + ForeNum; --pp) { if (pp < wtab) pp = wtab + MAXWIN - 1; if (*pp) break; } return pp - wtab; } static int MoreWindows() { if (fore->WinLink != -1) return 1; #ifdef NETHACK if (nethackflag) Msg(0, "You cannot escape from window %d!", ForeNum); else #endif Msg(0, "No other window."); return 0; } static void FreeWindow(wp) struct win *wp; { #ifdef UTMPOK RemoveUtmp(wp); #endif #ifdef SUIDROOT (void) chmod(wp->tty, 0666); (void) chown(wp->tty, 0, 0); #endif close(wp->ptyfd); if (wp->logfp != NULL) fclose(wp->logfp); ChangeWindowSize(wp, 0, 0); Free(wp); } int MakeWindow(prog, args, aflag, flowflag, StartAt, dir, lflag, histheight, term) char *prog, **args, *dir; int aflag, flowflag, StartAt, lflag, histheight; char *term; /* if term is nonzero we assume it "vt100" or the like.. */ { register struct win **pp, *p; register int n, f; int tf, tlflag; char ebuf[10]; #ifndef TIOCSWINSZ char libuf[20], cobuf[20]; #endif char tebuf[25]; pp = wtab + StartAt; do { if (*pp == 0) break; if (++pp == wtab + MAXWIN) pp = wtab; } while (pp != wtab + StartAt); if (*pp) { Msg(0, "No more windows."); return -1; } if (((tlflag = lflag) == -1) && ((tlflag = loginflag) == -1)) tlflag = LOGINDEFAULT; #ifdef USRLIMIT /* * Count current number of users, if logging windows in. */ if (tlflag == 1 && CountUsers() >= USRLIMIT) { Msg(0, "User limit reached. Window will not be logged in."); tlflag = 0; } #endif n = pp - wtab; debug1("Makewin creating %d\n", n); if ((f = OpenPTY()) == -1) { Msg(0, "No more PTYs."); return -1; } #ifdef SYSV (void) fcntl(f, F_SETFL, O_NDELAY); #else (void) fcntl(f, F_SETFL, FNDELAY); #endif #ifdef TIOCPKT { # ifdef sgi /* * on IRIX 3.3, regardless of stream head's read mode (RNORM/RMSGN/RMSGD) * we loose data in TIOCPKT mode if our buffer is too small (IOSIZE) * to hold the whole packet at first read(). * (Marc Boucher) */ int flag = 0; # else /* sgi */ int flag = 1; # endif /* sgi */ if (ioctl(f, TIOCPKT, &flag)) { Msg(errno, "TIOCPKT ioctl"); close(f); return -1; } } #endif if ((p = (struct win *) malloc(sizeof(struct win))) == 0) { close(f); Msg_nomem; return -1; } bzero((char *) p, (int) sizeof(struct win)); p->ptyfd = f; p->aflag = aflag; if (flowflag < 0) flowflag = default_flow; p->flow = flowflag | ((flowflag & FLOW_AUTOFLAG) ? (FLOW_AUTO|FLOW_NOW) : FLOW_AUTO); if (!prog) prog = Filename(args[0]); strncpy(p->cmd, prog, MAXSTR - 1); if ((prog = rindex(p->cmd, '|')) != NULL) { *prog++ = '\0'; prog += strlen(prog); p->akapos = prog - p->cmd; p->autoaka = 0; } else p->akapos = 0; p->monitor = default_monitor; p->norefresh = 0; strncpy(p->tty, TtyName, MAXSTR - 1); #ifdef SUIDROOT (void) chown(TtyName, real_uid, real_gid); # ifdef UTMPOK (void) chmod(TtyName, tlflag ? TtyMode : (TtyMode & ~022)); # else (void) chmod(TtyName, TtyMode); # endif #endif if (histheight < 0) histheight = default_histheight; if (ChangeWindowSize(p, default_width, default_height)) { FreeWindow(p); return -1; } ChangeScrollback(p, histheight, default_width); ResetScreen(p); debug("forking...\n"); switch (p->wpid = fork()) { case -1: Msg(errno, "fork"); FreeWindow(p); return -1; case 0: signal(SIGHUP, SIG_DFL); signal(SIGINT, SIG_DFL); signal(SIGQUIT, SIG_DFL); signal(SIGTERM, SIG_DFL); #ifdef BSDJOBS signal(SIGTTIN, SIG_DFL); signal(SIGTTOU, SIG_DFL); #endif setuid(real_uid); setgid(real_gid); if (dir && chdir(dir) == -1) { SendErrorMsg("Cannot chdir to %s: %s", dir, sys_errlist[errno]); eexit(1); } freetty(); if ((tf = open(TtyName, O_RDWR)) == -1) { SendErrorMsg("Cannot open %s: %s", TtyName, sys_errlist[errno]); eexit(1); } #ifdef SVR4 if (ioctl(tf, I_PUSH, "ptem")) { SendErrorMsg("Cannot I_PUSH ptem %s %s", TtyName, sys_errlist[errno]); eexit(1); } if (ioctl(tf, I_PUSH, "ldterm")) { SendErrorMsg("Cannot I_PUSH ldterm %s %s", TtyName, sys_errlist[errno]); eexit(1); } if (ioctl(tf, I_PUSH, "ttcompat")) { SendErrorMsg("Cannot I_PUSH ttcompat %s %s", TtyName, sys_errlist[errno]); eexit(1); } #endif (void) dup2(tf, 0); (void) dup2(tf, 1); (void) dup2(tf, 2); #ifdef DEBUG dfp = stderr; #endif closeallfiles(); fgtty(); #ifdef TIOCSWINSZ glwz.ws_col = p->width; glwz.ws_row = p->height; (void) ioctl(0, TIOCSWINSZ, &glwz); #else sprintf(libuf, "LINES=%d", p->height); sprintf(cobuf, "COLUMNS=%d", p->width); NewEnv[4] = libuf; NewEnv[5] = cobuf; #endif SetTTY(0, &OldMode); if (aflag) NewEnv[2] = MakeTermcap(1); else NewEnv[2] = Termcap; if (term && *term && strcmp(screenterm, term) && (strlen(term) < 20)) { char *s1, *s2, tl; sprintf(tebuf, "TERM=%s", term); debug2("Makewindow %d with %s\n", n, tebuf); tl = strlen(term); NewEnv[1] = tebuf; if (s1 = index(Termcap, '|')) { if (s2 = index(++s1, '|')) { if (strlen(Termcap) - (s2 - s1) + tl < 1024) { bcopy(s2, s1 + tl, strlen(s2) + 1); bcopy(term, s1, tl); } } } } sprintf(ebuf, "WINDOW=%d", n); NewEnv[3] = ebuf; execvpe(*args, args, NewEnv); SendErrorMsg("Cannot exec %s: %s", *args, sys_errlist[errno]); exit(1); } /* end fork switch */ /* * Place the newly created window at the head of the most-recently-used list. */ *pp = p; p->WinLink = WinList; WinList = n; HasWindow = 1; #ifdef UTMPOK debug1("MakeWindow will %slog in.\n", tlflag?"":"not "); if (tlflag == 1) SetUtmp(p, n); else p->slot = (slot_t) -1; #endif SetForeWindow(n); Activate(0); return n; } static void execvpe(prog, args, env) char *prog, **args, **env; { register char *path, *p; char buf[1024]; char *shargs[MAXARGS + 1]; register int i, 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]) != NULL; ++i) ; execve(DefaultShell, shargs, env); return; case EACCES: eaccess = 1; break; case ENOMEM: case E2BIG: case ETXTBSY: return; } } while (*path); if (eaccess) errno = EACCES; } static void LogToggle() { char buf[1024]; sprintf(buf, "screenlog.%d", ForeNum); if (fore->logfp != NULL) { Msg(0, "Logfile \"%s\" closed.", buf); fclose(fore->logfp); fore->logfp = NULL; return; } if ((fore->logfp = secfopen(buf, "a")) == NULL) { Msg(errno, "Error opening logfile \"%s\"", buf); return; } Msg(0, "%s logfile \"%s\"", ftell(fore->logfp) ? "Appending to" : "Creating", buf); } #ifdef NOREUID static int UserPID; static sig_t (*Usersigcld)__P(SIGPROTOARG); #endif static int UserSTAT; int UserContext() { #ifdef NOREUID if (eff_uid == real_uid) return(1); # ifdef SYSV Usersigcld = signal(SIGCLD, SIG_DFL); # else Usersigcld = signal(SIGCHLD, SIG_DFL); # endif debug("UserContext: forking.\n"); switch (UserPID = fork()) { case -1: Msg(errno, "fork"); return -1; case 0: signal(SIGHUP, SIG_DFL); signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_DFL); signal(SIGTERM, SIG_DFL); # ifdef BSDJOBS signal(SIGTTIN, SIG_DFL); signal(SIGTTOU, SIG_DFL); # endif setuid(real_uid); setgid(real_gid); return 1; default: return 0; } #else setreuid(eff_uid, real_uid); setregid(eff_gid, real_gid); return 1; #endif } void UserReturn(val) int val; { #if defined(NOREUID) if (eff_uid == real_uid) UserSTAT = val; else exit(val); #else setreuid(real_uid, eff_uid); setregid(real_gid, eff_gid); UserSTAT = val; #endif } int UserStatus() { #ifdef NOREUID int i; # ifdef BSDWAIT union wait wstat; # else int wstat; # endif if (eff_uid == real_uid) return UserSTAT; if (UserPID < 0) return -1; while ((errno = 0, i = wait(&wstat)) != UserPID) if (i < 0 && errno != EINTR) break; # ifdef SYSV (void) signal(SIGCLD, Usersigcld); # else (void) signal(SIGCHLD, Usersigcld); # endif if (i == -1) return -1; return (WEXITSTATUS(wstat)); #else return UserSTAT; #endif } static void ShowWindows() { char buf[1024]; register char *s; register struct win **pp, *p; register int i, OtherNum = fore->WinLink; register char *cmd; for (i = 0, s = buf, pp = wtab; pp < wtab + MAXWIN; ++i, ++pp) { if ((p = *pp) == 0) continue; if (p->akapos) { if (*(p->cmd + p->akapos) && *(p->cmd + p->akapos - 1) != ':') cmd = p->cmd + p->akapos; else cmd = p->cmd + strlen(p->cmd) + 1; } else cmd = p->cmd; if (s - buf + 5 + strlen(cmd) > fore->width - 1) break; if (s > buf) { *s++ = ' '; *s++ = ' '; } *s++ = i + '0'; if (i == ForeNum) *s++ = '*'; else if (i == OtherNum) *s++ = '-'; if (p->monitor == MON_DONE) *s++ = '@'; if (p->bell == BELL_DONE) *s++ = '!'; #ifdef UTMPOK if (p->slot != (slot_t) 0 && p->slot != (slot_t) -1) *s++ = '$'; #endif if (p->logfp != NULL) { strcpy(s, "(L)"); s += 3; } *s++ = ' '; strcpy(s, cmd); s += strlen(s); if (i == ForeNum) { /* * this is usually done by Activate(), but when looking * on your current window, you may get annoyed, as there is still * that temporal '!' and '@' displayed. * So we remove that after displaying it once. */ p->bell = BELL_OFF; if (p->monitor != MON_OFF) p->monitor = MON_ON; } } *s++ = ' '; *s = '\0'; Msg(0, "%s", buf); } #ifdef LOADAV_3LONGS extern long loadav[3]; #else # ifdef LOADAV_4LONGS extern long loadav[4]; # else # ifdef LOADAV_NEXT extern float loadav; # else extern double loadav[3]; # endif # endif #endif extern avenrun; static void ShowTime() { char buf[512]; #ifdef LOADAV char *p; #endif struct tm *tp; time_t now; (void) time(&now); tp = localtime(&now); sprintf(buf, "%2d:%02.2d:%02.2d %s", tp->tm_hour, tp->tm_min, tp->tm_sec, HostName); #ifdef LOADAV if (avenrun && GetAvenrun()) { p = buf + strlen(buf); # ifdef LOADAV_3LONGS sprintf(p, " %2.2f %2.2f %2.2f", (double) loadav[0] / FSCALE, (double) loadav[1] / FSCALE, (double) loadav[2] / FSCALE); # else # ifdef LOADAV_4LONGS sprintf(p, " %2.2f %2.2f %2.2f %2.2f", (double) loadav[0] / 100, (double) loadav[1] / 100, (double) loadav[2] / 100, (double) loadav[3] / 100); # else # ifdef LOADAV_NEXT sprintf(p, " %2.2f", loadav); # else # ifdef apollo sprintf(p, " %2.2f %2.2f %2.2f", loadav[0]/65536.0, loadav[1]/65536.0, loadav[2]/65536.0); # else sprintf(p, " %2.2f %2.2f %2.2f", loadav[0], loadav[1], loadav[2]); # endif /* apollo */ # endif /* LOADAV_NEXT */ # endif /* LOADAV_4LONGS */ # endif /* LOADAV_3LONGS */ } #endif /* LOADAV */ Msg(0, "%s", buf); } static void ShowInfo() { char buf[512], *p; register struct win *wp = fore; register int i; sprintf(buf, "(%d,%d)/(%d,%d)+%d %c%sflow %cins %corg %cwrap %capp %clog %cmon %cr", wp->x + 1, wp->y + 1, wp->width, wp->height, wp->histheight, (wp->flow & FLOW_NOW) ? '+' : '-', (wp->flow & FLOW_AUTOFLAG) ? "" : ((wp->flow & FLOW_AUTO) ? "(+)" : "(-)"), wp->insert ? '+' : '-', wp->origin ? '+' : '-', wp->wrap ? '+' : '-', wp->keypad ? '+' : '-', (wp->logfp != NULL) ? '+' : '-', (wp->monitor != MON_OFF) ? '+' : '-', wp->norefresh ? '-' : '+'); if (ISO2022) { p = buf + strlen(buf); sprintf(p, " G%1d [", wp->LocalCharset); for (i = 0; i < 4; i++) p[i + 5] = wp->charsets[i] ? wp->charsets[i] : 'B'; p[9] = ']'; p[10] = '\0'; } Msg(0, "%s", buf); } #if defined(sequent) || defined(_SEQUENT_) || defined(SVR4) static int OpenPTY() { char *m, *s; register int f; # ifdef SVR4 char *ptsname(); sig_t (*sigcld)(); if ((f = open("/dev/ptmx", O_RDWR)) == -1) return(-1); /* * SIGCLD set to SIG_DFL for grantpt() because it fork()s and * exec()s pt_chmod */ sigcld = signal(SIGCLD, SIG_DFL); if ((m = ptsname(f)) == NULL || unlockpt(f) || grantpt(f)) { signal(SIGCLD, sigcld); close(f); return(-1); } signal(SIGCLD, sigcld); strncpy(TtyName, m, sizeof TtyName); # else /* SVR4 */ if ((f = getpseudotty(&s, &m)) < 0) return(-1); strncpy(PtyName, m, sizeof PtyName); strncpy(TtyName, s, sizeof TtyName); # endif /* SVR4 */ # ifdef POSIX tcflush(f, TCIOFLUSH); # else (void) ioctl(f, TIOCFLUSH, (char *) 0); # endif # ifdef LOCKPTY (void) ioctl(f, TIOCEXCL, (char *) 0); # endif return (f); } #else /* defined(sequent) || defined(_SEQUENT_) || defined(SVR4) */ # ifdef MIPS static int OpenPTY() { register char *p, *l, *d; register f, tf; register my_minor; struct stat buf; strcpy(PtyName, PtyProto); for (p = PtyName; *p != 'X'; ++p) ; for (l = "zyxwvutsrqp"; *p = *l; ++l) { for (d = "0123456789abcdef"; p[1] = *d; ++d) { if ((f = open(PtyName, O_RDWR)) != -1) { fstat(f, &buf); my_minor = minor(buf.st_rdev); sprintf(TtyName, "/dev/ttyq%d", my_minor); if ((tf = open(TtyName, O_RDWR)) != -1) { close(tf); #ifdef LOCKPTY (void) ioctl(f, TIOCEXCL, (char *)0); #endif return f; } close(f); } } } return -1; } # else /* MIPS */ # ifdef sgi static int OpenPTY() { register f; register my_minor; struct stat buf; strcpy(PtyName, "/dev/ptc"); f = open(PtyName, O_RDWR|O_NDELAY); if (f >= 0) { if (fstat(f, &buf) < 0) { close(f); return -1; } my_minor = minor(buf.st_rdev); sprintf(TtyName, "/dev/ttyq%d", my_minor); } return f; } # else /* sgi */ # ifdef _AIX /* RS6000 */ static int OpenPTY() { register int i, f, tf; for (i = 0; i < 256; i++) { sprintf(PtyName, "/dev/ptc/%d", i); if ((f = open(PtyName, O_RDWR)) != -1) { sprintf(TtyName, "/dev/pts/%d", i); if ((tf = open(TtyName, O_RDWR)) != -1) { close(tf); #ifdef LOCKPTY (void) ioctl(f, TIOCEXCL, (char *) 0); #endif return f; } close(f); } } return -1; } # else /* _AIX, RS6000 */ static int OpenPTY() { register char *p, *q, *l, *d; register int f, tf; # if !defined(hpux) debug("Hello, You are none of: sequent, _SEQUENT_, SVR4, MIPS, sgi, AIX\n"); debug(" This OpenPTY() is for hpux, ... and for you?\n"); # endif strcpy(PtyName, PtyProto); strcpy(TtyName, TtyProto); for (p = PtyName; *p != 'X'; ++p) ; for (q = TtyName; *q != 'X'; ++q) ; #ifdef sequent /* why ask for sequent in #else (not sequent) section? jw. */ for (l = "p"; (*p = *l) != '\0'; ++l) { /* } */ for (d = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; (p[1] = *d) != '\0'; ++d) { /* } */ #else # ifdef hpux for (l = "pqrstuvw"; (*p = *l) != '\0'; ++l) # else for (l = "qpr"; (*p = *l) != '\0'; ++l) #endif { for (d = "0123456789abcdef"; (p[1] = *d) != '\0'; ++d) { #endif if ((f = open(PtyName, O_RDWR)) != -1) { q[0] = *l; q[1] = *d; if ((tf = open(TtyName, O_RDWR)) != -1) { /* close tf, thus we also get rid of an unwanted * controlling terminal! */ close(tf); #ifdef LOCKPTY (void) ioctl(f, TIOCEXCL, (char *) 0); #endif return f; } close(f); } } } return -1; } # endif /* _AIX, RS6000 */ # endif /* sgi */ # endif /* MIPS */ #endif void SetTTY(fd, mp) int fd; struct mode *mp; { errno = 0; #ifdef POSIX tcsetattr(fd, TCSADRAIN, &mp->tio); # ifdef hpux ioctl(fd, TIOCSLTC, &mp->m_ltchars); # endif #else # ifdef TERMIO ioctl(fd, TCSETA, &mp->tio); # else /* 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); ioctl(fd, TIOCSETP, &mp->m_ttyb); # endif #endif if (errno) Msg(0, "SetTTY: ioctl failed"); } void GetTTY(fd, mp) int fd; struct mode *mp; { errno = 0; #ifdef POSIX tcgetattr(fd, &mp->tio); # ifdef hpux ioctl(fd, TIOCGLTC, &mp->m_ltchars); # endif #else # ifdef TERMIO ioctl(fd, TCGETA, &mp->tio); # else 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); # endif #endif if (errno) Msg(0, "GetTTY: ioctl failed"); } void SetMode(op, np) struct mode *op, *np; { *np = *op; #if defined(TERMIO) || defined(POSIX) np->tio.c_iflag &= ~ICRNL; # ifdef ONLCR np->tio.c_oflag &= ~ONLCR; # endif np->tio.c_lflag &= ~(ICANON | ECHO); /* * Unfortunately, the master process never will get SIGINT if the real * terminal is different from the one on which it was originaly started * (process group membership has not been restored or the new tty could not * be made controlling again). In my solution, it is the attacher who * receives SIGINT (because it is always correctly associated with the real * tty) and forwards it to the master [kill(MasterPid, SIGINT)]. * Marc Boucher (marc@CAM.ORG) */ np->tio.c_lflag |= ISIG; /* * careful, careful catche monkey.. * never set VMIN and VTIME to zero, if you want blocking io. */ np->tio.c_cc[VMIN] = 1; np->tio.c_cc[VTIME] = 0; #ifdef VSTART startc = op->tio.c_cc[VSTART]; #endif #ifdef VSTOP stopc = op->tio.c_cc[VSTOP]; #endif if (iflag) intrc = op->tio.c_cc[VINTR]; else intrc = np->tio.c_cc[VINTR] = 0377; np->tio.c_cc[VQUIT] = 0377; if (flow == 0) { np->tio.c_cc[VINTR] = 0377; #ifdef VSTART np->tio.c_cc[VSTART] = 0377; #endif #ifdef VSTOP np->tio.c_cc[VSTOP] = 0377; #endif np->tio.c_iflag &= ~IXON; } #ifdef VDISCARD np->tio.c_cc[VDISCARD] = 0377; #endif #ifdef VSUSP np->tio.c_cc[VSUSP] = 0377; #endif # ifdef hpux np->m_ltchars.t_suspc = 0377; np->m_ltchars.t_dsuspc = 0377; np->m_ltchars.t_flushc = 0377; np->m_ltchars.t_lnextc = 0377; # else # ifdef VDSUSP np->tio.c_cc[VDSUSP] = 0377; # endif # endif #else startc = op->m_tchars.t_startc; stopc = op->m_tchars.t_stopc; if (iflag) intrc = op->m_tchars.t_intrc; else intrc = np->m_tchars.t_intrc = -1; np->m_ttyb.sg_flags &= ~(CRMOD | ECHO); np->m_ttyb.sg_flags |= CBREAK; np->m_tchars.t_quitc = -1; if (flow == 0) { np->m_tchars.t_intrc = -1; 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; #endif /* defined(TERMIO) || defined(POSIX) */ } void SetFlow(on) int on; { if (flow == on) return; #if defined(TERMIO) || defined(POSIX) if (on) { NewMode.tio.c_cc[VINTR] = intrc; #ifdef VSTART NewMode.tio.c_cc[VSTART] = startc; #endif #ifdef VSTOP NewMode.tio.c_cc[VSTOP] = stopc; #endif NewMode.tio.c_iflag |= IXON; } else { NewMode.tio.c_cc[VINTR] = 0377; #ifdef VSTART NewMode.tio.c_cc[VSTART] = 0377; #endif #ifdef VSTOP NewMode.tio.c_cc[VSTOP] = 0377; #endif NewMode.tio.c_iflag &= ~IXON; } # ifdef POSIX if (tcsetattr(0, TCSADRAIN, &NewMode.tio)) # else if (ioctl(0, TCSETA, &NewMode.tio) != 0) # endif debug1("SetFlow: ioctl errno %d\n", errno); #else if (on) { NewMode.m_tchars.t_intrc = intrc; NewMode.m_tchars.t_startc = startc; NewMode.m_tchars.t_stopc = stopc; } else { NewMode.m_tchars.t_intrc = -1; NewMode.m_tchars.t_startc = -1; NewMode.m_tchars.t_stopc = -1; } if (ioctl(0, TIOCSETC, &NewMode.m_tchars) != 0) debug1("SetFlow: ioctl errno %d\n", errno); #endif /* defined(TERMIO) || defined(POSIX) */ flow = on; } /* we return 1 if we could attach one, or 0 if none */ static int Attach(how) int how; { int lasts; struct msg m; struct stat st; char *s; if (how == MSG_WINCH) { bzero((char *) &m, sizeof(m)); m.type = how; if ((lasts = MakeClientSocket(0, SockName)) >= 0) { write(lasts, &m, sizeof(m)); close(lasts); } return 0; } if (how == MSG_CONT) { if ((lasts = MakeClientSocket(0, SockName)) < 0) { printf("Sorry, cannot contact session \"%s\" again\r\n", SockName); sleep(2); how = MSG_ATTACH; } } if (how != MSG_CONT) { switch (FindSocket(how, &lasts)) { case 0: if (rflag == 2) return 0; if (quietflag) eexit(10); if (SockName && *SockName) Msg(0, "There is no screen to be %sed matching %s.", dflag ? "detach" : "resum", SockName); else Msg(0, "There is no screen to be %sed.", dflag ? "detach" : "resum"); /* NOTREACHED */ case 1: break; default: Msg(0, "Type \"screen [-d] -r [pid.]tty.host\" to resume one of them."); /* NOTREACHED */ } /* * Go in UserContext. Advantage is, you can kill your attacher * when things go wrong. Any disadvantages? jw. */ setuid(real_uid); setgid(real_gid); SockName = SockNamePtr; MasterPid = 0; while (*SockName) { if (*SockName > '9' || *SockName < '0') break; MasterPid = 10 * MasterPid + *SockName - '0'; SockName++; } SockName = SockNamePtr; debug1("Attach decided, it is '%s'\n", SockPath); debug1("Attach found MasterPid == %d\n", MasterPid); if (stat(SockPath, &st) == -1) Msg(errno, "stat %s", SockPath); if ((st.st_mode & 0700) != (dflag ? 0700 : 0600)) Msg(0, "That screen is %sdetached.", dflag ? "already " : "not "); #ifdef REMOTE_DETACH if (dflag && (how == MSG_ATTACH || how == MSG_DETACH || how == MSG_POW_DETACH)) { strcpy(m.m.detach.tty, attach_tty); debug1("attach_tty is %s\n", attach_tty); m.m.detach.dpid = getpid(); # ifdef POW_DETACH if (dflag == 2) m.type = MSG_POW_DETACH; else # endif m.type = MSG_DETACH; if (write(lasts, (char *) &m, sizeof(m)) != sizeof(m)) Msg(errno, "write"); close(lasts); if (how != MSG_ATTACH) return 0; /* we detached it. jw. */ sleep(1); /* we dont want to overrun our poor backend. jw. */ if ((lasts = MakeClientSocket(0, SockName)) == -1) Msg(0, "Cannot contact screen again. Shit."); } #endif } m.type = how; strcpy(m.m.attach.tty, attach_tty); debug1("attach_tty is %s\n", attach_tty); s = getenv("TERM"); if (s) { if (strlen(s) >= MAXPATH - 5) Msg(0, "$TERM too long - sorry."); sprintf(m.m.attach.envterm, "TERM=%s", s); } else *m.m.attach.envterm = '\0'; debug1("attach: sending %d bytes... ", sizeof m); m.m.attach.apid = getpid(); m.m.attach.adaptflag = adaptflag; m.m.attach.lines = m.m.attach.columns = 0; if (s = getenv("LINES")) m.m.attach.lines = atoi(s); if (s = getenv("COLUMNS")) m.m.attach.columns = atoi(s); #ifdef PASSWORD if (how == MSG_ATTACH || how == MSG_CONT) trysend(lasts, &m, m.m.attach.password); else #endif { if (write(lasts, (char *) &m, sizeof(m)) != sizeof(m)) Msg(errno, "write"); close(lasts); } debug1("Attach(%d): sent\n", m.type); Suspended = 0; rflag = 0; return 1; } #ifdef PASSWORD static trysendstat; static sig_t trysendok(SIGDEFARG) { trysendstat = 1; } static sig_t trysendfail(SIGDEFARG) { trysendstat = -1; # ifdef SYSV signal(SIG_PW_FAIL, trysendfail); # endif /* SYSV */ } static char screenpw[9]; static void trysend(fd, m, pwto) int fd; struct msg *m; char *pwto; { char *npw = NULL; sig_t (*sighup)(); sig_t (*sigusr1)(); int tries; sigusr1 = signal(SIG_PW_OK, trysendok); sighup = signal(SIG_PW_FAIL, trysendfail); for (tries = 0; ; ) { strcpy(pwto, screenpw); trysendstat = 0; if (write(fd, (char *) m, sizeof(*m)) != sizeof(*m)) Msg(errno, "write"); close(fd); while (trysendstat == 0) pause(); if (trysendstat > 0) { signal(SIG_PW_OK, sigusr1); signal(SIG_PW_FAIL, sighup); return; } if (++tries > 1 || (npw = getpass("Screen Password:")) == 0 || *npw == 0) Msg(0, "Password incorrect"); strncpy(screenpw, npw, 8); if ((fd = MakeClientSocket(0, SockName)) == -1) Msg(0, "Cannot contact screen again. Shit."); } } #endif /* PASSWORD */ /* * Unfortunatelly this is also the SIGHUP handler, so we have to * check, if the backend is already detached. */ static sig_t AttacherFinit(SIGDEFARG) { struct stat statb; struct msg m; int s; debug("AttacherFinit();\n"); signal(SIGHUP, SIG_IGN); /* Check if signal comes from backend */ if (SockName) { strcpy(SockNamePtr, SockName); if (stat(SockPath, &statb) == 0 && (statb.st_mode & 0777) != 0600) { debug("Detaching backend!\n"); bzero((char *) &m, sizeof(m)); strcpy(m.m.detach.tty, attach_tty); debug1("attach_tty is %s\n", attach_tty); m.m.detach.dpid = getpid(); m.type = MSG_HANGUP; if ((s = MakeClientSocket(0, SockName)) >= 0) { write(s, &m, sizeof(m)); close(s); } } } exit(0); #ifndef SIGVOID return((sig_t) 0); #endif } #ifdef POW_DETACH static sig_t AttacherFinitBye(SIGDEFARG) { int ppid; debug("AttacherFintBye()\n"); freetty(); setuid(real_uid); setgid(real_gid); /* we don't want to disturb init (even if we were root), eh? jw */ if ((ppid = getppid()) > 1) Kill(ppid, SIGHUP); /* carefully say good bye. jw. */ exit(0); #ifndef SIGVOID return((sig_t) 0); #endif } #endif static SuspendPlease; static sig_t SigStop(SIGDEFARG) { debug("SigStop()\n"); SuspendPlease = 1; #ifndef SIGVOID return((sig_t) 0); #endif } #ifdef LOCK static LockPlease; static sig_t DoLock(SIGDEFARG) { debug("DoLock()\n"); LockPlease = 1; # ifdef SYSV signal(SIG_LOCK, DoLock); # endif # ifndef SIGVOID return((sig_t) 0); # endif } #endif #if defined(SIGWINCH) && defined(TIOCGWINSZ) static SigWinchPlease; static sig_t SigAttWinch(SIGDEFARG) { debug("SigAttWinch()\n"); SigWinchPlease = 1; # ifndef SIGVOID return((sig_t) 0); # endif } #endif static void Attacher() { /* * permanent in UserContext. Advantage is, you can kill your attacher * when things go wrong. Any disadvantages? jw. */ setuid(real_uid); /* XXX: already done in Attach() */ setgid(real_gid); /* XXX: already done in Attach() */ signal(SIGHUP, AttacherFinit); signal(SIG_BYE, AttacherFinit); #ifdef POW_DETACH signal(SIG_POWER_BYE, AttacherFinitBye); #endif #ifdef LOCK signal(SIG_LOCK, DoLock); #endif signal(SIGINT, AttacherSigInt); #ifdef BSDJOBS signal(SIG_STOP, SigStop); #endif #if defined(SIGWINCH) && defined(TIOCGWINSZ) signal(SIGWINCH, SigAttWinch); #endif #ifdef DEBUG # ifdef SYSV signal(SIGCLD, FEChld); # else signal(SIGCHLD, FEChld); # endif #endif debug("attacher: going for a nap.\n"); dflag = 0; while (1) { pause(); debug("attacher: huh! a signal!\n"); #ifdef DEBUG if (FEpanic) { printf("\n\rSuddenly the Dungeon collapses!! - You die...\n\r"); SetTTY(0, &OldMode); eexit(1); } #endif #ifdef BSDJOBS if (SuspendPlease) { SuspendPlease = 0; signal(SIGTSTP, SIG_DFL); debug("attacher: killing myself SIGTSTP\n"); kill(getpid(), SIGTSTP); debug1("attacher: continuing from stop(%d)\n", Suspended); signal(SIG_STOP, SigStop); (void) Attach(MSG_CONT); } #endif #ifdef LOCK if (LockPlease) { LockPlease = 0; LockTerminal(); # ifdef SYSV signal(SIG_LOCK, DoLock); # endif (void) Attach(MSG_CONT); } #endif /* LOCK */ #if defined(SIGWINCH) && defined(TIOCGWINSZ) if (SigWinchPlease) { SigWinchPlease = 0; # ifdef SYSV signal(SIGWINCH, SigAttWinch); # endif (void) Attach(MSG_WINCH); } #endif /* SIGWINCH */ } } #ifdef LOCK /* ADDED by Rainer Pruy 10/15/87 */ /* POLISHED by mls. 03/10/91 */ static char LockEnd[] = "Welcome back to screen !!\n"; static void LockTerminal() { char *prg; int sig, pid; sig_t (*sigs[NSIG])__P(SIGPROTOARG); for (sig = 1; sig < NSIG; sig++) { sigs[sig] = signal(sig, SIG_IGN); } SetTTY(0, &OldMode); printf("\n"); prg = getenv("LOCKPRG"); if (prg && strcmp(prg, "builtin") && !access(prg, X_OK)) { # ifdef SYSV signal(SIGCLD, SIG_DFL); # else /* SYSV */ signal(SIGCHLD, SIG_DFL); # endif /* SYSV */ debug1("lockterminal: '%s' seems executable, execl it!\n", prg); if ((pid = fork()) == 0) { /* Child */ setuid(real_uid); /* this should be done already */ setgid(real_gid); closeallfiles(); /* important: /etc/shadow may be open */ execl(prg, "SCREEN-LOCK", NULL); exit(errno); } if (pid == -1) { #ifdef NETHACK if (nethackflag) Msg(errno, "Cannot fork terminal - lock failed"); else #endif Msg(errno, "Cannot lock terminal - fork failed"); } else { #ifdef BSDWAIT union wait wstat; #else int wstat; #endif int wret; #ifdef hpux signal(SIGCLD, SIG_DFL); #endif errno = 0; while (((wret = wait((int *) &wstat)) != pid) || ((wret == -1) && (errno == EINTR)) ) errno = 0; if (errno) { perror("Lock"); sleep(2); } else if (WTERMSIG(wstat) != 0) { fprintf(stderr, "Lock: %s: Killed by signal: %d%s\n", prg, WTERMSIG(wstat), WIFCORESIG(wstat) ? " (Core dumped)" : ""); sleep(2); } else if (WEXITSTATUS(wstat)) { debug2("Lock: %s: return code %d\n", prg, WEXITSTATUS(wstat)); } else printf(LockEnd); } } else { if (prg) { debug1("lockterminal: '%s' seems NOT executable, we use our builtin\n", prg); } else { debug("lockterminal: using buitin.\n"); } screen_builtin_lck(); } /* reset signals */ for (sig = 1; sig < NSIG; sig++) { if (sigs[sig] != (sig_t(*) ()) - 1) signal(sig, sigs[sig]); } } /* LockTerminal */ /* -- original copyright by Luigi Cannelloni 1985 (luigi@faui70.UUCP) -- */ void screen_builtin_lck() { char fullname[100], *cp1, message[BUFSIZ]; char c, *pass, mypass[9]; #ifdef SHADOWPW struct spwd *sss = NULL; #endif int t; #ifdef undef /* get password entry */ if ((ppp = getpwuid(real_uid)) == NULL) { fprintf(stderr, "screen_builtin_lck: No passwd entry.\007\n"); sleep(2); return; } if (!isatty(0)) { fprintf(stderr, "screen_builtin_lck: Not a tty.\007\n"); sleep(2); return; } #endif pass = ppp->pw_passwd; realpw: for (t = 0; t < 13; t++) { c = pass[t]; if (!(c == '.' || c == '/' || (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))) break; } if (t < 13) { debug("builtin_lock: ppp->pw_passwd bad, has it a shadow?\n"); #ifdef SHADOWPW setspent(); /* rewind shadow file */ if ((sss == NULL) && (sss = getspnam(ppp->pw_name))) { pass = sss->sp_pwdp; goto realpw; } #endif /* SHADOWPW */ if (pass = getpass("Key: ")) { strncpy(mypass, pass, 8); mypass[8] = 0; if (*mypass == 0) return; if (pass = getpass("Again: ")) { if (strcmp(mypass, pass)) { fprintf(stderr, "Passwords don't match.\007\n"); sleep(2); return; } } } if (pass == 0) { fprintf(stderr, "Getpass error.\007\n"); sleep(2); return; } pass = 0; } debug("screen_builtin_lck looking in gcos field\n"); strcpy(fullname, ppp->pw_gecos); if ((cp1 = index(fullname, ',')) != NULL) *cp1 = '\0'; if ((cp1 = index(fullname, '&')) != NULL) { sprintf(cp1, "%s", ppp->pw_name); *cp1 = islower(*cp1) ? toupper(*cp1) : *cp1; } sprintf(message, "Screen used by %s <%s>.\nPassword:\007", fullname, ppp->pw_name); /* loop here to wait for correct password */ for (;;) { debug("screen_builtin_lck awaiting password\n"); if ((cp1 = getpass(message)) == NULL) { AttacherFinit(SIGARG); /* NOTREACHED */ } if (pass) { if (!strcmp(crypt(cp1, pass), pass)) break; } else { if (!strcmp(cp1, mypass)) break; } debug("screen_builtin_lck: NO!!!!!\n"); } debug("password ok.\n"); } #endif /* LOCK */ /* * Detach now has the following modes: * D_DETACH SIG_BYE detach backend and exit attacher * D_STOP SIG_STOP stop attacher (and detach backend) * D_REMOTE SIG_BYE remote detach -- reattach to new attacher * D_POWER SIG_POWER_BYE power detach -- attacher kills his parent * D_REMOTE_POWER SIG_POWER_BYE remote power detach -- both * D_LOCK SIG_LOCK lock the attacher * (jw) * we always remove our utmp slots. (even when "lock" or "stop") * Note: Take extra care here, we may be called by unterrupt! */ void Detach(mode) int mode; { int sign = 0; #ifdef UTMPOK register int n; #endif if (Detached) return; debug1("Detach(%d)\n", mode); if (fore && status) RemoveStatus(); signal(SIGHUP, SIG_IGN); SetTTY(0, &OldMode); FinitTerm(); switch (mode) { case D_DETACH: printf("\n[detached]\n"); sign = SIG_BYE; break; #ifdef BSDJOBS case D_STOP: (void) fflush(stdout); sign = SIG_STOP; break; #endif #ifdef REMOTE_DETACH case D_REMOTE: printf("\n[remote detached]\n"); sign = SIG_BYE; break; #endif #ifdef POW_DETACH case D_POWER: printf("\n[power detached]\n"); if (PowDetachString) printf("%s\n", PowDetachString); sign = SIG_POWER_BYE; break; #ifdef REMOTE_DETACH case D_REMOTE_POWER: printf("\n[remote power detached]\n"); if (PowDetachString) printf("%s\n", PowDetachString); sign = SIG_POWER_BYE; break; #endif #endif case D_LOCK: ClearDisplay(); sign = SIG_LOCK; /* tell attacher to lock terminal with a lockprg. */ break; } #ifdef UTMPOK for (n = WinList; n != -1; n = wtab[n]->WinLink) if (wtab[n]->slot != (slot_t) -1) { RemoveUtmp(wtab[n]); /* * Set the slot to 0 to get the window * logged in again. */ wtab[n]->slot = (slot_t) 0; } RestoreLoginSlot(); #endif freetty(); (void) chmod(SockPath, /* S_IFSOCK | */ 0600); /* Flag detached-ness */ /* * tell father to father what to do. We do that after we * freed the tty, thus getty feels more comfortable on hpux * if it was a power detach. */ Kill(AttacherPid, sign); debug2("Detach: Signal %d to Attacher(%d)!\n", sign, AttacherPid); if (mode != D_LOCK && mode != D_STOP) AttacherPid = 0; Detached = 1; Suspended = (mode == D_STOP) ? 1 : 0; if (fore) fore->active = 0; debug("Detach returns, we are successfully detached.\n"); } void Kill(pid, sig) int pid, sig; { if (pid < 2) return; (void) kill(pid, sig); } static int IsSymbol(e, s) register char *e, *s; { register char *p; register int n; for (p = e; *p && *p != '='; ++p) ; if (*p) { *p = '\0'; n = strcmp(e, s); *p = '='; return n == 0; } return 0; } static void MakeNewEnv() { register char **op, **np; static char buf[MAXSTR]; for (op = environ; *op; ++op) ; NewEnv = np = (char **) malloc((unsigned) (op - environ + 6 + 1) * sizeof(char **)); if (!NewEnv) Msg_nomem; if (strlen(SockName) > MAXSTR - 5) SockName = "?"; sprintf(buf, "STY=%s", SockName); *np++ = buf; /* NewEnv[0] */ *np++ = Term; /* NewEnv[1] */ #ifdef TIOCSWINSZ np += 2; /* room for TERMCAP and WINDOW */ #else np += 4; /* room for TERMCAP WINDOW LINES COLUMNS */ #endif for (op = environ; *op; ++op) { if (!IsSymbol(*op, "TERM") && !IsSymbol(*op, "TERMCAP") && !IsSymbol(*op, "STY") && !IsSymbol(*op, "WINDOW") && !IsSymbol(*op, "SCREENCAP") #ifndef TIOCGWINSZ && !IsSymbol(*op, "LINES") && !IsSymbol(*op, "COLUMNS") #endif ) *np++ = *op; } *np = 0; } void #ifdef USEVARARGS /*VARARGS2*/ # if defined(__STDC__) Msg(int err, char *fmt, ...) # else Msg(err, fmt, va_alist) int err; char *fmt; va_dcl # endif { static va_list ap = 0; #else /*VARARGS2*/ Msg(err, fmt, p1, p2, p3, p4, p5, p6) int err; char *fmt; unsigned long p1, p2, p3, p4, p5, p6; { #endif char buf[MAXPATH*2]; char *p = buf; if (Detached) return; #ifdef USEVARARGS # if defined(__STDC__) va_start(ap, fmt); # else va_start(ap); # endif (void) vsprintf(p, fmt, ap); va_end(ap); #else sprintf(p, fmt, p1, p2, p3, p4, p5, p6); #endif if (err) { p += strlen(p); if (err > 0 && err < sys_nerr) sprintf(p, ": %s", sys_errlist[err]); else sprintf(p, ": Error %d", err); } if (HasWindow) { debug1("Msg('%s');\n", p); MakeStatus(buf); } else { printf("%s\r\n", buf); if (DeadlyMsg) { debug1("Msg('%s') screen is not up, exiting..\n", buf); Kill(AttacherPid, SIG_BYE); eexit(1); } else debug1("Harmless; Msg('%s');\n", buf); } DeadlyMsg = 1; } char *Filename(s) char *s; { register char *p; if (s == NULL) return s; p = s + strlen(s) - 1; while (p >= s && *p != '/') --p; return ++p; } /* * '^' is allowed as an escape mechanism for control characters. jw. */ static char *MakeWinMsg(s, n) register char *s; int n; { static char buf[MAXSTR]; register char *p = buf; register int ctrl; ctrl = 0; for (; *s && p < buf + MAXSTR - 1; s++, p++) if (ctrl) { ctrl = 0; if (*s == '^' || *s < 64) *p = *s; else *p = *s - 64; } else { switch (*s) { case '%': *p = n + '0'; break; case '~': *p = BELL; break; case '^': ctrl = 1; *p-- = '^'; break; default: *p = *s; break; } } *p = '\0'; return buf; }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.