/* Copyright (c) 2010 * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) * Sadrul Habib Chowdhury (sadrul@users.sourceforge.net) * Copyright (c) 2008, 2009 * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) * Micah Cowan (micah@cowan.name) * Sadrul Habib Chowdhury (sadrul@users.sourceforge.net) * Copyright (c) 1993-2002, 2003, 2005, 2006, 2007 * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) * Copyright (c) 1987 Oliver Laumann #ifdef HAVE_BRAILLE * Modified by: * Authors: Hadi Bargi Rangin bargi@dots.physics.orst.edu * Bill Barry barryb@dots.physics.orst.edu * Randy Lundquist randyl@dots.physics.orst.edu * * Modifications Copyright (c) 1995 by * Science Access Project, Oregon State University. #endif * * 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 3, 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, see * https://www.gnu.org/licenses/, or contact Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA * **************************************************************** */ #include #ifdef _AIX #include #endif #include #include #include #include #include #if defined(__sun) # include #endif #ifdef sgi # include #endif #include #ifndef sun # include #endif #ifndef SIGINT # include #endif #include "config.h" #ifdef HAVE_STROPTS_H # include #endif #if defined(SYSV) && !defined(ISC) # include #endif #if defined(sequent) || defined(SVR4) # include #endif /* sequent || SVR4 */ #ifdef ISC # include # include # include #endif /* ISC */ #if (defined(AUX) || defined(_AUX_SOURCE)) && defined(POSIX) # include #endif #if defined(USE_LOCALE) || defined(ENCODINGS) # include #endif #if defined(HAVE_NL_LANGINFO) && defined(ENCODINGS) # include #endif #include "screen.h" #ifdef HAVE_BRAILLE # include "braille.h" #endif #include "patchlevel.h" /* * At the moment we only need the real password if the * builtin lock is used. Therefore disable SHADOWPW if * we do not really need it (kind of security thing). */ #ifndef LOCK # undef SHADOWPW #endif #include #ifdef SHADOWPW # include #endif /* SHADOWPW */ #include "logfile.h" /* islogfile, logfflush, logfopen/logfclose */ #ifdef DEBUG FILE *dfp; #endif extern char Term[], screenterm[], **environ, Termcap[]; int force_vt = 1; int VBellWait, MsgWait, MsgMinWait, SilenceWait; extern struct acluser *users; extern struct display *displays, *display; extern struct LayFuncs MarkLf; extern int visual_bell; #ifdef COPY_PASTE extern unsigned char mark_key_tab[]; #endif extern char version[]; extern char DefaultShell[]; #ifdef ZMODEM extern char *zmodem_sendcmd; extern char *zmodem_recvcmd; #endif extern struct layout *layout_last; char *ShellProg; char *ShellArgs[2]; extern struct NewWindow nwin_undef, nwin_default, nwin_options; struct backtick; static struct passwd *getpwbyname __P((char *, struct passwd *)); static void SigChldHandler __P((void)); static sigret_t SigChld __P(SIGPROTOARG); static sigret_t SigInt __P(SIGPROTOARG); static sigret_t CoreDump __P(SIGPROTOARG); static sigret_t FinitHandler __P(SIGPROTOARG); static void DoWait __P((void)); static void serv_read_fn __P((struct event *, char *)); static void serv_select_fn __P((struct event *, char *)); static void logflush_fn __P((struct event *, char *)); static void backtick_filter __P((struct backtick *)); static void backtick_fn __P((struct event *, char *)); static char *runbacktick __P((struct backtick *, int *, time_t)); static int IsSymbol __P((char *, char *)); static char *ParseChar __P((char *, char *)); static int ParseEscape __P((char *)); static char *pad_expand __P((char *, char *, int, int)); static void SetTtyname __P((bool, struct stat *)); #ifdef DEBUG static void fds __P((void)); #endif int nversion; /* numerical version, used for secondary DA */ /* the attacher */ struct passwd *ppp; char *attach_tty; /* Indicator whether the current tty exists in another namespace. */ bool attach_tty_is_in_new_ns = false; /* Content of the tty symlink when attach_tty_is_in_new_ns == true. */ char attach_tty_name_in_ns[MAXPATHLEN]; int attach_fd = -1; char *attach_term; char *LoginName; struct mode attach_Mode; char SockPath[MAXPATHLEN + 2 * MAXSTR]; char *SockName; /* SockName is pointer in SockPath */ char *SockMatch = NULL; /* session id command line argument */ int ServerSocket = -1; struct event serv_read; struct event serv_select; struct event logflushev; char **NewEnv = NULL; char *RcFileName = NULL; char *home; char *screenlogfile; /* filename layout */ int log_flush = 10; /* flush interval in seconds */ int logtstamp_on = 0; /* tstamp disabled */ char *logtstamp_string; /* stamp layout */ int logtstamp_after = 120; /* first tstamp after 120s */ char *hardcopydir = NULL; char *BellString; char *VisualBellString; char *ActivityString; #ifdef COPY_PASTE char *BufferFile; #endif #ifdef POW_DETACH char *PowDetachString; #endif char *hstatusstring; char *captionstring; char *timestring; char *wliststr; char *wlisttit; int auto_detach = 1; int iflag, rflag, dflag, lsflag, quietflag, wipeflag, xflag; int cmdflag; int queryflag = -1; int adaptflag; #ifdef MULTIUSER char *multi; char *multi_home; int multi_uid; int own_uid; int multiattach; int tty_mode; int tty_oldmode = -1; #endif char HostName[MAXSTR]; int MasterPid, PanicPid; int real_uid, real_gid, eff_uid, eff_gid; int default_startup; int ZombieKey_destroy, ZombieKey_resurrect, ZombieKey_onerror; char *preselect = NULL; /* only used in Attach() */ #ifdef UTF8 char *screenencodings; #endif #ifdef DW_CHARS int cjkwidth; #endif #ifdef NETHACK int nethackflag = 0; #endif int maxwin; struct layer *flayer; struct win *fore; struct win *windows; struct win *console_window; #ifdef BUILTIN_TELNET int af; #endif /* * Do this last */ #include "extern.h" char strnomem[] = "Out of memory."; static int InterruptPlease; static int GotSigChld; static int lf_secreopen(name, wantfd, l) char *name; int wantfd; struct logfile *l; { int got_fd; close(wantfd); if (((got_fd = secopen(name, O_WRONLY | O_CREAT | O_APPEND, 0666)) < 0) || lf_move_fd(got_fd, wantfd) < 0) { logfclose(l); debug1("lf_secreopen: failed for %s\n", name); return -1; } l->st->st_ino = l->st->st_dev = 0; debug2("lf_secreopen: %d = %s\n", wantfd, name); return 0; } /********************************************************************/ /********************************************************************/ /********************************************************************/ static struct passwd * getpwbyname(name, ppp) char *name; struct passwd *ppp; { int n; #ifdef SHADOWPW struct spwd *sss = NULL; static char *spw = NULL; #endif if (!ppp && !(ppp = getpwnam(name))) return NULL; /* Do password sanity check..., allow ##user for SUN_C2 security */ #ifdef SHADOWPW pw_try_again: #endif n = 0; if (ppp->pw_passwd[0] == '#' && ppp->pw_passwd[1] == '#' && strcmp(ppp->pw_passwd + 2, ppp->pw_name) == 0) n = 13; for (; n < 13; n++) { char c = ppp->pw_passwd[n]; if (!(c == '.' || c == '/' || c == '$' || (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))) break; } #ifdef SHADOWPW /* try to determine real password */ if (n < 13 && sss == 0) { sss = getspnam(ppp->pw_name); if (sss) { if (spw) free(spw); ppp->pw_passwd = spw = SaveStr(sss->sp_pwdp); endspent(); /* this should delete all buffers ... */ goto pw_try_again; } endspent(); /* this should delete all buffers ... */ } #endif if (n < 13) ppp->pw_passwd = 0; #ifdef linux if (ppp->pw_passwd && strlen(ppp->pw_passwd) == 13 + 11) ppp->pw_passwd[13] = 0; /* beware of linux's long passwords */ #endif return ppp; } static char *locale_name(void) { static char *s; if (!s) { s = getenv("LC_ALL"); if (s == NULL) s = getenv("LC_CTYPE"); if (s == NULL) s = getenv("LANG"); } return s; } int main(int ac, char** av) { register int n; char *ap; char *av0; char socknamebuf[2 * MAXSTR]; int mflag = 0; char *myname = (ac == 0) ? "screen" : av[0]; char *SockDir; struct stat st; #ifdef _MODE_T /* (jw) */ mode_t oumask; #else int oumask; #endif #if defined(SYSV) && !defined(ISC) struct utsname utsnam; #endif struct NewWindow nwin; int detached = 0; /* start up detached */ #ifdef MULTIUSER char *sockp; #endif char *sty = 0; #if (defined(AUX) || defined(_AUX_SOURCE)) && defined(POSIX) setcompat(COMPAT_POSIX|COMPAT_BSDPROT); /* turn on seteuid support */ #endif #if defined(sun) && defined(SVR4) { /* Solaris' login blocks SIGHUP! This is _very bad_ */ sigset_t sset; sigemptyset(&sset); sigprocmask(SIG_SETMASK, &sset, 0); } #endif /* * First, close all unused descriptors * (otherwise, we might have problems with the select() call) */ closeallfiles(0); #ifdef DEBUG opendebug(1, 0); #endif snprintf(version, 59, "%d.%.2d.%.2d%s (%s%s) %s", REV, VERS, PATCHLEVEL, STATE, ORIGIN, GIT_REV, DATE); nversion = REV * 10000 + VERS * 100 + PATCHLEVEL; debug2("-- screen debug started %s (%s)\n", *av, version); #ifdef POSIX debug("POSIX\n"); #endif #ifdef TERMIO debug("TERMIO\n"); #endif #ifdef SYSV debug("SYSV\n"); #endif #ifdef SYSVSIGS debug("SYSVSIGS\n"); #endif #if defined(SIGWINCH) && defined(TIOCGWINSZ) debug("Window size changing enabled\n"); #endif #ifdef HAVE_SETREUID debug("SETREUID\n"); #endif #ifdef HAVE_SETEUID debug("SETEUID\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 SHADOWPW debug("SHADOWPW\n"); #endif #ifdef NAME_MAX debug1("NAME_MAX = %d\n", NAME_MAX); #endif BellString = SaveStr("Bell in window %n"); VisualBellString = SaveStr(" Wuff, Wuff!! "); ActivityString = SaveStr("Activity in window %n"); screenlogfile = SaveStr("screenlog.%n"); logtstamp_string = SaveStr("-- %n:%t -- time-stamp -- %M/%d/%y %c:%s --\n"); hstatusstring = SaveStr("%h"); captionstring = SaveStr("%4n %t"); timestring = SaveStr("%c:%s %M %d %H%? %l%?"); wlisttit = SaveStr(" Num Name%=Flags"); wliststr = SaveStr("%4n %t%=%f"); #ifdef COPY_PASTE BufferFile = SaveStr(DEFAULT_BUFFERFILE); #endif ShellProg = NULL; #ifdef POW_DETACH PowDetachString = 0; #endif default_startup = (ac > 1) ? 0 : 1; adaptflag = 0; VBellWait = VBELLWAIT * 1000; MsgWait = MSGWAIT * 1000; MsgMinWait = MSGMINWAIT * 1000; SilenceWait = SILENCEWAIT; #ifdef HAVE_BRAILLE InitBraille(); #endif #ifdef ZMODEM zmodem_sendcmd = SaveStr("!!! sz -vv -b "); zmodem_recvcmd = SaveStr("!!! rz -vv -b -E"); #endif #ifdef COPY_PASTE CompileKeys((char *)0, 0, mark_key_tab); #endif #ifdef UTF8 InitBuiltinTabs(); screenencodings = SaveStr(SCREENENCODINGS); #endif #ifdef DW_CHARS cjkwidth = 0; #endif nwin = nwin_undef; nwin_options = nwin_undef; strncpy(screenterm, "screen", MAXTERMLEN); screenterm[MAXTERMLEN] = '\0'; #ifdef BUILTIN_TELNET af = AF_UNSPEC; #endif real_uid = getuid(); real_gid = getgid(); eff_uid = geteuid(); eff_gid = getegid(); logreopen_register(lf_secreopen); av0 = *av; /* if this is a login screen, assume -RR */ if (*av0 == '-') { rflag = 4; #ifdef MULTI xflag = 1; #else dflag = 1; #endif ShellProg = SaveStr(DefaultShell); /* to prevent nasty circles */ } while (ac > 0){ ap = *++av; if (--ac > 0 && *ap == '-') { if (ap[1] == '-' && ap[2] == 0) { av++; ac--; break; } if (ap[1] == '-' && !strcmp(ap, "--version")) { printf("Screen version %s\n", version); exit(0); } if (ap[1] == '-' && !strcmp(ap, "--help")) exit_with_usage(myname, NULL, NULL); while (ap && *ap && *++ap) { switch (*ap) { #ifdef BUILTIN_TELNET case '4': af = AF_INET; break; case '6': af = AF_INET6; break; #endif case 'a': nwin_options.aflag = 1; break; case 'A': adaptflag = 1; break; case 'p': /* preselect */ if (*++ap) preselect = ap; else { if (!--ac) exit_with_usage(myname, "Specify a window to preselect with -p", NULL); preselect = *++av; } ap = NULL; break; #ifdef HAVE_BRAILLE case 'B': bd.bd_start_braille = 1; break; #endif case 'c': if (*++ap) RcFileName = ap; else { if (--ac == 0) exit_with_usage(myname, "Specify an alternate rc-filename with -c", NULL); RcFileName = *++av; } ap = NULL; break; case 'e': if (!*++ap) { if (--ac == 0) exit_with_usage(myname, "Specify command characters with -e", NULL); ap = *++av; } if (ParseEscape(ap)) Panic(0, "Two characters are required with -e option, not '%s'.", ap); ap = NULL; break; case 'f': ap++; switch (*ap++) { case 'n': case '0': nwin_options.flowflag = FLOW_NOW * 0; break; case '\0': ap--; /* FALLTHROUGH */ case 'y': case '1': nwin_options.flowflag = FLOW_NOW * 1; break; case 'a': nwin_options.flowflag = FLOW_AUTOFLAG; break; default: exit_with_usage(myname, "Unknown flow option -%s", --ap); } break; case 'h': if (--ac == 0) exit_with_usage(myname, NULL, NULL); nwin_options.histheight = atoi(*++av); if (nwin_options.histheight < 0) exit_with_usage(myname, "-h: %s: negative scrollback size?", *av); break; case 'i': iflag = 1; break; case 't': /* title, the former AkA == -k */ if (--ac == 0) exit_with_usage(myname, "Specify a new window-name with -t", NULL); nwin_options.aka = *++av; break; case 'l': ap++; switch (*ap++) { case 'n': case '0': nwin_options.lflag = 0; break; case '\0': ap--; /* FALLTHROUGH */ case 'y': case '1': nwin_options.lflag = 1; break; case 'a': nwin_options.lflag = 3; break; case 's': /* -ls */ case 'i': /* -list */ lsflag = 1; if (ac > 1 && !SockMatch) { SockMatch = *++av; ac--; } ap = NULL; break; default: exit_with_usage(myname, "%s: Unknown suboption to -l", --ap); } break; case 'w': if (strcmp(ap+1, "ipe")) exit_with_usage(myname, "Unknown option %s", --ap); lsflag = 1; wipeflag = 1; if (ac > 1 && !SockMatch) { SockMatch = *++av; ac--; } break; case 'L': if (!strcmp(ap + 1, "ogfile")) { if (--ac == 0) exit_with_usage(myname, "Specify logfile path with -Logfile", NULL); if (strlen(*++av) > PATH_MAX) Panic(1, "-Logfile name too long. (max. %d char)", PATH_MAX); free(screenlogfile); /* we already set it up while starting */ screenlogfile = SaveStr(*av); ap = NULL; } else if (!strcmp(ap, "L")) nwin_options.Lflag = 1; break; case 'm': mflag = 1; break; case 'O': /* to be (or not to be?) deleted. jw. */ force_vt = 0; break; case 'T': if (--ac == 0) exit_with_usage(myname, "Specify terminal-type with -T", NULL); if (strlen(*++av) < MAXTERMLEN) { strncpy(screenterm, *av, MAXTERMLEN); screenterm[MAXTERMLEN] = '\0'; } else Panic(0, "-T: terminal name too long. (max. %d char)", MAXTERMLEN); nwin_options.term = screenterm; break; case 'q': quietflag = 1; break; case 'Q': queryflag = 1; cmdflag = 1; break; case 'r': case 'R': #ifdef MULTI case 'x': #endif if (ac > 1 && *av[1] != '-' && !SockMatch) { SockMatch = *++av; ac--; debug2("rflag=%d, SockMatch=%s\n", dflag, SockMatch); } #ifdef MULTI if (*ap == 'x') xflag = 1; #endif if (rflag) rflag = 2; rflag += (*ap == 'R') ? 2 : 1; break; #ifdef REMOTE_DETACH case 'd': dflag = 1; /* FALLTHROUGH */ case 'D': if (!dflag) dflag = 2; if (ac == 2) { if (*av[1] != '-' && !SockMatch) { SockMatch = *++av; ac--; debug2("dflag=%d, SockMatch=%s\n", dflag, SockMatch); } } break; #endif case 's': if (--ac == 0) exit_with_usage(myname, "Specify shell with -s", NULL); if (ShellProg) free(ShellProg); ShellProg = SaveStr(*++av); debug1("ShellProg: '%s'\n", ShellProg); break; case 'S': if (!SockMatch) { if (--ac == 0) exit_with_usage(myname, "Specify session-name with -S", NULL); SockMatch = *++av; debug1("SockMatch: '%s'\n", SockMatch); debug1("SockMatch len: '%d'\n", (int)strlen(SockMatch)); if (strlen(SockMatch) > 80) exit_with_usage(myname, "Session-name is too long (max length is 80 symbols)", NULL); } if (!*SockMatch) exit_with_usage(myname, "Empty session-name?", NULL); break; case 'X': cmdflag = 1; break; case 'v': printf("Screen version %s\n", version); exit(0); #ifdef UTF8 case 'U': nwin_options.encoding = nwin_options.encoding == -1 ? UTF8 : 0; break; #endif default: exit_with_usage(myname, "Unknown option %s", --ap); } } } else break; } #ifdef SIGBUS /* OOPS, linux has no bus errors! */ signal(SIGBUS, CoreDump); #endif /* SIGBUS */ signal(SIGSEGV, CoreDump); #ifdef USE_LOCALE setlocale(LC_ALL, ""); #endif #ifdef ENCODINGS if (nwin_options.encoding == -1) { /* ask locale if we should start in UTF-8 mode */ # ifdef HAVE_NL_LANGINFO # ifndef USE_LOCALE setlocale(LC_CTYPE, ""); # endif nwin_options.encoding = FindEncoding(nl_langinfo(CODESET)); debug1("locale says encoding = %d\n", nwin_options.encoding); # else # ifdef UTF8 char *s; if ((s = locale_name()) && InStr(s, "UTF-8")) nwin_options.encoding = UTF8; # endif debug1("environment says encoding=%d\n", nwin_options.encoding); #endif } # ifdef DW_CHARS { char *s; if ((s = locale_name())) { if(!strncmp(s, "zh_", 3) || !strncmp(s, "ja_", 3) || !strncmp(s, "ko_", 3)) { cjkwidth = 1; } } } #endif #endif if (nwin_options.aka) { #ifdef ENCODINGS if (nwin_options.encoding > 0) { size_t len = strlen(nwin_options.aka); size_t newsz; char *newbuf = malloc(3 * len); if (!newbuf) Panic(0, "%s", strnomem); newsz = RecodeBuf((unsigned char *)nwin_options.aka, len, nwin_options.encoding, 0, (unsigned char *)newbuf); newbuf[newsz] = '\0'; nwin_options.aka = newbuf; } else #endif { /* If we just use the original value from av, subsequent shelltitle invocations will attempt to free space we don't own... */ nwin_options.aka = SaveStr(nwin_options.aka); } } if (SockMatch && strlen(SockMatch) >= MAXSTR) Panic(0, "Ridiculously long socketname - try again."); if (cmdflag && !rflag && !dflag && !xflag) xflag = 1; if (!cmdflag && dflag && mflag && !(rflag || xflag)) detached = 1; nwin = nwin_options; #ifdef ENCODINGS nwin.encoding = nwin_undef.encoding; /* let screenrc overwrite it */ #endif if (ac) nwin.args = av; /* make the write() calls return -1 on all errors */ #ifdef SIGXFSZ /* * Ronald F. Guilmette, Oct 29 '94, bug-gnu-utils@prep.ai.mit.edu: * It appears that in System V Release 4, UNIX, if you are writing * an output file and you exceed the currently set file size limit, * you _don't_ just get the call to `write' returning with a * failure code. Rather, you get a signal called `SIGXFSZ' which, * if neither handled nor ignored, will cause your program to crash * with a core dump. */ signal(SIGXFSZ, SIG_IGN); #endif /* SIGXFSZ */ #ifdef SIGPIPE signal(SIGPIPE, SIG_IGN); #endif if (!ShellProg) { register char *sh; sh = getenv("SHELL"); ShellProg = SaveStr(sh ? sh : DefaultShell); } ShellArgs[0] = ShellProg; home = getenv("HOME"); if (!mflag && !SockMatch) { sty = getenv("STY"); if (sty && *sty == 0) sty = 0; } #ifdef NETHACK if (!(nethackflag = (getenv("NETHACKOPTIONS") != NULL))) { char nethackrc[MAXPATHLEN]; if (home && (strlen(home) < (MAXPATHLEN - 20))) { sprintf(nethackrc,"%s/.nethackrc", home); nethackflag = !access(nethackrc, F_OK); } } #endif #ifdef MULTIUSER own_uid = multi_uid = real_uid; if (SockMatch && (sockp = index(SockMatch, '/'))) { *sockp = 0; multi = SockMatch; SockMatch = sockp + 1; if (*multi) { struct passwd *mppp; if ((mppp = getpwnam(multi)) == (struct passwd *)0) Panic(0, "Cannot identify account '%s'.", multi); multi_uid = mppp->pw_uid; multi_home = SaveStr(mppp->pw_dir); if (strlen(multi_home) > MAXPATHLEN - 10) Panic(0, "home directory path too long"); # ifdef MULTI /* always fake multi attach mode */ if (rflag || lsflag) xflag = 1; # endif /* MULTI */ detached = 0; multiattach = 1; } /* Special case: effective user is multiuser. */ if (eff_uid && (multi_uid != eff_uid)) Panic(0, "Must run suid root for multiuser support."); } if (SockMatch && *SockMatch == 0) SockMatch = 0; #endif /* MULTIUSER */ if ((LoginName = getlogin()) && LoginName[0] != '\0') { if ((ppp = getpwnam(LoginName)) != (struct passwd *) 0) if ((int)ppp->pw_uid != real_uid) ppp = (struct passwd *) 0; } if (ppp == 0) { if ((ppp = getpwuid(real_uid)) == 0) { Panic(0, "getpwuid() can't identify your account!"); exit(1); } LoginName = ppp->pw_name; } LoginName = SaveStr(LoginName); ppp = getpwbyname(LoginName, ppp); #if !defined(SOCKDIR) && defined(MULTIUSER) if (multi && !multiattach) { if (home && strcmp(home, ppp->pw_dir)) Panic(0, "$HOME must match passwd entry for multiuser screens."); } #endif #define SET_GUID() do \ { \ setgid(real_gid); \ setuid(real_uid); \ eff_uid = real_uid; \ eff_gid = real_gid; \ } while (0) if (home == 0 || *home == '\0') home = ppp->pw_dir; if (strlen(LoginName) > MAXLOGINLEN) Panic(0, "LoginName too long - sorry."); #ifdef MULTIUSER if (multi && strlen(multi) > MAXLOGINLEN) Panic(0, "Screen owner name too long - sorry."); #endif if (strlen(home) > MAXPATHLEN - 25) Panic(0, "$HOME too long - sorry."); attach_tty = ""; if (!detached && !lsflag && !cmdflag && !(dflag && !mflag && !rflag && !xflag) && !(sty && !SockMatch && !mflag && !rflag && !xflag)) { int fl; /* ttyname implies isatty */ SetTtyname(true, &st); #ifdef MULTIUSER tty_mode = (int)st.st_mode & 0777; #endif fl = fcntl(0, F_GETFL, 0); if (fl != -1 && (fl & (O_RDWR|O_RDONLY|O_WRONLY)) == O_RDWR) attach_fd = 0; if (attach_fd == -1) { if ((n = secopen(attach_tty, O_RDWR | O_NONBLOCK, 0)) < 0) Panic(0, "Cannot open your terminal '%s' - please check.", attach_tty); /* If the server uses a socket we need an open fd. */ attach_fd = n; } debug2("attach_tty is %s, attach_fd is %d\n", attach_tty, attach_fd); if ((attach_term = getenv("TERM")) == 0 || *attach_term == 0) Panic(0, "Please set a terminal type."); if (strlen(attach_term) > MAXTERMLEN) Panic(0, "$TERM too long - sorry."); GetTTY(0, &attach_Mode); #ifdef DEBUG DebugTTY(&attach_Mode); #endif /* DEBUG */ } #ifdef _MODE_T oumask = umask(0); /* well, unsigned never fails? jw. */ #else if ((oumask = (int)umask(0)) == -1) Panic(errno, "Cannot change umask to zero"); #endif SockDir = getenv("SCREENDIR"); if (SockDir) { if (strlen(SockDir) >= MAXPATHLEN - 1) Panic(0, "Ridiculously long $SCREENDIR - try again."); #ifdef MULTIUSER if (multi) Panic(0, "No $SCREENDIR with multi screens, please."); #endif } #ifdef MULTIUSER if (multiattach) { # ifndef SOCKDIR sprintf(SockPath, "%s/.screen", multi_home); SockDir = SockPath; # else SockDir = SOCKDIR; sprintf(SockPath, "%s/S-%s", SockDir, multi); # endif } else #endif { #ifndef SOCKDIR if (SockDir == 0) { sprintf(SockPath, "%s/.screen", home); SockDir = SockPath; } #endif if (SockDir) { if (access(SockDir, F_OK)) { debug1("SockDir '%s' missing ...\n", SockDir); if (UserContext() > 0) { if (mkdir(SockDir, 0700)) UserReturn(0); UserReturn(1); } if (UserStatus() <= 0) Panic(0, "Cannot make directory '%s'.", SockDir); } if (SockDir != SockPath) strcpy(SockPath, SockDir); } #ifdef SOCKDIR else { SockDir = SOCKDIR; if (stat(SockDir, &st)) { n = (eff_uid == 0 && (real_uid || eff_gid == real_gid)) ? 0755 : (eff_gid != real_gid) ? 0775 : #ifdef S_ISVTX 0777|S_ISVTX; #else 0777; #endif if (mkdir(SockDir, n) == -1) Panic(errno, "Cannot make directory '%s'", SockDir); } else { if (!S_ISDIR(st.st_mode)) Panic(0, "'%s' must be a directory.", SockDir); if (eff_uid == 0 && real_uid && (int)st.st_uid != eff_uid) Panic(0, "Directory '%s' must be owned by root.", SockDir); n = (eff_uid == 0 && (real_uid || (st.st_mode & 0775) != 0775)) ? 0755 : (eff_gid == (int)st.st_gid && eff_gid != real_gid) ? 0775 : 0777; if (((int)st.st_mode & 0777) != n) Panic(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 && errno != EEXIST) Panic(errno, "Cannot make directory '%s'", SockPath); (void) chown(SockPath, real_uid, real_gid); } } #endif } if (stat(SockPath, &st) == -1) Panic(errno, "Cannot access %s", SockPath); else if (!S_ISDIR(st.st_mode)) Panic(0, "%s is not a directory.", SockPath); #ifdef MULTIUSER if (multi) { if ((int)st.st_uid != multi_uid) Panic(0, "%s is not the owner of %s.", multi, SockPath); } else #endif { #ifdef SOCKDIR /* if SOCKDIR is not defined, the socket is in $HOME. in that case it does not make sense to compare uids. */ if ((int)st.st_uid != real_uid) Panic(0, "You are not the owner of %s.", SockPath); #endif } if ((st.st_mode & 0777) != 0700) Panic(0, "Directory %s must have mode 700.", SockPath); if (SockMatch && index(SockMatch, '/')) Panic(0, "Bad session name '%s'", SockMatch); SockName = SockPath + strlen(SockPath) + 1; *SockName = 0; (void) umask(oumask); debug2("SockPath: %s SockMatch: %s\n", SockPath, SockMatch ? SockMatch : "NULL"); #if defined(SYSV) && !defined(ISC) if (uname(&utsnam) == -1) Panic(errno, "uname"); strncpy(HostName, utsnam.nodename, sizeof(utsnam.nodename) < MAXSTR ? sizeof(utsnam.nodename) : MAXSTR - 1); HostName[sizeof(utsnam.nodename) < MAXSTR ? sizeof(utsnam.nodename) : MAXSTR - 1] = '\0'; #else (void) gethostname(HostName, MAXSTR); HostName[MAXSTR - 1] = '\0'; #endif if ((ap = index(HostName, '.')) != NULL) *ap = '\0'; if (lsflag) { int i, fo, oth; bool sock; #ifdef MULTIUSER if (multi) real_uid = multi_uid; #endif SET_GUID(); i = FindSocket((int *)NULL, &fo, &oth, SockMatch, &sock); if (quietflag) { if (rflag) exit(10 + i); else exit(9 + (fo || oth ? 1 : 0) + fo); } if (fo == 0) Panic(0, "No Sockets found in %s.\n", SockPath); Msg(0, "%d Socket%s in %s.", fo, fo > 1 ? "s" : "", SockPath); eexit(0); } signal(SIG_BYE, AttacherFinit); /* prevent races */ if (cmdflag) { #ifdef MULTIUSER if (multi) real_uid = multi_uid; #endif /* attach_tty is not mandatory */ SetTtyname(false, &st); if (!*av) Panic(0, "Please specify a command."); if (!strncmp("sessionname", *av, 11)) { if (!*++av) Panic(0, "Please specify a parameter."); if (strlen(*av) > 80) Panic(0, "Parameter of command 'sessionname' is too long."); *--av; } SET_GUID(); SendCmdMessage(sty, SockMatch, av, queryflag >= 0); exit(0); } else if (rflag || xflag) { debug("screen -r: - is there anybody out there?\n"); if (Attach(MSG_ATTACH)) { Attacher(); /* NOTREACHED */ } #ifdef MULTIUSER if (multiattach) Panic(0, "Can't create sessions of other users."); #endif debug("screen -r: backend not responding -- still crying\n"); } else if (dflag && !mflag) { SetTtyname(false, &st); Attach(MSG_DETACH); Msg(0, "[%s %sdetached.]\n", SockName, (dflag > 1 ? "power " : "")); eexit(0); /* NOTREACHED */ } if (!SockMatch && !mflag && sty) { /* attach_tty is not mandatory */ SetTtyname(false, &st); SET_GUID(); nwin_options.args = av; SendCreateMsg(sty, &nwin); exit(0); /* NOTREACHED */ } nwin_compose(&nwin_default, &nwin_options, &nwin_default); if (!detached || dflag != 2) MasterPid = fork(); else MasterPid = 0; switch (MasterPid) { case -1: Panic(errno, "fork"); /* NOTREACHED */ case 0: break; default: if (detached) exit(0); if (SockMatch) sprintf(socknamebuf, "%d.%s", MasterPid, SockMatch); else sprintf(socknamebuf, "%d.%s.%s", MasterPid, stripdev(attach_tty), HostName); for (ap = socknamebuf; *ap; ap++) if (*ap == '/') *ap = '-'; #ifdef NAME_MAX if (strlen(socknamebuf) > NAME_MAX) socknamebuf[NAME_MAX] = 0; #endif sprintf(SockPath + strlen(SockPath), "/%s", socknamebuf); SET_GUID(); Attacher(); /* NOTREACHED */ } if (!detached) PanicPid = getppid(); if (DefaultEsc == -1) DefaultEsc = Ctrl('a'); if (DefaultMetaEsc == -1) DefaultMetaEsc = 'a'; ap = av0 + strlen(av0) - 1; while (ap >= av0) { if (!strncmp("screen", ap, 6)) { memcpy(ap, "SCREEN", 6); /* name this process "SCREEN-BACKEND" */ break; } ap--; } if (ap < av0) *av0 = 'S'; #ifdef DEBUG { char buf[256]; if (dfp && dfp != stderr) fclose(dfp); sprintf(buf, "%s/SCREEN.%d", DEBUGDIR, (int)getpid()); if ((dfp = fopen(buf, "w")) == NULL) dfp = stderr; else (void) chmod(buf, 0666); } #endif if (!detached) { if (attach_fd == -1) { if ((n = secopen(attach_tty, O_RDWR | O_NONBLOCK, 0)) < 0) Panic(0, "Cannot reopen '%s' - please check.", attach_tty); } else n = dup(attach_fd); } else n = -1; freopen("/dev/null", "r", stdin); freopen("/dev/null", "w", stdout); #ifdef DEBUG if (dfp != stderr) #endif freopen("/dev/null", "w", stderr); debug("-- screen.back debug started\n"); /* This guarantees that the session owner is listed, even when we * start detached. From now on we should not refer to 'LoginName' * any more, use users->u_name instead. */ if (UserAdd(LoginName, (char *)0, (struct acluser **)0) < 0) Panic(0, "Could not create user info"); if (!detached) { if (MakeDisplay(LoginName, attach_tty, attach_term, n, getppid(), &attach_Mode) == 0) Panic(0, "Could not alloc display"); PanicPid = 0; #ifdef ENCODINGS D_encoding = nwin_options.encoding > 0 ? nwin_options.encoding : 0; debug1("D_encoding = %d\n", D_encoding); #endif } if (SockMatch) { /* user started us with -S option */ sprintf(socknamebuf, "%d.%s", (int)getpid(), SockMatch); } else { sprintf(socknamebuf, "%d.%s.%s", (int)getpid(), stripdev(attach_tty), HostName); } for (ap = socknamebuf; *ap; ap++) if (*ap == '/') *ap = '-'; #ifdef NAME_MAX if (strlen(socknamebuf) > NAME_MAX) { debug2("Socketname %s truncated to %d chars\n", socknamebuf, NAME_MAX); socknamebuf[NAME_MAX] = 0; } #endif sprintf(SockPath + strlen(SockPath), "/%s", socknamebuf); /* Always create sockets. We only allow attaching to fifos not creating * new ones. */ ServerSocket = MakeServerSocket(true); #ifdef ETCSCREENRC # ifdef ALLOW_SYSSCREENRC if ((ap = getenv("SYSSCREENRC"))) (void)StartRc(ap, 0); else # endif (void)StartRc(ETCSCREENRC, 0); #endif (void)StartRc(RcFileName, 0); # ifdef UTMPOK # ifndef UTNOKEEP InitUtmp(); # endif /* UTNOKEEP */ # endif /* UTMPOK */ if (display) { if (InitTermcap(0, 0)) { debug("Could not init termcap - exiting\n"); fcntl(D_userfd, F_SETFL, 0); /* Flush sets FNBLOCK */ freetty(); if (D_userpid) Kill(D_userpid, SIG_BYE); eexit(1); } MakeDefaultCanvas(); InitTerm(0); #ifdef UTMPOK RemoveLoginSlot(); #endif } else MakeTermcap(1); #ifdef LOADAV InitLoadav(); #endif /* LOADAV */ InitKeytab(); MakeNewEnv(); signal(SIGHUP, SigHup); signal(SIGINT, FinitHandler); signal(SIGQUIT, FinitHandler); signal(SIGTERM, FinitHandler); #ifdef BSDJOBS signal(SIGTTIN, SIG_IGN); signal(SIGTTOU, SIG_IGN); #endif if (display) { brktty(D_userfd); SetMode(&D_OldMode, &D_NewMode, D_flow, iflag); /* Note: SetMode must be called _before_ FinishRc. */ SetTTY(D_userfd, &D_NewMode); if (fcntl(D_userfd, F_SETFL, FNBLOCK)) Msg(errno, "Warning: NBLOCK fcntl failed"); } else brktty(-1); /* just try */ signal(SIGCHLD, SigChld); #ifdef ETCSCREENRC # ifdef ALLOW_SYSSCREENRC if ((ap = getenv("SYSSCREENRC"))) FinishRc(ap); else # endif FinishRc(ETCSCREENRC); #endif FinishRc(RcFileName); debug2("UID %d EUID %d\n", (int)getuid(), (int)geteuid()); if (windows == NULL) { debug("We open one default window, as screenrc did not specify one.\n"); if (MakeWindow(&nwin) == -1) { fd_set rfd; struct timeval tv = { MsgWait/1000, 1000*(MsgWait%1000) }; FD_SET(0, &rfd); Msg(0, "Sorry, could not find a PTY or TTY."); // allow user to exit early by pressing any key. select(1, &rfd, NULL, NULL, &tv); Finit(0); /* NOTREACHED */ } } else if (ac) /* Screen was invoked with a command */ MakeWindow(&nwin); #ifdef HAVE_BRAILLE StartBraille(); #endif if (display && default_startup) display_copyright(); signal(SIGINT, SigInt); if (rflag && (rflag & 1) == 0 && !quietflag) { Msg(0, "New screen..."); rflag = 0; } serv_read.type = EV_READ; serv_read.fd = ServerSocket; serv_read.handler = serv_read_fn; evenq(&serv_read); serv_select.pri = -10; serv_select.type = EV_ALWAYS; serv_select.handler = serv_select_fn; evenq(&serv_select); logflushev.type = EV_TIMEOUT; logflushev.handler = logflush_fn; sched(); /* NOTREACHED */ return 0; } void WindowDied(p, wstat, wstat_valid) struct win *p; #ifdef BSDWAIT union wait wstat; #else int wstat; #endif int wstat_valid; { int killit = 0; if (p->w_destroyev.data == (char *)p) { wstat = p->w_exitstatus; wstat_valid = 1; evdeq(&p->w_destroyev); p->w_destroyev.data = 0; } #if defined(BSDJOBS) && !defined(BSDWAIT) if (!wstat_valid && p->w_pid > 0) { /* EOF on file descriptor. The process is probably also dead. * try a waitpid */ if (waitpid(p->w_pid, &wstat, WNOHANG | WUNTRACED) == p->w_pid) { p->w_pid = 0; wstat_valid = 1; } } #endif if (ZombieKey_destroy && ZombieKey_onerror && wstat_valid && WIFEXITED(wstat) && WEXITSTATUS(wstat) == 0) killit = 1; if (ZombieKey_destroy && !killit) { char buf[100], *s, reason[100]; time_t now; if (wstat_valid) { if (WIFEXITED(wstat)) if (WEXITSTATUS(wstat)) sprintf(reason, "terminated with exit status %d", WEXITSTATUS(wstat)); else sprintf(reason, "terminated normally"); else if (WIFSIGNALED(wstat)) sprintf(reason, "terminated with signal %d%s", WTERMSIG(wstat), #ifdef WCOREDUMP WCOREDUMP(wstat) ? " (core file generated)" : ""); #else ""); #endif } else sprintf(reason, "detached from window"); (void) time(&now); s = ctime(&now); if (s && *s) s[strlen(s) - 1] = '\0'; debug3("window %d (%s) going into zombie state fd %d", p->w_number, p->w_title, p->w_ptyfd); #ifdef UTMPOK if (p->w_slot != (slot_t)0 && p->w_slot != (slot_t)-1) { RemoveUtmp(p); p->w_slot = 0; /* "detached" */ } #endif CloseDevice(p); p->w_deadpid = p->w_pid; p->w_pid = 0; ResetWindow(p); /* p->w_y = p->w_bot; */ p->w_y = MFindUsedLine(p, p->w_bot, 1); sprintf(buf, "\n\r=== Command %s (%s) ===", reason, s ? s : "?"); WriteString(p, buf, strlen(buf)); if (p->w_poll_zombie_timeout) { debug2("Set zombie poll timeout for window %s to %d\n", p->w_title, p->w_poll_zombie_timeout); SetTimeout(&p->w_zombieev, p->w_poll_zombie_timeout * 1000); evenq(&p->w_zombieev); } WindowChanged(p, 'f'); } else KillWindow(p); #ifdef UTMPOK CarefulUtmp(); #endif } static void SigChldHandler() { struct stat st; #ifdef DEBUG fds(); #endif while (GotSigChld) { GotSigChld = 0; DoWait(); #ifdef SYSVSIGS signal(SIGCHLD, SigChld); #endif } if (stat(SockPath, &st) == -1) { debug1("SigChldHandler: 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("SigChldHandler: stat '%s' o.k. (%03o)\n", SockPath, (int)st.st_mode); } static sigret_t SigChld SIGDEFARG { debug("SigChld()\n"); GotSigChld = 1; SIGRETURN; } sigret_t SigHup SIGDEFARG { /* Hangup all displays */ while ((display = displays) != 0) Hangup(); SIGRETURN; } /* * the backend's Interrupt handler * we cannot insert the intrc directly, as we never know * if fore is valid. */ static sigret_t SigInt SIGDEFARG { #if HAZARDOUS char ibuf; debug("SigInt()\n"); if (fore && displays) { # if defined(TERMIO) || defined(POSIX) ibuf = displays->d_OldMode.tio.c_cc[VINTR]; # else ibuf = displays->d_OldMode.m_tchars.t_intrc; # endif fore->w_inlen = 0; write(fore->w_ptyfd, &ibuf, 1); } #else signal(SIGINT, SigInt); debug("SigInt() careful\n"); InterruptPlease = 1; #endif SIGRETURN; } static sigret_t CoreDump SIGDEFARG { /* if running with s-bit, we must reset the s-bit, so that we get a * core file anyway. */ struct display *disp; char buf[80]; char *dump_msg = " (core dumped)"; int running_w_s_bit = getuid() != geteuid(); #if defined(SHADOWPW) && !defined(DEBUG) && !defined(DUMPSHADOW) if (running_w_s_bit) dump_msg = ""; #endif #if defined(SYSVSIGS) && defined(SIGHASARG) signal(sigsig, SIG_IGN); #endif setgid(getgid()); setuid(getuid()); unlink("core"); #ifdef SIGHASARG sprintf(buf, "\r\n[screen caught signal %d.%s]\r\n", sigsig, dump_msg); #else sprintf(buf, "\r\n[screen caught a fatal signal.%s]\r\n", dump_msg); #endif for (disp = displays; disp; disp = disp->d_next) { if (disp->d_nonblock < -1 || disp->d_nonblock > 1000000) continue; fcntl(disp->d_userfd, F_SETFL, 0); SetTTY(disp->d_userfd, &D_OldMode); write(disp->d_userfd, buf, strlen(buf)); Kill(disp->d_userpid, SIG_BYE); } if (running_w_s_bit) { #if defined(SHADOWPW) && !defined(DEBUG) && !defined(DUMPSHADOW) Kill(getpid(), SIGKILL); eexit(11); #else /* SHADOWPW && !DEBUG */ abort(); #endif /* SHADOWPW && !DEBUG */ } else abort(); SIGRETURN; } static void DoWait() { register int pid; struct win *p, *next; #ifdef BSDWAIT union wait wstat; #else int wstat; #endif #ifdef BSDJOBS # ifndef BSDWAIT while ((pid = waitpid(-1, &wstat, WNOHANG | WUNTRACED)) > 0) # else # ifdef USE_WAIT2 /* * From: rouilj@sni-usa.com (John Rouillard) * note that WUNTRACED is not documented to work, but it is defined in * /usr/include/sys/wait.h, so it may work */ while ((pid = wait2(&wstat, WNOHANG | WUNTRACED )) > 0) # else /* USE_WAIT2 */ while ((pid = wait3(&wstat, WNOHANG | WUNTRACED, (struct rusage *) 0)) > 0) # endif /* USE_WAIT2 */ # endif #else /* BSDJOBS */ while ((pid = wait(&wstat)) < 0) if (errno != EINTR) break; if (pid > 0) #endif /* BSDJOBS */ { for (p = windows; p; p = next) { next = p->w_next; if ((p->w_pid && pid == p->w_pid) || (p->w_deadpid && pid == p->w_deadpid)) { /* child has ceased to exist */ p->w_pid = 0; #ifdef BSDJOBS if (WIFSTOPPED(wstat)) { debug3("Window %d pid %d: WIFSTOPPED (sig %d)\n", p->w_number, pid, WSTOPSIG(wstat)); #ifdef SIGTTIN if (WSTOPSIG(wstat) == SIGTTIN) { Msg(0, "Suspended (tty input)"); continue; } #endif #ifdef SIGTTOU if (WSTOPSIG(wstat) == SIGTTOU) { Msg(0, "Suspended (tty output)"); continue; } #endif /* Try to restart process */ Msg(0, "Child has been stopped, restarting."); if (killpg(pid, SIGCONT)) kill(pid, SIGCONT); } else #endif { /* Screen will detect the window has died when the window's * file descriptor signals EOF (which it will do when the process in * the window terminates). So do this in a timeout of 10 seconds. * (not doing this at all might also work) * See #27061 for more details. */ p->w_destroyev.data = (char *)p; p->w_exitstatus = wstat; SetTimeout(&p->w_destroyev, 10 * 1000); evenq(&p->w_destroyev); } break; } #ifdef PSEUDOS if (p->w_pwin && pid == p->w_pwin->p_pid) { debug2("pseudo of win Nr %d died. pid == %d\n", p->w_number, p->w_pwin->p_pid); FreePseudowin(p); break; } #endif } if (p == 0) { debug1("pid %d not found - hope that's ok\n", pid); } } } static sigret_t FinitHandler SIGDEFARG { #ifdef SIGHASARG debug1("FinitHandler called, sig %d.\n", sigsig); #else debug("FinitHandler called.\n"); #endif Finit(1); SIGRETURN; } void Finit(int i) { signal(SIGCHLD, SIG_DFL); signal(SIGHUP, SIG_IGN); debug1("Finit(%d);\n", i); while (windows) { struct win *p = windows; windows = windows->w_next; FreeWindow(p); } if (ServerSocket != -1) { debug1("we unlink(%s)\n", SockPath); #ifdef USE_SETEUID xseteuid(real_uid); xsetegid(real_gid); #endif (void) unlink(SockPath); #ifdef USE_SETEUID xseteuid(eff_uid); xsetegid(eff_gid); #endif } for (display = displays; display; display = display->d_next) { if (D_status) RemoveStatus(); FinitTerm(); #ifdef UTMPOK RestoreLoginSlot(); #endif AddStr("[screen is terminating]\r\n"); Flush(3); SetTTY(D_userfd, &D_OldMode); fcntl(D_userfd, F_SETFL, 0); freetty(); Kill(D_userpid, SIG_BYE); } /* * we _cannot_ call eexit(i) here, * instead of playing with the Socket above. Sigh. */ exit(i); } void eexit(int e) { debug("eexit\n"); if (ServerSocket != -1) { debug1("we unlink(%s)\n", SockPath); setgid(real_gid); setuid(real_uid); (void) unlink(SockPath); } exit(e); } void Hangup() { if (display == 0) return; debug1("Hangup %x\n", display); if (D_userfd >= 0) { close(D_userfd); D_userfd = -1; } if (auto_detach || displays->d_next) Detach(D_HANGUP); else Finit(0); } /* * Detach now has the following modes: *D_DETACH SIG_BYE detach backend and exit attacher *D_HANGUP 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 interrupt! */ void Detach(int mode) { int sign = 0, pid; struct canvas *cv; struct win *p; if (display == 0) return; #define AddStrSock(msg) do { \ if (SockName) \ { \ AddStr("[" msg " from "); \ AddStr(SockName); \ AddStr("]\r\n"); \ } \ else \ AddStr("[" msg "]\r\n"); \ } while (0) signal(SIGHUP, SIG_IGN); debug1("Detach(%d)\n", mode); if (D_status) RemoveStatus(); FinitTerm(); if (!display) return; switch (mode) { case D_HANGUP: sign = SIG_BYE; break; case D_DETACH: AddStrSock("detached"); sign = SIG_BYE; break; #ifdef BSDJOBS case D_STOP: sign = SIG_STOP; break; #endif #ifdef REMOTE_DETACH case D_REMOTE: AddStrSock("remote detached"); sign = SIG_BYE; break; #endif #ifdef POW_DETACH case D_POWER: AddStrSock("power detached"); if (PowDetachString) { AddStr(PowDetachString); AddStr("\r\n"); } sign = SIG_POWER_BYE; break; #ifdef REMOTE_DETACH case D_REMOTE_POWER: AddStrSock("remote power detached"); if (PowDetachString) { AddStr(PowDetachString); AddStr("\r\n"); } sign = SIG_POWER_BYE; break; #endif #endif case D_LOCK: ClearAll(); sign = SIG_LOCK; /* tell attacher to lock terminal with a lockprg. */ break; } #ifdef UTMPOK if (displays->d_next == 0) { for (p = windows; p; p = p->w_next) { if (p->w_slot != (slot_t) -1 && !(p->w_lflag & 2)) { RemoveUtmp(p); /* Set the slot to 0 to get the window logged in again. */ p->w_slot = (slot_t) 0; } } } if (mode != D_HANGUP) RestoreLoginSlot(); #endif if (displays->d_next == 0 && console_window) { if (TtyGrabConsole(console_window->w_ptyfd, 0, "detach")) { debug("could not release console - killing window\n"); KillWindow(console_window); display = displays; /* restore display */ } } if (D_fore) { #ifdef MULTIUSER ReleaseAutoWritelock(display, D_fore); #endif D_user->u_detachwin = D_fore->w_number; D_user->u_detachotherwin = D_other ? D_other->w_number : -1; } AutosaveLayout(D_layout); layout_last = D_layout; for (cv = D_cvlist; cv; cv = cv->c_next) { p = Layer2Window(cv->c_layer); SetCanvasWindow(cv, 0); if (p) WindowChanged(p, 'u'); } pid = D_userpid; debug2("display: %#x displays: %#x\n", (unsigned int)display, (unsigned int)displays); FreeDisplay(); if (displays == 0) /* Flag detached-ness */ (void) chsock(); /* * tell 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(pid, sign); debug2("Detach: Signal %d to Attacher(%d)!\n", sign, pid); debug("Detach returns, we are successfully detached.\n"); signal(SIGHUP, SigHup); #undef AddStrSock } static int IsSymbol(char *e, char *s) { register int l; l = strlen(s); return strncmp(e, s, l) == 0 && e[l] == '='; } void MakeNewEnv() { register char **op, **np; static char stybuf[MAXSTR]; for (op = environ; *op; ++op) ; if (NewEnv) free((char *)NewEnv); NewEnv = np = (char **) malloc((unsigned) (op - environ + 7 + 1) * sizeof(char **)); if (!NewEnv) Panic(0, "%s", strnomem); sprintf(stybuf, "STY=%s", strlen(SockName) <= MAXSTR - 5 ? SockName : "?"); *np++ = stybuf; /* NewEnv[0] */ *np++ = Term; /* NewEnv[1] */ np++; /* room for SHELL */ #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") && !IsSymbol(*op, "SHELL") && !IsSymbol(*op, "LINES") && !IsSymbol(*op, "COLUMNS")) *np++ = *op; } *np = 0; } #if defined(USEVARARGS) && defined(__STDC__) #define DEFINE_VARARGS_FN(fnname) void fnname (int err, const char *fmt, VA_DOTS) #else #define DEFINE_VARARGS_FN(fnname) void fnname(err, fmt, VA_DOTS) \ int err; \ const char *fmt; \ VA_DECL #endif #define PROCESS_MESSAGE(B) do { \ char *p = B; \ VA_LIST(ap) \ VA_START(ap, fmt); \ fmt = DoNLS(fmt); \ (void)vsnprintf(p, sizeof(B) - 100, fmt, VA_ARGS(ap)); \ VA_END(ap); \ if (err) \ { \ p += strlen(p); \ *p++ = ':'; \ *p++ = ' '; \ strncpy(p, strerror(err), B + sizeof(B) - p - 1); \ B[sizeof(B) - 1] = 0; \ } \ } while (0) DEFINE_VARARGS_FN(Msg) { char buf[MAXPATHLEN*2]; PROCESS_MESSAGE(buf); debug2("Msg('%s') (%#x);\n", buf, (unsigned int)display); if (display && displays) MakeStatus(buf); else if (displays) { for (display = displays; display; display = display->d_next) MakeStatus(buf); } else if (display) { /* no displays but a display - must have forked. * send message to backend! */ char *tty = D_usertty; struct display *olddisplay = display; display = 0; /* only send once */ SendErrorMsg(tty, buf); display = olddisplay; } else printf("%s\r\n", buf); if (queryflag >= 0) write(queryflag, buf, strlen(buf)); } /* * Call FinitTerm for all displays, write a message to each and call eexit(); */ DEFINE_VARARGS_FN(Panic) { char buf[MAXPATHLEN*2]; PROCESS_MESSAGE(buf); debug3("Panic('%s'); display=%x displays=%x\n", buf, display, displays); if (displays == 0 && display == 0) { printf("%s\r\n", buf); if (PanicPid) Kill(PanicPid, SIG_BYE); } else if (displays == 0) { /* no displays but a display - must have forked. * send message to backend! */ char *tty = D_usertty; display = 0; SendErrorMsg(tty, buf); sleep(2); _exit(1); } else for (display = displays; display; display = display->d_next) { if (D_status) RemoveStatus(); FinitTerm(); Flush(3); #ifdef UTMPOK RestoreLoginSlot(); #endif SetTTY(D_userfd, &D_OldMode); fcntl(D_userfd, F_SETFL, 0); write(D_userfd, buf, strlen(buf)); write(D_userfd, "\n", 1); freetty(); if (D_userpid) Kill(D_userpid, SIG_BYE); } #ifdef MULTIUSER if (tty_oldmode >= 0) { # ifdef USE_SETEUID if (setuid(own_uid)) xseteuid(own_uid); /* may be a loop. sigh. */ # else setuid(own_uid); # endif debug1("Panic: changing back modes from %s\n", attach_tty); chmod(attach_tty, tty_oldmode); } #endif eexit(1); } DEFINE_VARARGS_FN(QueryMsg) { char buf[MAXPATHLEN*2]; if (queryflag < 0) return; PROCESS_MESSAGE(buf); write(queryflag, buf, strlen(buf)); } DEFINE_VARARGS_FN(Dummy) {} #undef PROCESS_MESSAGE #undef DEFINE_VARARGS_FN /* * '^' is allowed as an escape mechanism for control characters. jw. * * Added time insertion using ideas/code from /\ndy Jones * (andy@lingua.cltr.uq.OZ.AU) - thanks a lot! * */ #ifndef USE_LOCALE static const char days[] = "SunMonTueWedThuFriSat"; static const char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; #endif static char winmsg_buf[MAXSTR]; #define MAX_WINMSG_REND 256 /* rendition changes */ static int winmsg_rend[MAX_WINMSG_REND]; static int winmsg_rendpos[MAX_WINMSG_REND]; static int winmsg_numrend; static char *pad_expand(char *buf, char *p, int numpad, int padlen) { char *pn, *pn2; int i, r; padlen = padlen - (p - buf); /* space for rent */ if (padlen < 0) padlen = 0; pn2 = pn = p + padlen; r = winmsg_numrend; while (p >= buf) { if (r && *p != 127 && p - buf == winmsg_rendpos[r - 1]) { winmsg_rendpos[--r] = pn - buf; continue; } *pn-- = *p; if (*p-- == 127) { pn[1] = ' '; i = numpad > 0 ? (padlen + numpad - 1) / numpad : 0; padlen -= i; while (i-- > 0) *pn-- = ' '; numpad--; if (r && p - buf + 1== winmsg_rendpos[r - 1]) winmsg_rendpos[--r] = pn - buf + 1; } } return pn2; } struct backtick { struct backtick *next; int num; int tick; int lifespan; time_t bestbefore; char result[MAXSTR]; char **cmdv; struct event ev; char *buf; int bufi; }; struct backtick *backticks; static void backtick_filter(struct backtick *bt) { char *p, *q; int c; for (p = q = bt->result; (c = (unsigned char)*p++) != 0;) { if (c == '\t') c = ' '; if (c >= ' ' || c == '\005') *q++ = c; } *q = 0; } static void backtick_fn(struct event *ev, char *data) { struct backtick *bt; int i, j, k, l; bt = (struct backtick *)data; debug1("backtick_fn for #%d\n", bt->num); i = bt->bufi; l = read(ev->fd, bt->buf + i, MAXSTR - i); if (l <= 0) { debug1("EOF on backtick #%d\n", bt->num); evdeq(ev); close(ev->fd); ev->fd = -1; return; } debug1("read %d bytes\n", l); i += l; for (j = 0; j < l; j++) if (bt->buf[i - j - 1] == '\n') break; if (j < l) { for (k = i - j - 2; k >= 0; k--) if (bt->buf[k] == '\n') break; k++; bcopy(bt->buf + k, bt->result, i - j - k); bt->result[i - j - k - 1] = 0; backtick_filter(bt); WindowChanged(0, '`'); } if (j == l && i == MAXSTR) { j = MAXSTR/2; l = j + 1; } if (j < l){ if (j) bcopy(bt->buf + i - j, bt->buf, j); i = j; } bt->bufi = i; } void setbacktick(int num, int lifespan, int tick, char **cmdv) { struct backtick **btp, *bt; char **v; debug1("setbacktick called for backtick #%d\n", num); for (btp = &backticks; (bt = *btp) != 0; btp = &bt->next) if (bt->num == num) break; if (!bt && !cmdv) return; if (bt) { for (v = bt->cmdv; *v; v++) free(*v); free(bt->cmdv); if (bt->buf) free(bt->buf); if (bt->ev.fd >= 0) close(bt->ev.fd); evdeq(&bt->ev); } if (bt && !cmdv) { *btp = bt->next; free(bt); return; } if (!bt){ bt = (struct backtick *)malloc(sizeof *bt); if (!bt){ Msg(0, "%s", strnomem); return; } bzero(bt, sizeof(*bt)); bt->next = 0; *btp = bt; } bt->num = num; bt->tick = tick; bt->lifespan = lifespan; bt->bestbefore = 0; bt->result[0] = 0; bt->buf = 0; bt->bufi = 0; bt->cmdv = cmdv; bt->ev.fd = -1; if (bt->tick == 0 && bt->lifespan == 0) { debug("setbacktick: continuous mode\n"); bt->buf = (char *)malloc(MAXSTR); if (bt->buf == 0) { Msg(0, "%s", strnomem); setbacktick(num, 0, 0, (char **)0); return; } bt->ev.type = EV_READ; bt->ev.fd = readpipe(bt->cmdv); bt->ev.handler = backtick_fn; bt->ev.data = (char *)bt; if (bt->ev.fd >= 0) evenq(&bt->ev); } } static char * runbacktick(struct backtick *bt, int *tickp, time_t now) { int f, i, l, j; time_t now2; debug1("runbacktick called for backtick #%d\n", bt->num); if (bt->tick && (!*tickp || bt->tick < *tickp)) *tickp = bt->tick; if ((bt->lifespan == 0 && bt->tick == 0) || now < bt->bestbefore) { debug1("returning old result (%d)\n", bt->lifespan); return bt->result; } f = readpipe(bt->cmdv); if (f == -1) return bt->result; i = 0; while ((l = read(f, bt->result + i, sizeof(bt->result) - i)) > 0) { debug1("runbacktick: read %d bytes\n", l); i += l; for (j = 1; j < l; j++) if (bt->result[i - j - 1] == '\n') break; if (j == l && i == sizeof(bt->result)) { j = sizeof(bt->result) / 2; l = j + 1; } if (j < l) { bcopy(bt->result + i - j, bt->result, j); i = j; } } close(f); bt->result[sizeof(bt->result) - 1] = '\n'; if (i && bt->result[i - 1] == '\n') i--; debug1("runbacktick: finished, %d bytes\n", i); bt->result[i] = 0; backtick_filter(bt); (void)time(&now2); bt->bestbefore = now2 + bt->lifespan; return bt->result; } int AddWinMsgRend(const char *str, int r) { if (winmsg_numrend >= MAX_WINMSG_REND || str < winmsg_buf || str >= winmsg_buf + MAXSTR) return -1; winmsg_rend[winmsg_numrend] = r; winmsg_rendpos[winmsg_numrend] = str - winmsg_buf; winmsg_numrend++; return 0; } char *MakeWinMsgEv(char *str, struct win *win, int esc, int padlen, struct event *ev, int rec) { static int tick; char *s = str; register char *p = winmsg_buf; register int ctrl; struct timeval now; struct tm *tm; int l, i, r; int num; int zeroflg; int longflg; int minusflg; int plusflg; int qmflag = 0, omflag = 0, qmnumrend = 0; char *qmpos = 0; int numpad = 0; int lastpad = 0; int truncpos = -1; int truncper = 0; int trunclong = 0; struct backtick *bt = NULL; if (winmsg_numrend >= 0) winmsg_numrend = 0; else winmsg_numrend = -winmsg_numrend; tick = 0; tm = 0; ctrl = 0; gettimeofday(&now, NULL); for (; *s && (l = winmsg_buf + MAXSTR - 1 - p) > 0; s++, p++) { *p = *s; if (ctrl) { ctrl = 0; if (*s != '^' && *s >= 64) *p &= 0x1f; continue; } if (*s != esc) { if (esc == '%') { switch (*s) { #if 0 case '~': *p = BELL; break; #endif case '^': ctrl = 1; *p-- = '^'; break; default: break; } } continue; } if (*++s == esc) /* double escape ? */ continue; if ((plusflg = *s == '+') != 0) s++; if ((minusflg = *s == '-') != 0) s++; if ((zeroflg = *s == '0') != 0) s++; num = 0; while(*s >= '0' && *s <= '9') num = num * 10 + (*s++ - '0'); if ((longflg = *s == 'L') != 0) s++; switch (*s) { case '?': p--; if (qmpos) { if ((!qmflag && !omflag) || omflag == 1){ p = qmpos; if (qmnumrend < winmsg_numrend) winmsg_numrend = qmnumrend; } qmpos = 0; break; } qmpos = p; qmnumrend = winmsg_numrend; qmflag = omflag = 0; break; case ':': p--; if (!qmpos) break; if (qmflag && omflag != 1) { omflag = 1; qmpos = p; qmnumrend = winmsg_numrend; } else { p = qmpos; if (qmnumrend < winmsg_numrend) winmsg_numrend = qmnumrend; omflag = -1; } break; case 'd': case 'D': case 'm': case 'M': case 'y': case 'Y': case 'a': case 'A': case 's': case 'c': case 'C': if (l < 4) break; if (tm == 0) { time_t nowsec = now.tv_sec; tm = localtime(&nowsec); } qmflag = 1; if (!tick || tick > 3600) tick = 3600; switch (*s) { case 'd': sprintf(p, "%02d", tm->tm_mday % 100); break; case 'D': #ifdef USE_LOCALE strftime(p, l, (longflg ? "%A" : "%a"), tm); #else sprintf(p, "%3.3s", days + 3 * tm->tm_wday); #endif break; case 'm': sprintf(p, "%02d", tm->tm_mon + 1); break; case 'M': #ifdef USE_LOCALE strftime(p, l, (longflg ? "%B" : "%b"), tm); #else sprintf(p, "%3.3s", months + 3 * tm->tm_mon); #endif break; case 'y': sprintf(p, "%02d", tm->tm_year % 100); break; case 'Y': sprintf(p, "%04d", tm->tm_year + 1900); break; case 'a': sprintf(p, tm->tm_hour >= 12 ? "pm" : "am"); break; case 'A': sprintf(p, tm->tm_hour >= 12 ? "PM" : "AM"); break; case 's': sprintf(p, "%02d", tm->tm_sec); tick = 1; break; case 'c': sprintf(p, zeroflg ? "%02d:%02d" : "%2d:%02d", tm->tm_hour, tm->tm_min); if (!tick || tick > 60) tick = 60; break; case 'C': sprintf(p, zeroflg ? "%02d:%02d" : "%2d:%02d", (tm->tm_hour + 11) % 12 + 1, tm->tm_min); if (!tick || tick > 60) tick = 60; break; default: break; } p += strlen(p) - 1; break; case 'X': case 'x': *p = 0; for (i = 0; win && win->w_cmdargs[i]; i++) { if (l < strlen(win->w_cmdargs[i]) + 1) break; sprintf(p, i ? " %s" : "%s", win->w_cmdargs[i]); l -= strlen(p); p += strlen(p); if (i == 0 && *s == 'X') break; } p--; break; case 'l': #ifdef LOADAV *p = 0; if (l > 20) AddLoadav(p); if (*p) { qmflag = 1; p += strlen(p) - 1; } else *p = '?'; if (!tick || tick > 60) tick = 60; #else *p = '?'; #endif p += strlen(p) - 1; break; case '`': case 'h': if (rec >= 10 || (*s == 'h' && (win == 0 || win->w_hstatus == 0 || *win->w_hstatus == 0))) { p--; break; } if (*s == '`') { for (bt = backticks; bt; bt = bt->next) if (bt->num == num) break; if (bt == 0) { p--; break; } } { char savebuf[sizeof(winmsg_buf)]; int oldtick = tick; int oldnumrend = winmsg_numrend; *p = 0; strcpy(savebuf, winmsg_buf); winmsg_numrend = -winmsg_numrend; MakeWinMsgEv(*s == 'h' ? win->w_hstatus : runbacktick(bt, &oldtick, now.tv_sec), win, '\005', 0, (struct event *)0, rec + 1); debug2("oldtick=%d tick=%d\n", oldtick, tick); if (!tick || oldtick < tick) tick = oldtick; if ((int)strlen(winmsg_buf) < l) strcat(savebuf, winmsg_buf); strcpy(winmsg_buf, savebuf); while (oldnumrend < winmsg_numrend) winmsg_rendpos[oldnumrend++] += p - winmsg_buf; if (*p) qmflag = 1; p += strlen(p) - 1; } break; case 'w': case 'W': { struct win *oldfore = 0; char *ss; if (display) { oldfore = D_fore; D_fore = win; } ss = AddWindows(p, l - 1, (*s == 'w' ? 0 : 1) | (longflg ? 0 : 2) | (plusflg ? 4 : 0) | (minusflg ? 8 : 0), win ? win->w_number : -1); if (display) D_fore = oldfore; } if (*p) qmflag = 1; p += strlen(p) - 1; break; case 'u': *p = 0; if (win) AddOtherUsers(p, l - 1, win); if (*p) qmflag = 1; p += strlen(p) - 1; break; case 'f': *p = 0; if (win) AddWindowFlags(p, l - 1, win); if (*p) qmflag = 1; p += strlen(p) - 1; break; case 't': *p = 0; if (win && (int)strlen(win->w_title) < l) { strcpy(p, win->w_title); if (*p) qmflag = 1; } p += strlen(p) - 1; break; #ifdef ENCODINGS case 'e': *p = 0; D_encoding = nwin_options.encoding > 0 ? nwin_options.encoding : 0; if (win && win->w_encoding) { *p++ = ' '; strcpy(p, EncodingName(win->w_encoding)); } p += strlen(p) - 1; break; #endif case '{': { char rbuf[128]; s++; for (i = 0; i < 127; i++) if (s[i] && s[i] != '}') rbuf[i] = s[i]; else break; if (s[i] == '}' && winmsg_numrend < MAX_WINMSG_REND) { r = -1; rbuf[i] = 0; debug1("MakeWinMsg attrcolor %s\n", rbuf); if (i != 1 || rbuf[0] != '-') r = ParseAttrColor(rbuf, (char *)0, 0); if (r != -1 || (i == 1 && rbuf[0] == '-')) { winmsg_rend[winmsg_numrend] = r; winmsg_rendpos[winmsg_numrend] = p - winmsg_buf; winmsg_numrend++; } } s += i; p--; } break; case 'H': *p = 0; if ((int)strlen(HostName) < l) { strcpy(p, HostName); if (*p) qmflag = 1; } p += strlen(p) - 1; break; case 'S': { char *session_name; *p = 0; session_name = strchr(SockName, '.') + 1; if ((int)strlen(session_name) < l) { strcpy(p, session_name); if (*p) qmflag = 1; } p += strlen(p) - 1; } break; case 'p': { sprintf(p, "%d", (plusflg && display) ? D_userpid : getpid()); p += strlen(p) - 1; } break; case 'F': p--; /* small hack */ if (display && ((ev && ev == &D_forecv->c_captev) || (!ev && win && win == D_fore))) minusflg = !minusflg; if (minusflg) qmflag = 1; break; case 'P': p--; #ifdef COPY_PASTE if (display && ev && ev != &D_hstatusev) { /* Hack */ /* Is the layer in the current canvas in copy mode? */ struct canvas *cv = (struct canvas *)ev->data; if (ev == &cv->c_captev && cv->c_layer->l_layfn == &MarkLf) qmflag = 1; } #endif break; case 'E': p--; if (display && D_ESCseen) qmflag = 1; break; case '>': truncpos = p - winmsg_buf; truncper = num > 100 ? 100 : num; trunclong = longflg; p--; break; case '=': case '<': *p = ' '; if (num || zeroflg || plusflg || longflg || (*s != '=')) { /* expand all pads */ if (minusflg) { num = (plusflg ? lastpad : padlen) - num; if (!plusflg && padlen == 0) num = p - winmsg_buf; plusflg = 0; } else if (!zeroflg) { if (*s != '=' && num == 0 && !plusflg) num = 100; if (num > 100) num = 100; if (padlen == 0) num = p - winmsg_buf; else num = (padlen - (plusflg ? lastpad : 0)) * num / 100; } if (num < 0) num = 0; if (plusflg) num += lastpad; if (num > MAXSTR - 1) num = MAXSTR - 1; if (numpad) p = pad_expand(winmsg_buf, p, numpad, num); numpad = 0; if (p - winmsg_buf > num && !longflg) { int left, trunc; if (truncpos == -1) { truncpos = lastpad; truncper = 0; } trunc = lastpad + truncper * (num - lastpad) / 100; if (trunc > num) trunc = num; if (trunc < lastpad) trunc = lastpad; left = truncpos - trunc; if (left > p - winmsg_buf - num) left = p - winmsg_buf - num; debug1("lastpad = %d, ", lastpad); debug3("truncpos = %d, trunc = %d, left = %d\n", truncpos, trunc, left); if (left > 0) { if (left + lastpad > p - winmsg_buf) left = p - winmsg_buf - lastpad; if (p - winmsg_buf - lastpad - left > 0) bcopy(winmsg_buf + lastpad + left, winmsg_buf + lastpad, p - winmsg_buf - lastpad - left); p -= left; r = winmsg_numrend; while (r && winmsg_rendpos[r - 1] > lastpad) { r--; winmsg_rendpos[r] -= left; if (winmsg_rendpos[r] < lastpad) winmsg_rendpos[r] = lastpad; } if (trunclong) { if (p - winmsg_buf > lastpad) winmsg_buf[lastpad] = '.'; if (p - winmsg_buf > lastpad + 1) winmsg_buf[lastpad + 1] = '.'; if (p - winmsg_buf > lastpad + 2) winmsg_buf[lastpad + 2] = '.'; } } if (p - winmsg_buf > num) { p = winmsg_buf + num; if (trunclong) { if (num - 1 >= lastpad) p[-1] = '.'; if (num - 2 >= lastpad) p[-2] = '.'; if (num - 3 >= lastpad) p[-3] = '.'; } r = winmsg_numrend; while (r && winmsg_rendpos[r - 1] > num) winmsg_rendpos[--r] = num; } truncpos = -1; trunclong = 0; if (lastpad > p - winmsg_buf) lastpad = p - winmsg_buf; debug1("lastpad now %d\n", lastpad); } if (*s == '=') { while (p - winmsg_buf < num) *p++ = ' '; lastpad = p - winmsg_buf; truncpos = -1; trunclong = 0; debug1("lastpad2 now %d\n", lastpad); } p--; } else if (padlen) { *p = 127; /* internal pad representation */ numpad++; } break; case 'n': s++; /* FALLTHROUGH */ default: s--; if (l > 10 + num) { if (num == 0) num = 1; if (!win) sprintf(p, "%*s", num, num > 1 ? "--" : "-"); else sprintf(p, "%*d", num, win->w_number); qmflag = 1; p += strlen(p) - 1; } break; } } if (qmpos && !qmflag) p = qmpos + 1; *p = '\0'; if (numpad) { if (padlen > MAXSTR - 1) padlen = MAXSTR - 1; p = pad_expand(winmsg_buf, p, numpad, padlen); } if (ev) { evdeq(ev); /* just in case */ ev->timeout.tv_sec = 0; ev->timeout.tv_usec = 0; } if (ev && tick) { now.tv_usec = 100000; if (tick == 1) now.tv_sec++; else now.tv_sec += tick - (now.tv_sec % tick); ev->timeout = now; debug2("NEW timeout %d %d\n", ev->timeout.tv_sec, tick); } return winmsg_buf; } char *MakeWinMsg(char *s, struct win *win, int esc) { return MakeWinMsgEv(s, win, esc, 0, (struct event *)0, 0); } void PutWinMsg(char *s, int start, int max) { int i, p, l, r, n; struct mchar rend; struct mchar rendstack[MAX_WINMSG_REND]; int rendstackn = 0; if (s != winmsg_buf) { /* sorry, no fancy coloring available */ debug1("PutWinMsg %s plain\n", s); l = strlen(s); if (l > max) l = max; l -= start; s += start; while (l-- > 0) PUTCHARLP(*s++); return; } rend = D_rend; p = 0; l = strlen(s); debug2("PutWinMsg %s start attr %x\n", s, rend.attr); for (i = 0; i < winmsg_numrend && max > 0; i++) { if (p > winmsg_rendpos[i] || winmsg_rendpos[i] > l) break; if (p < winmsg_rendpos[i]) { n = winmsg_rendpos[i] - p; if (n > max) n = max; max -= n; p += n; while(n-- > 0) { if (start-- > 0) s++; else PUTCHARLP(*s++); } } r = winmsg_rend[i]; if (r == -1) { if (rendstackn > 0) rend = rendstack[--rendstackn]; } else { rendstack[rendstackn++] = rend; ApplyAttrColor(r, &rend); } SetRendition(&rend); } if (p < l){ n = l - p; if (n > max) n = max; while(n-- > 0) { if (start-- > 0) s++; else PUTCHARLP(*s++); } } } #ifdef DEBUG static void fds1(int i, int j) { while (i < j) { debug1("%d ", i); i++; } if ((j = open("/dev/null", 0)) >= 0) { fds1(i + 1, j); close(j); } else { while (dup(++i) < 0 && errno != EBADF) debug1("%d ", i); debug1(" [%d]\n", i); } } static void fds() { debug("fds: "); fds1(-1, -1); } #endif static void serv_read_fn(struct event *ev, char *data) { debug("Knock - knock!\n"); ReceiveMsg(); } static void serv_select_fn(struct event *ev, char *data) { struct win *p; debug("serv_select_fn called\n"); /* XXX: messages?? */ if (GotSigChld) SigChldHandler(); if (InterruptPlease) { debug("Backend received interrupt\n"); /* This approach is rather questionable in a multi-display * environment */ if (fore && displays) { #if defined(TERMIO) || defined(POSIX) char ibuf = displays->d_OldMode.tio.c_cc[VINTR]; #else char ibuf = displays->d_OldMode.m_tchars.t_intrc; #endif #ifdef PSEUDOS write(W_UWP(fore) ? fore->w_pwin->p_ptyfd : fore->w_ptyfd, &ibuf, 1); debug1("Backend wrote interrupt to %d", fore->w_number); debug1("%s\n", W_UWP(fore) ? " (pseudowin)" : ""); #else write(fore->w_ptyfd, &ibuf, 1); debug1("Backend wrote interrupt to %d\n", fore->w_number); #endif } InterruptPlease = 0; } for (p = windows; p; p = p->w_next){ if (p->w_bell == BELL_FOUND || p->w_bell == BELL_VISUAL) { struct canvas *cv; int visual = p->w_bell == BELL_VISUAL || visual_bell; p->w_bell = BELL_ON; for (display = displays; display; display = display->d_next) { for (cv = D_cvlist; cv; cv = cv->c_next) if (cv->c_layer->l_bottom == &p->w_layer) break; if (cv == 0) { p->w_bell = BELL_DONE; Msg(0, "%s", MakeWinMsg(BellString, p, '%')); } else if (visual && !D_VB && (!D_status || !D_status_bell)) { Msg(0, "%s", VisualBellString); if (D_status) { D_status_bell = 1; debug1("using vbell timeout %d\n", VBellWait); SetTimeout(&D_statusev, VBellWait ); } } } /* don't annoy the user with two messages */ if (p->w_monitor == MON_FOUND) p->w_monitor = MON_DONE; WindowChanged(p, 'f'); } if (p->w_monitor == MON_FOUND) { struct canvas *cv; p->w_monitor = MON_ON; for (display = displays; display; display = display->d_next) { for (cv = D_cvlist; cv; cv = cv->c_next) if (cv->c_layer->l_bottom == &p->w_layer) break; if (cv) continue; /* user already sees window */ #ifdef MULTIUSER if (!(ACLBYTE(p->w_mon_notify, D_user->u_id) & ACLBIT(D_user->u_id))) continue; /* user doesn't care */ #endif Msg(0, "%s", MakeWinMsg(ActivityString, p, '%')); p->w_monitor = MON_DONE; } WindowChanged(p, 'f'); } if (p->w_silence == SILENCE_FOUND) { /* Unset the flag if the user switched to this window. */ if (p->w_layer.l_cvlist) { p->w_silence = SILENCE_ON; WindowChanged(p, 'f'); } } } for (display = displays; display; display = display->d_next) { struct canvas *cv; if (D_status == STATUS_ON_WIN) continue; /* XXX: should use display functions! */ for (cv = D_cvlist; cv; cv = cv->c_next) { int lx, ly; /* normalize window, see resize.c */ lx = cv->c_layer->l_x; ly = cv->c_layer->l_y; if (lx == cv->c_layer->l_width) lx--; if (ly + cv->c_yoff < cv->c_ys) { int i, n = cv->c_ys - (ly + cv->c_yoff); cv->c_yoff = cv->c_ys - ly; RethinkViewportOffsets(cv); if (n > cv->c_layer->l_height) n = cv->c_layer->l_height; CV_CALL(cv, LScrollV(flayer, -n, 0, flayer->l_height - 1, 0); LayRedisplayLine(-1, -1, -1, 1); for (i = 0; i < n; i++) LayRedisplayLine(i, 0, flayer->l_width - 1, 1); if (cv == cv->c_display->d_forecv) LaySetCursor(); ); } else if (ly + cv->c_yoff > cv->c_ye) { int i, n = ly + cv->c_yoff - cv->c_ye; cv->c_yoff = cv->c_ye - ly; RethinkViewportOffsets(cv); if (n > cv->c_layer->l_height) n = cv->c_layer->l_height; CV_CALL(cv, LScrollV(flayer, n, 0, cv->c_layer->l_height - 1, 0); LayRedisplayLine(-1, -1, -1, 1); for (i = 0; i < n; i++) LayRedisplayLine(i + flayer->l_height - n, 0, flayer->l_width - 1, 1); if (cv == cv->c_display->d_forecv) LaySetCursor(); ); } if (lx + cv->c_xoff < cv->c_xs) { int i, n = cv->c_xs - (lx + cv->c_xoff); if (n < (cv->c_xe - cv->c_xs + 1) / 2) n = (cv->c_xe - cv->c_xs + 1) / 2; if (cv->c_xoff + n > cv->c_xs) n = cv->c_xs - cv->c_xoff; cv->c_xoff += n; RethinkViewportOffsets(cv); if (n > cv->c_layer->l_width) n = cv->c_layer->l_width; CV_CALL(cv, LayRedisplayLine(-1, -1, -1, 1); for (i = 0; i < flayer->l_height; i++) { LScrollH(flayer, -n, i, 0, flayer->l_width - 1, 0, 0); LayRedisplayLine(i, 0, n - 1, 1); } if (cv == cv->c_display->d_forecv) LaySetCursor(); ); } else if (lx + cv->c_xoff > cv->c_xe) { int i, n = lx + cv->c_xoff - cv->c_xe; if (n < (cv->c_xe - cv->c_xs + 1) / 2) n = (cv->c_xe - cv->c_xs + 1) / 2; if (cv->c_xoff - n + cv->c_layer->l_width - 1 < cv->c_xe) n = cv->c_xoff + cv->c_layer->l_width - 1 - cv->c_xe; cv->c_xoff -= n; RethinkViewportOffsets(cv); if (n > cv->c_layer->l_width) n = cv->c_layer->l_width; CV_CALL(cv, LayRedisplayLine(-1, -1, -1, 1); for (i = 0; i < flayer->l_height; i++) { LScrollH(flayer, n, i, 0, flayer->l_width - 1, 0, 0); LayRedisplayLine(i, flayer->l_width - n, flayer->l_width - 1, 1); } if (cv == cv->c_display->d_forecv) LaySetCursor(); ); } } } for (display = displays; display; display = display->d_next) { if (D_status == STATUS_ON_WIN || D_cvlist == 0 || D_cvlist->c_next == 0) continue; debug1("serv_select_fn: Restore on cv %#x\n", (int)D_forecv); CV_CALL(D_forecv, LayRestore();LaySetCursor()); } } static void logflush_fn(struct event *ev, char *data) { struct win *p; char *buf; int n; if (!islogfile(NULL)) return; /* no more logfiles */ logfflush(NULL); n = log_flush ? log_flush : (logtstamp_after + 4) / 5; if (n) { SetTimeout(ev, n * 1000); evenq(ev); /* re-enqueue ourself */ } if (!logtstamp_on) return; /* write fancy time-stamp */ for (p = windows; p; p = p->w_next) { if (!p->w_log) continue; p->w_logsilence += n; if (p->w_logsilence < logtstamp_after) continue; if (p->w_logsilence - n >= logtstamp_after) continue; buf = MakeWinMsg(logtstamp_string, p, '%'); logfwrite(p->w_log, buf, strlen(buf)); } } /* * Interprets ^?, ^@ and other ^-control-char notation. * Interprets \ddd octal notation * * The result is placed in *cp, p is advanced behind the parsed expression and * returned. */ static char *ParseChar(char *p, char *cp) { if (*p == 0) return 0; if (*p == '^' && p[1]) { if (*++p == '?') *cp = '\177'; else if (*p >= '@') *cp = Ctrl(*p); else return 0; ++p; } else if (*p == '\\' && *++p <= '7' && *p >= '0') { *cp = 0; do *cp = *cp * 8 + *p - '0'; while (*++p <= '7' && *p >= '0'); } else *cp = *p++; return p; } static int ParseEscape(char *p) { unsigned char buf[2]; if (*p == 0) SetEscape((struct acluser *)0, -1, -1); else { if ((p = ParseChar(p, (char *)buf)) == NULL || (p = ParseChar(p, (char *)buf+1)) == NULL || *p) return -1; SetEscape((struct acluser *)0, buf[0], buf[1]); } return 0; } void SetTtyname(bool fatal, struct stat *st) { int ret; int saved_errno = 0; attach_tty_is_in_new_ns = false; memset(&attach_tty_name_in_ns, 0, sizeof(attach_tty_name_in_ns)); errno = 0; attach_tty = ttyname(0); if (!attach_tty) { if (errno == ENODEV) { saved_errno = errno; attach_tty = "/proc/self/fd/0"; attach_tty_is_in_new_ns = true; ret = readlink(attach_tty, attach_tty_name_in_ns, sizeof(attach_tty_name_in_ns)); if (ret < 0 || (size_t)ret >= sizeof(attach_tty_name_in_ns)) Panic(0, "Bad tty '%s'", attach_tty); } else if (fatal) { Panic(0, "Must be connected to a terminal."); } else { attach_tty = ""; } } if (attach_tty && strcmp(attach_tty, "")) { if (stat(attach_tty, st)) Panic(errno, "Cannot access '%s'", attach_tty); if (strlen(attach_tty) >= MAXPATHLEN) Panic(0, "TtyName too long - sorry."); /* Only call CheckTtyname() if the device does not exist in * another namespace. */ if (saved_errno != ENODEV && CheckTtyname(attach_tty)) Panic(0, "Bad tty '%s'", attach_tty); } }