2
* Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
3
* Sadrul Habib Chowdhury (sadrul@users.sourceforge.net)
4
* Copyright (c) 2008, 2009
5
* Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
6
* Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
7
* Micah Cowan (micah@cowan.name)
8
* Sadrul Habib Chowdhury (sadrul@users.sourceforge.net)
9
* Copyright (c) 1993-2002, 2003, 2005, 2006, 2007
10
* Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
11
* Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
12
* Copyright (c) 1987 Oliver Laumann
15
* Authors: Hadi Bargi Rangin bargi@dots.physics.orst.edu
16
* Bill Barry barryb@dots.physics.orst.edu
17
* Randy Lundquist randyl@dots.physics.orst.edu
19
* Modifications Copyright (c) 1995 by
20
* Science Access Project, Oregon State University.
23
* This program is free software; you can redistribute it and/or modify
24
* it under the terms of the GNU General Public License as published by
25
* the Free Software Foundation; either version 3, or (at your option)
28
* This program is distributed in the hope that it will be useful,
29
* but WITHOUT ANY WARRANTY; without even the implied warranty of
30
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31
* GNU General Public License for more details.
33
* You should have received a copy of the GNU General Public License
34
* along with this program (see the file COPYING); if not, see
35
* http://www.gnu.org/licenses/, or contact Free Software Foundation, Inc.,
36
* 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
38
****************************************************************
41
#include <sys/types.h>
47
# include <sys/sysmacros.h>
52
# include <sys/ioctl.h>
62
# include <sys/stropts.h>
65
#if defined(SYSV) && !defined(ISC)
66
# include <sys/utsname.h>
69
#if defined(sequent) || defined(SVR4)
70
# include <sys/resource.h>
71
#endif /* sequent || SVR4 */
75
# include <sys/sioctl.h>
79
#if (defined(AUX) || defined(_AUX_SOURCE)) && defined(POSIX)
82
#if defined(USE_LOCALE) || defined(ENCODINGS)
85
#if defined(HAVE_NL_LANGINFO) && defined(ENCODINGS)
86
# include <langinfo.h>
94
#include "patchlevel.h"
97
* At the moment we only need the real password if the
98
* builtin lock is used. Therefore disable SHADOWPW if
99
* we do not really need it (kind of security thing).
108
#endif /* SHADOWPW */
110
#include "logfile.h" /* islogfile, logfflush */
117
extern char Term[], screenterm[], **environ, Termcap[];
119
int VBellWait, MsgWait, MsgMinWait, SilenceWait;
121
extern struct acluser *users;
122
extern struct display *displays, *display;
124
extern struct LayFuncs MarkLf;
127
extern int visual_bell;
129
extern unsigned char mark_key_tab[];
131
extern char version[];
132
extern char DefaultShell[];
134
extern char *zmodem_sendcmd;
135
extern char *zmodem_recvcmd;
137
extern struct layout *layout_last;
143
extern struct NewWindow nwin_undef, nwin_default, nwin_options;
146
static struct passwd *getpwbyname __P((char *, struct passwd *));
147
static void SigChldHandler __P((void));
148
static sigret_t SigChld __P(SIGPROTOARG);
149
static sigret_t SigInt __P(SIGPROTOARG);
150
static sigret_t CoreDump __P(SIGPROTOARG);
151
static sigret_t FinitHandler __P(SIGPROTOARG);
152
static void DoWait __P((void));
153
static void serv_read_fn __P((struct event *, char *));
154
static void serv_select_fn __P((struct event *, char *));
155
static void logflush_fn __P((struct event *, char *));
156
static void backtick_filter __P((struct backtick *));
157
static void backtick_fn __P((struct event *, char *));
158
static char *runbacktick __P((struct backtick *, int *, time_t));
159
static int IsSymbol __P((char *, char *));
160
static char *ParseChar __P((char *, char *));
161
static int ParseEscape __P((char *));
162
static char *pad_expand __P((char *, char *, int, int));
164
static void fds __P((void));
167
int nversion; /* numerical version, used for secondary DA */
175
struct mode attach_Mode;
177
char SockPath[MAXPATHLEN + 2 * MAXSTR];
178
char *SockName; /* SockName is pointer in SockPath */
179
char *SockMatch = NULL; /* session id command line argument */
180
int ServerSocket = -1;
181
struct event serv_read;
182
struct event serv_select;
183
struct event logflushev;
185
char **NewEnv = NULL;
187
char *RcFileName = NULL;
190
char *screenlogfile; /* filename layout */
191
int log_flush = 10; /* flush interval in seconds */
192
int logtstamp_on = 0; /* tstamp disabled */
193
char *logtstamp_string; /* stamp layout */
194
int logtstamp_after = 120; /* first tstamp after 120s */
195
char *hardcopydir = NULL;
197
char *VisualBellString;
198
char *ActivityString;
203
char *PowDetachString;
211
int iflag, rflag, dflag, lsflag, quietflag, wipeflag, xflag;
223
int tty_oldmode = -1;
226
char HostName[MAXSTR];
227
int MasterPid, PanicPid;
228
int real_uid, real_gid, eff_uid, eff_gid;
230
int ZombieKey_destroy, ZombieKey_resurrect, ZombieKey_onerror;
231
char *preselect = NULL; /* only used in Attach() */
234
char *screenencodings;
247
struct layer *flayer;
250
struct win *console_window;
251
#ifdef BUILTIN_TELNET
260
char strnomem[] = "Out of memory.";
263
static int InterruptPlease;
264
static int GotSigChld;
267
lf_secreopen(name, wantfd, l)
275
if (((got_fd = secopen(name, O_WRONLY | O_CREAT | O_APPEND, 0666)) < 0) ||
276
lf_move_fd(got_fd, wantfd) < 0)
279
debug1("lf_secreopen: failed for %s\n", name);
282
l->st->st_ino = l->st->st_dev = 0;
283
debug2("lf_secreopen: %d = %s\n", wantfd, name);
287
/********************************************************************/
288
/********************************************************************/
289
/********************************************************************/
292
static struct passwd *
293
getpwbyname(name, ppp)
299
struct spwd *sss = NULL;
300
static char *spw = NULL;
303
if (!ppp && !(ppp = getpwnam(name)))
306
/* Do password sanity check..., allow ##user for SUN_C2 security */
311
if (ppp->pw_passwd[0] == '#' && ppp->pw_passwd[1] == '#' &&
312
strcmp(ppp->pw_passwd + 2, ppp->pw_name) == 0)
316
char c = ppp->pw_passwd[n];
317
if (!(c == '.' || c == '/' || c == '$' ||
318
(c >= '0' && c <= '9') ||
319
(c >= 'a' && c <= 'z') ||
320
(c >= 'A' && c <= 'Z')))
325
/* try to determine real password */
326
if (n < 13 && sss == 0)
328
sss = getspnam(ppp->pw_name);
333
ppp->pw_passwd = spw = SaveStr(sss->sp_pwdp);
334
endspent(); /* this should delete all buffers ... */
337
endspent(); /* this should delete all buffers ... */
343
if (ppp->pw_passwd && strlen(ppp->pw_passwd) == 13 + 11)
344
ppp->pw_passwd[13] = 0; /* beware of linux's long passwords */
357
s = getenv("LC_ALL");
359
s = getenv("LC_CTYPE");
374
char socknamebuf[2 * MAXSTR];
376
char *myname = (ac == 0) ? "screen" : av[0];
379
#ifdef _MODE_T /* (jw) */
384
#if defined(SYSV) && !defined(ISC)
385
struct utsname utsnam;
387
struct NewWindow nwin;
388
int detached = 0; /* start up detached */
394
#if (defined(AUX) || defined(_AUX_SOURCE)) && defined(POSIX)
395
setcompat(COMPAT_POSIX|COMPAT_BSDPROT); /* turn on seteuid support */
397
#if defined(sun) && defined(SVR4)
399
/* Solaris' login blocks SIGHUP! This is _very bad_ */
402
sigprocmask(SIG_SETMASK, &sset, 0);
407
* First, close all unused descriptors
408
* (otherwise, we might have problems with the select() call)
414
snprintf(version, 59, "%d.%.2d.%.2d%s (%s%s) %s", REV, VERS,
415
PATCHLEVEL, STATE, ORIGIN, GIT_REV, DATE);
416
nversion = REV * 10000 + VERS * 100 + PATCHLEVEL;
417
debug2("-- screen debug started %s (%s)\n", *av, version);
431
debug("NAMEDPIPE\n");
433
#if defined(SIGWINCH) && defined(TIOCGWINSZ)
434
debug("Window size changing enabled\n");
464
debug1("NAME_MAX = %d\n", NAME_MAX);
467
BellString = SaveStr("Bell in window %n");
468
VisualBellString = SaveStr(" Wuff, Wuff!! ");
469
ActivityString = SaveStr("Activity in window %n");
470
screenlogfile = SaveStr("screenlog.%n");
471
logtstamp_string = SaveStr("-- %n:%t -- time-stamp -- %M/%d/%y %c:%s --\n");
472
hstatusstring = SaveStr("%h");
473
captionstring = SaveStr("%4n %t");
474
timestring = SaveStr("%c:%s %M %d %H%? %l%?");
475
wlisttit = SaveStr(" Num Name%=Flags");
476
wliststr = SaveStr("%4n %t%=%f");
478
BufferFile = SaveStr(DEFAULT_BUFFERFILE);
484
default_startup = (ac > 1) ? 0 : 1;
486
VBellWait = VBELLWAIT * 1000;
487
MsgWait = MSGWAIT * 1000;
488
MsgMinWait = MSGMINWAIT * 1000;
489
SilenceWait = SILENCEWAIT;
494
zmodem_sendcmd = SaveStr("!!! sz -vv -b ");
495
zmodem_recvcmd = SaveStr("!!! rz -vv -b -E");
499
CompileKeys((char *)0, 0, mark_key_tab);
503
screenencodings = SaveStr(SCREENENCODINGS);
509
nwin_options = nwin_undef;
510
strcpy(screenterm, "screen");
511
#ifdef BUILTIN_TELNET
515
logreopen_register(lf_secreopen);
518
/* if this is a login screen, assume -RR */
527
ShellProg = SaveStr(DefaultShell); /* to prevent nasty circles */
532
if (--ac > 0 && *ap == '-')
534
if (ap[1] == '-' && ap[2] == 0)
540
if (ap[1] == '-' && !strcmp(ap, "--version"))
541
Panic(0, "Screen version %s", version);
542
if (ap[1] == '-' && !strcmp(ap, "--help"))
543
exit_with_usage(myname, NULL, NULL);
544
while (ap && *ap && *++ap)
548
#ifdef BUILTIN_TELNET
557
nwin_options.aflag = 1;
562
case 'p': /* preselect */
568
exit_with_usage(myname, "Specify a window to preselect with -p", NULL);
575
bd.bd_start_braille = 1;
584
exit_with_usage(myname, "Specify an alternate rc-filename with -c", NULL);
593
exit_with_usage(myname, "Specify command characters with -e", NULL);
597
Panic(0, "Two characters are required with -e option, not '%s'.", ap);
606
nwin_options.flowflag = FLOW_NOW * 0;
613
nwin_options.flowflag = FLOW_NOW * 1;
616
nwin_options.flowflag = FLOW_AUTOFLAG;
619
exit_with_usage(myname, "Unknown flow option -%s", --ap);
624
exit_with_usage(myname, NULL, NULL);
625
nwin_options.histheight = atoi(*++av);
626
if (nwin_options.histheight < 0)
627
exit_with_usage(myname, "-h: %s: negative scrollback size?", *av);
632
case 't': /* title, the former AkA == -k */
634
exit_with_usage(myname, "Specify a new window-name with -t", NULL);
635
nwin_options.aka = *++av;
643
nwin_options.lflag = 0;
650
nwin_options.lflag = 1;
653
nwin_options.lflag = 3;
656
case 'i': /* -list */
658
if (ac > 1 && !SockMatch)
666
exit_with_usage(myname, "%s: Unknown suboption to -l", --ap);
670
if (strcmp(ap+1, "ipe"))
671
exit_with_usage(myname, "Unknown option %s", --ap);
674
if (ac > 1 && !SockMatch)
681
nwin_options.Lflag = 1;
686
case 'O': /* to be (or not to be?) deleted. jw. */
691
exit_with_usage(myname, "Specify terminal-type with -T", NULL);
692
if (strlen(*++av) < 20)
693
strcpy(screenterm, *av);
695
Panic(0, "-T: terminal name too long. (max. 20 char)");
696
nwin_options.term = screenterm;
710
if (ac > 1 && *av[1] != '-' && !SockMatch)
714
debug2("rflag=%d, SockMatch=%s\n", dflag, SockMatch);
722
rflag += (*ap == 'R') ? 2 : 1;
733
if (*av[1] != '-' && !SockMatch)
737
debug2("dflag=%d, SockMatch=%s\n", dflag, SockMatch);
744
exit_with_usage(myname, "Specify shell with -s", NULL);
747
ShellProg = SaveStr(*++av);
748
debug1("ShellProg: '%s'\n", ShellProg);
754
exit_with_usage(myname, "Specify session-name with -S", NULL);
758
exit_with_usage(myname, "Empty session-name?", NULL);
764
Panic(0, "Screen version %s", version);
768
nwin_options.encoding = nwin_options.encoding == -1 ? UTF8 : 0;
772
exit_with_usage(myname, "Unknown option %s", --ap);
785
#ifdef SIGBUS /* OOPS, linux has no bus errors! */
786
signal(SIGBUS, CoreDump);
788
signal(SIGSEGV, CoreDump);
792
setlocale(LC_ALL, "");
795
if (nwin_options.encoding == -1)
797
/* ask locale if we should start in UTF-8 mode */
798
# ifdef HAVE_NL_LANGINFO
800
setlocale(LC_CTYPE, "");
802
nwin_options.encoding = FindEncoding(nl_langinfo(CODESET));
803
debug1("locale says encoding = %d\n", nwin_options.encoding);
807
if ((s = locale_name()) && InStr(s, "UTF-8"))
808
nwin_options.encoding = UTF8;
810
debug1("environment says encoding=%d\n", nwin_options.encoding);
816
if ((s = locale_name()))
818
if(!strncmp(s, "zh_", 3) || !strncmp(s, "ja_", 3) || !strncmp(s, "ko_", 3))
826
if (nwin_options.aka)
829
if (nwin_options.encoding > 0)
831
size_t len = strlen(nwin_options.aka);
833
char *newbuf = malloc(3 * len);
835
Panic(0, "%s", strnomem);
836
newsz = RecodeBuf((unsigned char *)nwin_options.aka, len,
837
nwin_options.encoding, 0, (unsigned char *)newbuf);
838
newbuf[newsz] = '\0';
839
nwin_options.aka = newbuf;
844
/* If we just use the original value from av,
845
subsequent shelltitle invocations will attempt to free
846
space we don't own... */
847
nwin_options.aka = SaveStr(nwin_options.aka);
851
if (SockMatch && strlen(SockMatch) >= MAXSTR)
852
Panic(0, "Ridiculously long socketname - try again.");
853
if (cmdflag && !rflag && !dflag && !xflag)
855
if (!cmdflag && dflag && mflag && !(rflag || xflag))
859
nwin.encoding = nwin_undef.encoding; /* let screenrc overwrite it */
864
/* make the write() calls return -1 on all errors */
867
* Ronald F. Guilmette, Oct 29 '94, bug-gnu-utils@prep.ai.mit.edu:
868
* It appears that in System V Release 4, UNIX, if you are writing
869
* an output file and you exceed the currently set file size limit,
870
* you _don't_ just get the call to `write' returning with a
871
* failure code. Rather, you get a signal called `SIGXFSZ' which,
872
* if neither handled nor ignored, will cause your program to crash
875
signal(SIGXFSZ, SIG_IGN);
879
signal(SIGPIPE, SIG_IGN);
886
sh = getenv("SHELL");
887
ShellProg = SaveStr(sh ? sh : DefaultShell);
889
ShellArgs[0] = ShellProg;
890
home = getenv("HOME");
891
if (!mflag && !SockMatch)
894
if (sty && *sty == 0)
899
if (!(nethackflag = (getenv("NETHACKOPTIONS") != NULL)))
901
char nethackrc[MAXPATHLEN];
903
if (home && (strlen(home) < (MAXPATHLEN - 20)))
905
sprintf(nethackrc,"%s/.nethackrc", home);
906
nethackflag = !access(nethackrc, F_OK);
912
own_uid = multi_uid = real_uid;
913
if (SockMatch && (sockp = index(SockMatch, '/')))
917
SockMatch = sockp + 1;
921
if ((mppp = getpwnam(multi)) == (struct passwd *)0)
922
Panic(0, "Cannot identify account '%s'.", multi);
923
multi_uid = mppp->pw_uid;
924
multi_home = SaveStr(mppp->pw_dir);
925
if (strlen(multi_home) > MAXPATHLEN - 10)
926
Panic(0, "home directory path too long");
928
/* always fake multi attach mode */
935
/* Special case: effective user is multiuser. */
936
if (eff_uid && (multi_uid != eff_uid))
937
Panic(0, "Must run suid root for multiuser support.");
939
if (SockMatch && *SockMatch == 0)
941
#endif /* MULTIUSER */
943
if ((LoginName = getlogin()) && LoginName[0] != '\0')
945
if ((ppp = getpwnam(LoginName)) != (struct passwd *) 0)
946
if ((int)ppp->pw_uid != real_uid)
947
ppp = (struct passwd *) 0;
951
if ((ppp = getpwuid(real_uid)) == 0)
953
Panic(0, "getpwuid() can't identify your account!");
956
LoginName = ppp->pw_name;
958
LoginName = SaveStr(LoginName);
960
ppp = getpwbyname(LoginName, ppp);
962
#if !defined(SOCKDIR) && defined(MULTIUSER)
963
if (multi && !multiattach)
965
if (home && strcmp(home, ppp->pw_dir))
966
Panic(0, "$HOME must match passwd entry for multiuser screens.");
970
#define SET_GUID() do \
974
eff_uid = real_uid; \
975
eff_gid = real_gid; \
978
#define SET_TTYNAME(fatal) do \
980
if (!(attach_tty = ttyname(0))) \
983
Panic(0, "Must be connected to a terminal."); \
989
if (stat(attach_tty, &st)) \
990
Panic(errno, "Cannot access '%s'", attach_tty); \
991
if (CheckTtyname(attach_tty)) \
992
Panic(0, "Bad tty '%s'", attach_tty); \
994
if (strlen(attach_tty) >= MAXPATHLEN) \
995
Panic(0, "TtyName too long - sorry."); \
998
if (home == 0 || *home == '\0')
1000
if (strlen(LoginName) > MAXLOGINLEN)
1001
Panic(0, "LoginName too long - sorry.");
1003
if (multi && strlen(multi) > MAXLOGINLEN)
1004
Panic(0, "Screen owner name too long - sorry.");
1006
if (strlen(home) > MAXPATHLEN - 25)
1007
Panic(0, "$HOME too long - sorry.");
1010
if (!detached && !lsflag && !cmdflag && !(dflag && !mflag && !rflag && !xflag) && !(sty && !SockMatch && !mflag && !rflag && !xflag))
1016
/* ttyname implies isatty */
1019
tty_mode = (int)st.st_mode & 0777;
1023
fl = fcntl(0, F_GETFL, 0);
1024
if (fl != -1 && (fl & (O_RDWR|O_RDONLY|O_WRONLY)) == O_RDWR)
1027
if (attach_fd == -1)
1029
if ((n = secopen(attach_tty, O_RDWR | O_NONBLOCK, 0)) < 0)
1030
Panic(0, "Cannot open your terminal '%s' - please check.", attach_tty);
1033
debug2("attach_tty is %s, attach_fd is %d\n", attach_tty, attach_fd);
1035
if ((attach_term = getenv("TERM")) == 0 || *attach_term == 0)
1036
Panic(0, "Please set a terminal type.");
1037
if (strlen(attach_term) > sizeof(D_termname) - 1)
1038
Panic(0, "$TERM too long - sorry.");
1039
GetTTY(0, &attach_Mode);
1040
#ifdef DEBUGGGGGGGGGGGGGGG
1041
DebugTTY(&attach_Mode);
1046
oumask = umask(0); /* well, unsigned never fails? jw. */
1048
if ((oumask = (int)umask(0)) == -1)
1049
Panic(errno, "Cannot change umask to zero");
1051
SockDir = getenv("SCREENDIR");
1054
if (strlen(SockDir) >= MAXPATHLEN - 1)
1055
Panic(0, "Ridiculously long $SCREENDIR - try again.");
1058
Panic(0, "No $SCREENDIR with multi screens, please.");
1065
sprintf(SockPath, "%s/.screen", multi_home);
1069
sprintf(SockPath, "%s/S-%s", SockDir, multi);
1078
sprintf(SockPath, "%s/.screen", home);
1084
if (access(SockDir, F_OK))
1086
debug1("SockDir '%s' missing ...\n", SockDir);
1087
if (UserContext() > 0)
1089
if (mkdir(SockDir, 0700))
1093
if (UserStatus() <= 0)
1094
Panic(0, "Cannot make directory '%s'.", SockDir);
1096
if (SockDir != SockPath)
1097
strcpy(SockPath, SockDir);
1103
if (stat(SockDir, &st))
1105
n = (eff_uid == 0 && (real_uid || eff_gid == real_gid)) ? 0755 :
1106
(eff_gid != real_gid) ? 0775 :
1112
if (mkdir(SockDir, n) == -1)
1113
Panic(errno, "Cannot make directory '%s'", SockDir);
1117
if (!S_ISDIR(st.st_mode))
1118
Panic(0, "'%s' must be a directory.", SockDir);
1119
if (eff_uid == 0 && real_uid && (int)st.st_uid != eff_uid)
1120
Panic(0, "Directory '%s' must be owned by root.", SockDir);
1121
n = (eff_uid == 0 && (real_uid || (st.st_mode & 0775) != 0775)) ? 0755 :
1122
(eff_gid == (int)st.st_gid && eff_gid != real_gid) ? 0775 :
1124
if (((int)st.st_mode & 0777) != n)
1125
Panic(0, "Directory '%s' must have mode %03o.", SockDir, n);
1127
sprintf(SockPath, "%s/S-%s", SockDir, LoginName);
1128
if (access(SockPath, F_OK))
1130
if (mkdir(SockPath, 0700) == -1 && errno != EEXIST)
1131
Panic(errno, "Cannot make directory '%s'", SockPath);
1132
(void) chown(SockPath, real_uid, real_gid);
1138
if (stat(SockPath, &st) == -1)
1139
Panic(errno, "Cannot access %s", SockPath);
1141
if (!S_ISDIR(st.st_mode))
1142
Panic(0, "%s is not a directory.", SockPath);
1146
if ((int)st.st_uid != multi_uid)
1147
Panic(0, "%s is not the owner of %s.", multi, SockPath);
1152
if ((int)st.st_uid != real_uid)
1153
Panic(0, "You are not the owner of %s.", SockPath);
1155
if ((st.st_mode & 0777) != 0700)
1156
Panic(0, "Directory %s must have mode 700.", SockPath);
1157
if (SockMatch && index(SockMatch, '/'))
1158
Panic(0, "Bad session name '%s'", SockMatch);
1159
SockName = SockPath + strlen(SockPath) + 1;
1161
(void) umask(oumask);
1162
debug2("SockPath: %s SockMatch: %s\n", SockPath, SockMatch ? SockMatch : "NULL");
1164
#if defined(SYSV) && !defined(ISC)
1165
if (uname(&utsnam) == -1)
1166
Panic(errno, "uname");
1167
strncpy(HostName, utsnam.nodename, sizeof(utsnam.nodename) < MAXSTR ? sizeof(utsnam.nodename) : MAXSTR - 1);
1168
HostName[sizeof(utsnam.nodename) < MAXSTR ? sizeof(utsnam.nodename) : MAXSTR - 1] = '\0';
1170
(void) gethostname(HostName, MAXSTR);
1171
HostName[MAXSTR - 1] = '\0';
1173
if ((ap = index(HostName, '.')) != NULL)
1182
real_uid = multi_uid;
1185
i = FindSocket((int *)NULL, &fo, &oth, SockMatch);
1190
exit(9 + (fo || oth ? 1 : 0) + fo);
1193
Panic(0, "No Sockets found in %s.\n", SockPath);
1194
Panic(0, "%d Socket%s in %s.\n", fo, fo > 1 ? "s" : "", SockPath);
1197
signal(SIG_BYE, AttacherFinit); /* prevent races */
1200
/* attach_tty is not mandatory */
1203
Panic(0, "Please specify a command.");
1205
SendCmdMessage(sty, SockMatch, av, queryflag >= 0);
1208
else if (rflag || xflag)
1210
debug("screen -r: - is there anybody out there?\n");
1211
if (Attach(MSG_ATTACH))
1218
Panic(0, "Can't create sessions of other users.");
1220
debug("screen -r: backend not responding -- still crying\n");
1222
else if (dflag && !mflag)
1226
Msg(0, "[%s %sdetached.]\n", SockName, (dflag > 1 ? "power " : ""));
1230
if (!SockMatch && !mflag && sty)
1232
/* attach_tty is not mandatory */
1235
nwin_options.args = av;
1236
SendCreateMsg(sty, &nwin);
1240
nwin_compose(&nwin_default, &nwin_options, &nwin_default);
1242
if (!detached || dflag != 2)
1250
Panic(errno, "fork");
1258
sprintf(socknamebuf, "%d.%s", MasterPid, SockMatch);
1260
sprintf(socknamebuf, "%d.%s.%s", MasterPid, stripdev(attach_tty), HostName);
1261
for (ap = socknamebuf; *ap; ap++)
1265
if (strlen(socknamebuf) > NAME_MAX)
1266
socknamebuf[NAME_MAX] = 0;
1268
sprintf(SockPath + strlen(SockPath), "/%s", socknamebuf);
1275
PanicPid = getppid();
1277
if (DefaultEsc == -1)
1278
DefaultEsc = Ctrl('a');
1279
if (DefaultMetaEsc == -1)
1280
DefaultMetaEsc = 'a';
1282
ap = av0 + strlen(av0) - 1;
1285
if (!strncmp("screen", ap, 6))
1287
strncpy(ap, "SCREEN", 6); /* name this process "SCREEN-BACKEND" */
1299
if (dfp && dfp != stderr)
1301
sprintf(buf, "%s/SCREEN.%d", DEBUGDIR, (int)getpid());
1302
if ((dfp = fopen(buf, "w")) == NULL)
1305
(void) chmod(buf, 0666);
1310
if (attach_fd == -1)
1312
if ((n = secopen(attach_tty, O_RDWR, 0)) < 0)
1313
Panic(0, "Cannot reopen '%s' - please check.", attach_tty);
1320
freopen("/dev/null", "r", stdin);
1321
freopen("/dev/null", "w", stdout);
1326
freopen("/dev/null", "w", stderr);
1327
debug("-- screen.back debug started\n");
1330
* This guarantees that the session owner is listed, even when we
1331
* start detached. From now on we should not refer to 'LoginName'
1332
* any more, use users->u_name instead.
1334
if (UserAdd(LoginName, (char *)0, (struct acluser **)0) < 0)
1335
Panic(0, "Could not create user info");
1338
if (MakeDisplay(LoginName, attach_tty, attach_term, n, getppid(), &attach_Mode) == 0)
1339
Panic(0, "Could not alloc display");
1342
D_encoding = nwin_options.encoding > 0 ? nwin_options.encoding : 0;
1343
debug1("D_encoding = %d\n", D_encoding);
1349
/* user started us with -S option */
1350
sprintf(socknamebuf, "%d.%s", (int)getpid(), SockMatch);
1354
sprintf(socknamebuf, "%d.%s.%s", (int)getpid(), stripdev(attach_tty),
1357
for (ap = socknamebuf; *ap; ap++)
1361
if (strlen(socknamebuf) > NAME_MAX)
1363
debug2("Socketname %s truncated to %d chars\n", socknamebuf, NAME_MAX);
1364
socknamebuf[NAME_MAX] = 0;
1367
sprintf(SockPath + strlen(SockPath), "/%s", socknamebuf);
1369
ServerSocket = MakeServerSocket();
1372
# ifdef ALLOW_SYSSCREENRC
1373
if ((ap = getenv("SYSSCREENRC")))
1374
(void)StartRc(ap, 0);
1377
(void)StartRc(ETCSCREENRC, 0);
1379
(void)StartRc(RcFileName, 0);
1383
# endif /* UTNOKEEP */
1384
# endif /* UTMPOK */
1387
if (InitTermcap(0, 0))
1389
debug("Could not init termcap - exiting\n");
1390
fcntl(D_userfd, F_SETFL, 0); /* Flush sets FNBLOCK */
1393
Kill(D_userpid, SIG_BYE);
1396
MakeDefaultCanvas();
1408
signal(SIGHUP, SigHup);
1409
signal(SIGINT, FinitHandler);
1410
signal(SIGQUIT, FinitHandler);
1411
signal(SIGTERM, FinitHandler);
1413
signal(SIGTTIN, SIG_IGN);
1414
signal(SIGTTOU, SIG_IGN);
1420
SetMode(&D_OldMode, &D_NewMode, D_flow, iflag);
1421
/* Note: SetMode must be called _before_ FinishRc. */
1422
SetTTY(D_userfd, &D_NewMode);
1423
if (fcntl(D_userfd, F_SETFL, FNBLOCK))
1424
Msg(errno, "Warning: NBLOCK fcntl failed");
1427
brktty(-1); /* just try */
1428
signal(SIGCHLD, SigChld);
1430
# ifdef ALLOW_SYSSCREENRC
1431
if ((ap = getenv("SYSSCREENRC")))
1435
FinishRc(ETCSCREENRC);
1437
FinishRc(RcFileName);
1439
debug2("UID %d EUID %d\n", (int)getuid(), (int)geteuid());
1440
if (windows == NULL)
1442
debug("We open one default window, as screenrc did not specify one.\n");
1443
if (MakeWindow(&nwin) == -1)
1446
struct timeval tv = { MsgWait/1000, 1000*(MsgWait%1000) };
1449
Msg(0, "Sorry, could not find a PTY or TTY.");
1450
// allow user to exit early by pressing any key.
1451
select(1, &rfd, NULL, NULL, &tv);
1456
else if (ac) /* Screen was invoked with a command */
1465
if (display && default_startup)
1466
display_copyright();
1467
signal(SIGINT, SigInt);
1468
if (rflag && (rflag & 1) == 0 && !quietflag)
1470
Msg(0, "New screen...");
1474
serv_read.type = EV_READ;
1475
serv_read.fd = ServerSocket;
1476
serv_read.handler = serv_read_fn;
1479
serv_select.pri = -10;
1480
serv_select.type = EV_ALWAYS;
1481
serv_select.handler = serv_select_fn;
1482
evenq(&serv_select);
1484
logflushev.type = EV_TIMEOUT;
1485
logflushev.handler = logflush_fn;
1493
WindowDied(p, wstat, wstat_valid)
1504
if (p->w_destroyev.data == (char *)p)
1506
wstat = p->w_exitstatus;
1508
evdeq(&p->w_destroyev);
1509
p->w_destroyev.data = 0;
1512
#if defined(BSDJOBS) && !defined(BSDWAIT)
1513
if (!wstat_valid && p->w_pid > 0)
1515
/* EOF on file descriptor. The process is probably also dead.
1517
if (waitpid(p->w_pid, &wstat, WNOHANG | WUNTRACED) == p->w_pid)
1524
if (ZombieKey_destroy && ZombieKey_onerror && wstat_valid &&
1525
WIFEXITED(wstat) && WEXITSTATUS(wstat) == 0)
1528
if (ZombieKey_destroy && !killit)
1530
char buf[100], *s, reason[100];
1534
if (WIFEXITED(wstat))
1535
if (WEXITSTATUS(wstat))
1536
sprintf(reason, "terminated with exit status %d", WEXITSTATUS(wstat));
1538
sprintf(reason, "terminated normally");
1539
else if (WIFSIGNALED(wstat))
1540
sprintf(reason, "terminated with signal %d%s", WTERMSIG(wstat),
1542
WCOREDUMP(wstat) ? " (core file generated)" : "");
1547
sprintf(reason, "detached from window");
1552
s[strlen(s) - 1] = '\0';
1553
debug3("window %d (%s) going into zombie state fd %d",
1554
p->w_number, p->w_title, p->w_ptyfd);
1556
if (p->w_slot != (slot_t)0 && p->w_slot != (slot_t)-1)
1559
p->w_slot = 0; /* "detached" */
1564
p->w_deadpid = p->w_pid;
1567
/* p->w_y = p->w_bot; */
1568
p->w_y = MFindUsedLine(p, p->w_bot, 1);
1569
sprintf(buf, "\n\r=== Command %s (%s) ===", reason, s ? s : "?");
1570
WriteString(p, buf, strlen(buf));
1571
WindowChanged(p, 'f');
1592
signal(SIGCHLD, SigChld);
1595
if (stat(SockPath, &st) == -1)
1597
debug1("SigChldHandler: Yuck! cannot stat '%s'\n", SockPath);
1598
if (!RecoverSocket())
1600
debug("SCREEN cannot recover from corrupt Socket, bye\n");
1604
debug1("'%s' reconstructed\n", SockPath);
1607
debug2("SigChldHandler: stat '%s' o.k. (%03o)\n", SockPath, (int)st.st_mode);
1613
debug("SigChld()\n");
1621
/* Hangup all displays */
1622
while ((display = displays) != 0)
1628
* the backend's Interrupt handler
1629
* we cannot insert the intrc directly, as we never know
1638
debug("SigInt()\n");
1639
if (fore && displays)
1641
# if defined(TERMIO) || defined(POSIX)
1642
ibuf = displays->d_OldMode.tio.c_cc[VINTR];
1644
ibuf = displays->d_OldMode.m_tchars.t_intrc;
1647
write(fore->w_ptyfd, &ibuf, 1);
1650
signal(SIGINT, SigInt);
1651
debug("SigInt() careful\n");
1652
InterruptPlease = 1;
1660
/* if running with s-bit, we must reset the s-bit, so that we get a
1664
struct display *disp;
1667
char *dump_msg = " (core dumped)";
1669
int running_w_s_bit = getuid() != geteuid();
1670
#if defined(SHADOWPW) && !defined(DEBUG) && !defined(DUMPSHADOW)
1671
if (running_w_s_bit)
1675
#if defined(SYSVSIGS) && defined(SIGHASARG)
1676
signal(sigsig, SIG_IGN);
1683
sprintf(buf, "\r\n[screen caught signal %d.%s]\r\n", sigsig, dump_msg);
1685
sprintf(buf, "\r\n[screen caught a fatal signal.%s]\r\n", dump_msg);
1688
for (disp = displays; disp; disp = disp->d_next)
1690
if (disp->d_nonblock < -1 || disp->d_nonblock > 1000000)
1692
fcntl(disp->d_userfd, F_SETFL, 0);
1693
SetTTY(disp->d_userfd, &D_OldMode);
1694
write(disp->d_userfd, buf, strlen(buf));
1695
Kill(disp->d_userpid, SIG_BYE);
1698
if (running_w_s_bit)
1700
#if defined(SHADOWPW) && !defined(DEBUG) && !defined(DUMPSHADOW)
1701
Kill(getpid(), SIGKILL);
1703
#else /* SHADOWPW && !DEBUG */
1705
#endif /* SHADOWPW && !DEBUG */
1716
struct win *p, *next;
1725
while ((pid = waitpid(-1, &wstat, WNOHANG | WUNTRACED)) > 0)
1729
* From: rouilj@sni-usa.com (John Rouillard)
1730
* note that WUNTRACED is not documented to work, but it is defined in
1731
* /usr/include/sys/wait.h, so it may work
1733
while ((pid = wait2(&wstat, WNOHANG | WUNTRACED )) > 0)
1734
# else /* USE_WAIT2 */
1735
while ((pid = wait3(&wstat, WNOHANG | WUNTRACED, (struct rusage *) 0)) > 0)
1736
# endif /* USE_WAIT2 */
1739
while ((pid = wait(&wstat)) < 0)
1743
#endif /* BSDJOBS */
1745
for (p = windows; p; p = next)
1748
if ( (p->w_pid && pid == p->w_pid) ||
1749
(p->w_deadpid && pid == p->w_deadpid) )
1751
/* child has ceased to exist */
1755
if (WIFSTOPPED(wstat))
1757
debug3("Window %d pid %d: WIFSTOPPED (sig %d)\n", p->w_number, pid, WSTOPSIG(wstat));
1759
if (WSTOPSIG(wstat) == SIGTTIN)
1761
Msg(0, "Suspended (tty input)");
1766
if (WSTOPSIG(wstat) == SIGTTOU)
1768
Msg(0, "Suspended (tty output)");
1772
/* Try to restart process */
1773
Msg(0, "Child has been stopped, restarting.");
1774
if (killpg(pid, SIGCONT))
1780
/* Screen will detect the window has died when the window's
1781
* file descriptor signals EOF (which it will do when the process in
1782
* the window terminates). So do this in a timeout of 10 seconds.
1783
* (not doing this at all might also work)
1784
* See #27061 for more details.
1786
p->w_destroyev.data = (char *)p;
1787
p->w_exitstatus = wstat;
1788
SetTimeout(&p->w_destroyev, 10 * 1000);
1789
evenq(&p->w_destroyev);
1794
if (p->w_pwin && pid == p->w_pwin->p_pid)
1796
debug2("pseudo of win Nr %d died. pid == %d\n", p->w_number, p->w_pwin->p_pid);
1804
debug1("pid %d not found - hope that's ok\n", pid);
1811
FinitHandler SIGDEFARG
1814
debug1("FinitHandler called, sig %d.\n", sigsig);
1816
debug("FinitHandler called.\n");
1826
signal(SIGCHLD, SIG_DFL);
1827
signal(SIGHUP, SIG_IGN);
1828
debug1("Finit(%d);\n", i);
1831
struct win *p = windows;
1832
windows = windows->w_next;
1835
if (ServerSocket != -1)
1837
debug1("we unlink(%s)\n", SockPath);
1842
(void) unlink(SockPath);
1848
for (display = displays; display; display = display->d_next)
1856
AddStr("[screen is terminating]\r\n");
1858
SetTTY(D_userfd, &D_OldMode);
1859
fcntl(D_userfd, F_SETFL, 0);
1861
Kill(D_userpid, SIG_BYE);
1864
* we _cannot_ call eexit(i) here,
1865
* instead of playing with the Socket above. Sigh.
1875
if (ServerSocket != -1)
1877
debug1("we unlink(%s)\n", SockPath);
1880
(void) unlink(SockPath);
1890
debug1("Hangup %x\n", display);
1896
if (auto_detach || displays->d_next)
1903
* Detach now has the following modes:
1904
*D_DETACH SIG_BYE detach backend and exit attacher
1905
*D_HANGUP SIG_BYE detach backend and exit attacher
1906
*D_STOP SIG_STOP stop attacher (and detach backend)
1907
*D_REMOTE SIG_BYE remote detach -- reattach to new attacher
1908
*D_POWER SIG_POWER_BYE power detach -- attacher kills his parent
1909
*D_REMOTE_POWER SIG_POWER_BYE remote power detach -- both
1910
*D_LOCK SIG_LOCK lock the attacher
1912
* we always remove our utmp slots. (even when "lock" or "stop")
1913
* Note: Take extra care here, we may be called by interrupt!
1926
#define AddStrSock(msg) do { \
1929
AddStr("[" msg " from "); \
1934
AddStr("[" msg "]\r\n"); \
1937
signal(SIGHUP, SIG_IGN);
1938
debug1("Detach(%d)\n", mode);
1950
AddStrSock("detached");
1958
#ifdef REMOTE_DETACH
1960
AddStrSock("remote detached");
1966
AddStrSock("power detached");
1967
if (PowDetachString)
1969
AddStr(PowDetachString);
1972
sign = SIG_POWER_BYE;
1974
#ifdef REMOTE_DETACH
1975
case D_REMOTE_POWER:
1976
AddStrSock("remote power detached");
1977
if (PowDetachString)
1979
AddStr(PowDetachString);
1982
sign = SIG_POWER_BYE;
1989
/* tell attacher to lock terminal with a lockprg. */
1993
if (displays->d_next == 0)
1995
for (p = windows; p; p = p->w_next)
1997
if (p->w_slot != (slot_t) -1 && !(p->w_lflag & 2))
2001
* Set the slot to 0 to get the window
2004
p->w_slot = (slot_t) 0;
2008
if (mode != D_HANGUP)
2011
if (displays->d_next == 0 && console_window)
2013
if (TtyGrabConsole(console_window->w_ptyfd, 0, "detach"))
2015
debug("could not release console - killing window\n");
2016
KillWindow(console_window);
2017
display = displays; /* restore display */
2023
ReleaseAutoWritelock(display, D_fore);
2025
D_user->u_detachwin = D_fore->w_number;
2026
D_user->u_detachotherwin = D_other ? D_other->w_number : -1;
2028
AutosaveLayout(D_layout);
2029
layout_last = D_layout;
2030
for (cv = D_cvlist; cv; cv = cv->c_next)
2032
p = Layer2Window(cv->c_layer);
2033
SetCanvasWindow(cv, 0);
2035
WindowChanged(p, 'u');
2039
debug2("display: %#x displays: %#x\n", (unsigned int)display, (unsigned int)displays);
2042
/* Flag detached-ness */
2045
* tell father what to do. We do that after we
2046
* freed the tty, thus getty feels more comfortable on hpux
2047
* if it was a power detach.
2050
debug2("Detach: Signal %d to Attacher(%d)!\n", sign, pid);
2051
debug("Detach returns, we are successfully detached.\n");
2052
signal(SIGHUP, SigHup);
2063
return strncmp(e, s, l) == 0 && e[l] == '=';
2069
register char **op, **np;
2070
static char stybuf[MAXSTR];
2072
for (op = environ; *op; ++op)
2075
free((char *)NewEnv);
2076
NewEnv = np = (char **) malloc((unsigned) (op - environ + 7 + 1) * sizeof(char **));
2078
Panic(0, "%s", strnomem);
2079
sprintf(stybuf, "STY=%s", strlen(SockName) <= MAXSTR - 5 ? SockName : "?");
2080
*np++ = stybuf; /* NewEnv[0] */
2081
*np++ = Term; /* NewEnv[1] */
2082
np++; /* room for SHELL */
2084
np += 2; /* room for TERMCAP and WINDOW */
2086
np += 4; /* room for TERMCAP WINDOW LINES COLUMNS */
2089
for (op = environ; *op; ++op)
2091
if (!IsSymbol(*op, "TERM") && !IsSymbol(*op, "TERMCAP")
2092
&& !IsSymbol(*op, "STY") && !IsSymbol(*op, "WINDOW")
2093
&& !IsSymbol(*op, "SCREENCAP") && !IsSymbol(*op, "SHELL")
2094
&& !IsSymbol(*op, "LINES") && !IsSymbol(*op, "COLUMNS")
2101
#if defined(USEVARARGS) && defined(__STDC__)
2102
#define DEFINE_VARARGS_FN(fnname) void fnname (int err, const char *fmt, VA_DOTS)
2104
#define DEFINE_VARARGS_FN(fnname) void fnname(err, fmt, VA_DOTS) \
2110
#define PROCESS_MESSAGE(B) do { \
2113
VA_START(ap, fmt); \
2115
(void)vsnprintf(p, sizeof(B) - 100, fmt, VA_ARGS(ap)); \
2122
strncpy(p, strerror(err), B + sizeof(B) - p - 1); \
2123
B[sizeof(B) - 1] = 0; \
2127
DEFINE_VARARGS_FN(Msg)
2129
char buf[MAXPATHLEN*2];
2130
PROCESS_MESSAGE(buf);
2132
debug2("Msg('%s') (%#x);\n", buf, (unsigned int)display);
2134
if (display && displays)
2138
for (display = displays; display; display = display->d_next)
2143
/* no displays but a display - must have forked.
2144
* send message to backend!
2146
char *tty = D_usertty;
2147
struct display *olddisplay = display;
2148
display = 0; /* only send once */
2149
SendErrorMsg(tty, buf);
2150
display = olddisplay;
2153
printf("%s\r\n", buf);
2156
write(queryflag, buf, strlen(buf));
2160
* Call FinitTerm for all displays, write a message to each and call eexit();
2162
DEFINE_VARARGS_FN(Panic)
2164
char buf[MAXPATHLEN*2];
2165
PROCESS_MESSAGE(buf);
2167
debug3("Panic('%s'); display=%x displays=%x\n", buf, display, displays);
2168
if (displays == 0 && display == 0)
2170
printf("%s\r\n", buf);
2172
Kill(PanicPid, SIG_BYE);
2174
else if (displays == 0)
2176
/* no displays but a display - must have forked.
2177
* send message to backend!
2179
char *tty = D_usertty;
2181
SendErrorMsg(tty, buf);
2186
for (display = displays; display; display = display->d_next)
2195
SetTTY(D_userfd, &D_OldMode);
2196
fcntl(D_userfd, F_SETFL, 0);
2197
write(D_userfd, buf, strlen(buf));
2198
write(D_userfd, "\n", 1);
2201
Kill(D_userpid, SIG_BYE);
2204
if (tty_oldmode >= 0)
2207
if (setuid(own_uid))
2208
xseteuid(own_uid); /* may be a loop. sigh. */
2212
debug1("Panic: changing back modes from %s\n", attach_tty);
2213
chmod(attach_tty, tty_oldmode);
2219
DEFINE_VARARGS_FN(QueryMsg)
2221
char buf[MAXPATHLEN*2];
2226
PROCESS_MESSAGE(buf);
2227
write(queryflag, buf, strlen(buf));
2230
DEFINE_VARARGS_FN(Dummy)
2233
#undef PROCESS_MESSAGE
2234
#undef DEFINE_VARARGS_FN
2237
* '^' is allowed as an escape mechanism for control characters. jw.
2239
* Added time insertion using ideas/code from /\ndy Jones
2240
* (andy@lingua.cltr.uq.OZ.AU) - thanks a lot!
2245
static const char days[] = "SunMonTueWedThuFriSat";
2246
static const char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
2249
static char winmsg_buf[MAXSTR];
2250
#define MAX_WINMSG_REND 256 /* rendition changes */
2251
static int winmsg_rend[MAX_WINMSG_REND];
2252
static int winmsg_rendpos[MAX_WINMSG_REND];
2253
static int winmsg_numrend;
2256
pad_expand(buf, p, numpad, padlen)
2265
padlen = padlen - (p - buf); /* space for rent */
2268
pn2 = pn = p + padlen;
2272
if (r && *p != 127 && p - buf == winmsg_rendpos[r - 1])
2274
winmsg_rendpos[--r] = pn - buf;
2281
i = numpad > 0 ? (padlen + numpad - 1) / numpad : 0;
2286
if (r && p - buf == winmsg_rendpos[r - 1])
2287
winmsg_rendpos[--r] = pn - buf;
2294
struct backtick *next;
2299
char result[MAXSTR];
2306
struct backtick *backticks;
2310
struct backtick *bt;
2315
for (p = q = bt->result; (c = (unsigned char)*p++) != 0;)
2319
if (c >= ' ' || c == '\005')
2326
backtick_fn(ev, data)
2330
struct backtick *bt;
2333
bt = (struct backtick *)data;
2334
debug1("backtick_fn for #%d\n", bt->num);
2336
l = read(ev->fd, bt->buf + i, MAXSTR - i);
2339
debug1("EOF on backtick #%d\n", bt->num);
2345
debug1("read %d bytes\n", l);
2347
for (j = 0; j < l; j++)
2348
if (bt->buf[i - j - 1] == '\n')
2352
for (k = i - j - 2; k >= 0; k--)
2353
if (bt->buf[k] == '\n')
2356
bcopy(bt->buf + k, bt->result, i - j - k);
2357
bt->result[i - j - k - 1] = 0;
2358
backtick_filter(bt);
2359
WindowChanged(0, '`');
2361
if (j == l && i == MAXSTR)
2369
bcopy(bt->buf + i - j, bt->buf, j);
2376
setbacktick(num, lifespan, tick, cmdv)
2382
struct backtick **btp, *bt;
2385
debug1("setbacktick called for backtick #%d\n", num);
2386
for (btp = &backticks; (bt = *btp) != 0; btp = &bt->next)
2393
for (v = bt->cmdv; *v; v++)
2410
bt = (struct backtick *)malloc(sizeof *bt);
2413
Msg(0, "%s", strnomem);
2416
bzero(bt, sizeof(*bt));
2422
bt->lifespan = lifespan;
2429
if (bt->tick == 0 && bt->lifespan == 0)
2431
debug("setbacktick: continuous mode\n");
2432
bt->buf = (char *)malloc(MAXSTR);
2435
Msg(0, "%s", strnomem);
2436
setbacktick(num, 0, 0, (char **)0);
2439
bt->ev.type = EV_READ;
2440
bt->ev.fd = readpipe(bt->cmdv);
2441
bt->ev.handler = backtick_fn;
2442
bt->ev.data = (char *)bt;
2449
runbacktick(bt, tickp, now)
2450
struct backtick *bt;
2457
debug1("runbacktick called for backtick #%d\n", bt->num);
2458
if (bt->tick && (!*tickp || bt->tick < *tickp))
2460
if ((bt->lifespan == 0 && bt->tick == 0) || now < bt->bestbefore)
2462
debug1("returning old result (%d)\n", bt->lifespan);
2465
f = readpipe(bt->cmdv);
2469
while ((l = read(f, bt->result + i, sizeof(bt->result) - i)) > 0)
2471
debug1("runbacktick: read %d bytes\n", l);
2473
for (j = 1; j < l; j++)
2474
if (bt->result[i - j - 1] == '\n')
2476
if (j == l && i == sizeof(bt->result))
2478
j = sizeof(bt->result) / 2;
2483
bcopy(bt->result + i - j, bt->result, j);
2488
bt->result[sizeof(bt->result) - 1] = '\n';
2489
if (i && bt->result[i - 1] == '\n')
2491
debug1("runbacktick: finished, %d bytes\n", i);
2493
backtick_filter(bt);
2495
bt->bestbefore = now2 + bt->lifespan;
2500
AddWinMsgRend(str, r)
2504
if (winmsg_numrend >= MAX_WINMSG_REND || str < winmsg_buf ||
2505
str >= winmsg_buf + MAXSTR)
2508
winmsg_rend[winmsg_numrend] = r;
2509
winmsg_rendpos[winmsg_numrend] = str - winmsg_buf;
2516
MakeWinMsgEv(str, win, esc, padlen, ev, rec)
2526
register char *p = winmsg_buf;
2536
int qmflag = 0, omflag = 0, qmnumrend = 0;
2543
struct backtick *bt = NULL;
2545
if (winmsg_numrend >= 0)
2548
winmsg_numrend = -winmsg_numrend;
2553
gettimeofday(&now, NULL);
2554
for (; *s && (l = winmsg_buf + MAXSTR - 1 - p) > 0; s++, p++)
2560
if (*s != '^' && *s >= 64)
2585
if (*++s == esc) /* double escape ? */
2587
if ((plusflg = *s == '+') != 0)
2589
if ((minusflg = *s == '-') != 0)
2591
if ((zeroflg = *s == '0') != 0)
2594
while(*s >= '0' && *s <= '9')
2595
num = num * 10 + (*s++ - '0');
2596
if ((longflg = *s == 'L') != 0)
2604
if ((!qmflag && !omflag) || omflag == 1)
2607
if (qmnumrend < winmsg_numrend)
2608
winmsg_numrend = qmnumrend;
2614
qmnumrend = winmsg_numrend;
2615
qmflag = omflag = 0;
2621
if (qmflag && omflag != 1)
2625
qmnumrend = winmsg_numrend;
2630
if (qmnumrend < winmsg_numrend)
2631
winmsg_numrend = qmnumrend;
2635
case 'd': case 'D': case 'm': case 'M': case 'y': case 'Y':
2636
case 'a': case 'A': case 's': case 'c': case 'C':
2641
time_t nowsec = now.tv_sec;
2642
tm = localtime(&nowsec);
2645
if (!tick || tick > 3600)
2650
sprintf(p, "%02d", tm->tm_mday % 100);
2654
strftime(p, l, (longflg ? "%A" : "%a"), tm);
2656
sprintf(p, "%3.3s", days + 3 * tm->tm_wday);
2660
sprintf(p, "%02d", tm->tm_mon + 1);
2664
strftime(p, l, (longflg ? "%B" : "%b"), tm);
2666
sprintf(p, "%3.3s", months + 3 * tm->tm_mon);
2670
sprintf(p, "%02d", tm->tm_year % 100);
2673
sprintf(p, "%04d", tm->tm_year + 1900);
2676
sprintf(p, tm->tm_hour >= 12 ? "pm" : "am");
2679
sprintf(p, tm->tm_hour >= 12 ? "PM" : "AM");
2682
sprintf(p, "%02d", tm->tm_sec);
2686
sprintf(p, zeroflg ? "%02d:%02d" : "%2d:%02d", tm->tm_hour, tm->tm_min);
2687
if (!tick || tick > 60)
2691
sprintf(p, zeroflg ? "%02d:%02d" : "%2d:%02d", (tm->tm_hour + 11) % 12 + 1, tm->tm_min);
2692
if (!tick || tick > 60)
2712
if (!tick || tick > 60)
2721
if (rec >= 10 || (*s == 'h' && (win == 0 || win->w_hstatus == 0 || *win->w_hstatus == 0)))
2728
for (bt = backticks; bt; bt = bt->next)
2738
char savebuf[sizeof(winmsg_buf)];
2740
int oldnumrend = winmsg_numrend;
2743
strcpy(savebuf, winmsg_buf);
2744
winmsg_numrend = -winmsg_numrend;
2745
MakeWinMsgEv(*s == 'h' ? win->w_hstatus : runbacktick(bt, &oldtick, now.tv_sec), win, '\005', 0, (struct event *)0, rec + 1);
2746
debug2("oldtick=%d tick=%d\n", oldtick, tick);
2747
if (!tick || oldtick < tick)
2749
if ((int)strlen(winmsg_buf) < l)
2750
strcat(savebuf, winmsg_buf);
2751
strcpy(winmsg_buf, savebuf);
2752
while (oldnumrend < winmsg_numrend)
2753
winmsg_rendpos[oldnumrend++] += p - winmsg_buf;
2762
struct win *oldfore = 0;
2770
ss = AddWindows(p, l - 1, (*s == 'w' ? 0 : 1) | (longflg ? 0 : 2) | (plusflg ? 4 : 0) | (minusflg ? 8 : 0), win ? win->w_number : -1);
2781
AddOtherUsers(p, l - 1, win);
2789
AddWindowFlags(p, l - 1, win);
2796
if (win && (int)strlen(win->w_title) < l)
2798
strcpy(p, win->w_title);
2807
D_encoding = nwin_options.encoding > 0 ? nwin_options.encoding : 0;
2808
if (win && win->w_encoding)
2811
strcpy(p, EncodingName(win->w_encoding));
2820
for (i = 0; i < 127; i++)
2821
if (s[i] && s[i] != '}')
2825
if (s[i] == '}' && winmsg_numrend < MAX_WINMSG_REND)
2829
debug1("MakeWinMsg attrcolor %s\n", rbuf);
2830
if (i != 1 || rbuf[0] != '-')
2831
r = ParseAttrColor(rbuf, (char *)0, 0);
2832
if (r != -1 || (i == 1 && rbuf[0] == '-'))
2834
winmsg_rend[winmsg_numrend] = r;
2835
winmsg_rendpos[winmsg_numrend] = p - winmsg_buf;
2845
if ((int)strlen(HostName) < l)
2847
strcpy(p, HostName);
2857
session_name = strchr(SockName, '.') + 1;
2858
if ((int)strlen(session_name) < l)
2860
strcpy(p, session_name);
2869
sprintf(p, "%d", (plusflg && display) ? D_userpid : getpid());
2876
if (display && ((ev && ev == &D_forecv->c_captev) || (!ev && win && win == D_fore)))
2877
minusflg = !minusflg;
2884
if (display && ev && ev != &D_hstatusev) /* Hack */
2886
/* Is the layer in the current canvas in copy mode? */
2887
struct canvas *cv = (struct canvas *)ev->data;
2888
if (ev == &cv->c_captev && cv->c_layer->l_layfn == &MarkLf)
2895
if (display && D_ESCseen)
2899
truncpos = p - winmsg_buf;
2900
truncper = num > 100 ? 100 : num;
2901
trunclong = longflg;
2907
if (num || zeroflg || plusflg || longflg || (*s != '='))
2909
/* expand all pads */
2912
num = (plusflg ? lastpad : padlen) - num;
2913
if (!plusflg && padlen == 0)
2914
num = p - winmsg_buf;
2919
if (*s != '=' && num == 0 && !plusflg)
2924
num = p - winmsg_buf;
2926
num = (padlen - (plusflg ? lastpad : 0)) * num / 100;
2932
if (num > MAXSTR - 1)
2935
p = pad_expand(winmsg_buf, p, numpad, num);
2937
if (p - winmsg_buf > num && !longflg)
2946
trunc = lastpad + truncper * (num - lastpad) / 100;
2949
if (trunc < lastpad)
2951
left = truncpos - trunc;
2952
if (left > p - winmsg_buf - num)
2953
left = p - winmsg_buf - num;
2954
debug1("lastpad = %d, ", lastpad);
2955
debug3("truncpos = %d, trunc = %d, left = %d\n", truncpos, trunc, left);
2958
if (left + lastpad > p - winmsg_buf)
2959
left = p - winmsg_buf - lastpad;
2960
if (p - winmsg_buf - lastpad - left > 0)
2961
bcopy(winmsg_buf + lastpad + left, winmsg_buf + lastpad, p - winmsg_buf - lastpad - left);
2964
while (r && winmsg_rendpos[r - 1] > lastpad)
2967
winmsg_rendpos[r] -= left;
2968
if (winmsg_rendpos[r] < lastpad)
2969
winmsg_rendpos[r] = lastpad;
2973
if (p - winmsg_buf > lastpad)
2974
winmsg_buf[lastpad] = '.';
2975
if (p - winmsg_buf > lastpad + 1)
2976
winmsg_buf[lastpad + 1] = '.';
2977
if (p - winmsg_buf > lastpad + 2)
2978
winmsg_buf[lastpad + 2] = '.';
2981
if (p - winmsg_buf > num)
2983
p = winmsg_buf + num;
2986
if (num - 1 >= lastpad)
2988
if (num - 2 >= lastpad)
2990
if (num - 3 >= lastpad)
2994
while (r && winmsg_rendpos[r - 1] > num)
2995
winmsg_rendpos[--r] = num;
2999
if (lastpad > p - winmsg_buf)
3000
lastpad = p - winmsg_buf;
3001
debug1("lastpad now %d\n", lastpad);
3005
while (p - winmsg_buf < num)
3007
lastpad = p - winmsg_buf;
3010
debug1("lastpad2 now %d\n", lastpad);
3016
*p = 127; /* internal pad representation */
3030
sprintf(p, "%*s", num, num > 1 ? "--" : "-");
3032
sprintf(p, "%*d", num, win->w_number);
3039
if (qmpos && !qmflag)
3044
if (padlen > MAXSTR - 1)
3045
padlen = MAXSTR - 1;
3046
p = pad_expand(winmsg_buf, p, numpad, padlen);
3050
evdeq(ev); /* just in case */
3051
ev->timeout.tv_sec = 0;
3052
ev->timeout.tv_usec = 0;
3056
now.tv_usec = 100000;
3060
now.tv_sec += tick - (now.tv_sec % tick);
3062
debug2("NEW timeout %d %d\n", ev->timeout.tv_sec, tick);
3068
MakeWinMsg(s, win, esc)
3073
return MakeWinMsgEv(s, win, esc, 0, (struct event *)0, 0);
3077
PutWinMsg(s, start, max)
3083
struct mchar rendstack[MAX_WINMSG_REND];
3086
if (s != winmsg_buf)
3088
/* sorry, no fancy coloring available */
3089
debug1("PutWinMsg %s plain\n", s);
3102
debug2("PutWinMsg %s start attr %x\n", s, rend.attr);
3103
for (i = 0; i < winmsg_numrend && max > 0; i++)
3105
if (p > winmsg_rendpos[i] || winmsg_rendpos[i] > l)
3107
if (p < winmsg_rendpos[i])
3109
n = winmsg_rendpos[i] - p;
3126
rend = rendstack[--rendstackn];
3130
rendstack[rendstackn++] = rend;
3131
ApplyAttrColor(r, &rend);
3133
SetRendition(&rend);
3161
if ((j = open("/dev/null", 0)) >= 0)
3168
while (dup(++i) < 0 && errno != EBADF)
3170
debug1(" [%d]\n", i);
3183
serv_read_fn(ev, data)
3187
debug("Knock - knock!\n");
3192
serv_select_fn(ev, data)
3198
debug("serv_select_fn called\n");
3199
/* XXX: messages?? */
3204
if (InterruptPlease)
3206
debug("Backend received interrupt\n");
3207
/* This approach is rather questionable in a multi-display
3209
if (fore && displays)
3211
#if defined(TERMIO) || defined(POSIX)
3212
char ibuf = displays->d_OldMode.tio.c_cc[VINTR];
3214
char ibuf = displays->d_OldMode.m_tchars.t_intrc;
3217
write(W_UWP(fore) ? fore->w_pwin->p_ptyfd : fore->w_ptyfd,
3219
debug1("Backend wrote interrupt to %d", fore->w_number);
3220
debug1("%s\n", W_UWP(fore) ? " (pseudowin)" : "");
3222
write(fore->w_ptyfd, &ibuf, 1);
3223
debug1("Backend wrote interrupt to %d\n", fore->w_number);
3226
InterruptPlease = 0;
3229
for (p = windows; p; p = p->w_next)
3231
if (p->w_bell == BELL_FOUND || p->w_bell == BELL_VISUAL)
3234
int visual = p->w_bell == BELL_VISUAL || visual_bell;
3235
p->w_bell = BELL_ON;
3236
for (display = displays; display; display = display->d_next)
3238
for (cv = D_cvlist; cv; cv = cv->c_next)
3239
if (cv->c_layer->l_bottom == &p->w_layer)
3243
p->w_bell = BELL_DONE;
3244
Msg(0, "%s", MakeWinMsg(BellString, p, '%'));
3246
else if (visual && !D_VB && (!D_status || !D_status_bell))
3248
Msg(0, "%s", VisualBellString);
3252
debug1("using vbell timeout %d\n", VBellWait);
3253
SetTimeout(&D_statusev, VBellWait );
3257
/* don't annoy the user with two messages */
3258
if (p->w_monitor == MON_FOUND)
3259
p->w_monitor = MON_DONE;
3260
WindowChanged(p, 'f');
3262
if (p->w_monitor == MON_FOUND)
3265
p->w_monitor = MON_ON;
3266
for (display = displays; display; display = display->d_next)
3268
for (cv = D_cvlist; cv; cv = cv->c_next)
3269
if (cv->c_layer->l_bottom == &p->w_layer)
3272
continue; /* user already sees window */
3274
if (!(ACLBYTE(p->w_mon_notify, D_user->u_id) & ACLBIT(D_user->u_id)))
3275
continue; /* user doesn't care */
3277
Msg(0, "%s", MakeWinMsg(ActivityString, p, '%'));
3278
p->w_monitor = MON_DONE;
3280
WindowChanged(p, 'f');
3282
if (p->w_silence == SILENCE_FOUND)
3284
/* Unset the flag if the user switched to this window. */
3285
if (p->w_layer.l_cvlist)
3287
p->w_silence = SILENCE_ON;
3288
WindowChanged(p, 'f');
3293
for (display = displays; display; display = display->d_next)
3296
if (D_status == STATUS_ON_WIN)
3298
/* XXX: should use display functions! */
3299
for (cv = D_cvlist; cv; cv = cv->c_next)
3303
/* normalize window, see resize.c */
3304
lx = cv->c_layer->l_x;
3305
ly = cv->c_layer->l_y;
3306
if (lx == cv->c_layer->l_width)
3308
if (ly + cv->c_yoff < cv->c_ys)
3310
int i, n = cv->c_ys - (ly + cv->c_yoff);
3311
cv->c_yoff = cv->c_ys - ly;
3312
RethinkViewportOffsets(cv);
3313
if (n > cv->c_layer->l_height)
3314
n = cv->c_layer->l_height;
3316
LScrollV(flayer, -n, 0, flayer->l_height - 1, 0);
3317
LayRedisplayLine(-1, -1, -1, 1);
3318
for (i = 0; i < n; i++)
3319
LayRedisplayLine(i, 0, flayer->l_width - 1, 1);
3320
if (cv == cv->c_display->d_forecv)
3324
else if (ly + cv->c_yoff > cv->c_ye)
3326
int i, n = ly + cv->c_yoff - cv->c_ye;
3327
cv->c_yoff = cv->c_ye - ly;
3328
RethinkViewportOffsets(cv);
3329
if (n > cv->c_layer->l_height)
3330
n = cv->c_layer->l_height;
3332
LScrollV(flayer, n, 0, cv->c_layer->l_height - 1, 0);
3333
LayRedisplayLine(-1, -1, -1, 1);
3334
for (i = 0; i < n; i++)
3335
LayRedisplayLine(i + flayer->l_height - n, 0, flayer->l_width - 1, 1);
3336
if (cv == cv->c_display->d_forecv)
3340
if (lx + cv->c_xoff < cv->c_xs)
3342
int i, n = cv->c_xs - (lx + cv->c_xoff);
3343
if (n < (cv->c_xe - cv->c_xs + 1) / 2)
3344
n = (cv->c_xe - cv->c_xs + 1) / 2;
3345
if (cv->c_xoff + n > cv->c_xs)
3346
n = cv->c_xs - cv->c_xoff;
3348
RethinkViewportOffsets(cv);
3349
if (n > cv->c_layer->l_width)
3350
n = cv->c_layer->l_width;
3352
LayRedisplayLine(-1, -1, -1, 1);
3353
for (i = 0; i < flayer->l_height; i++)
3355
LScrollH(flayer, -n, i, 0, flayer->l_width - 1, 0, 0);
3356
LayRedisplayLine(i, 0, n - 1, 1);
3358
if (cv == cv->c_display->d_forecv)
3362
else if (lx + cv->c_xoff > cv->c_xe)
3364
int i, n = lx + cv->c_xoff - cv->c_xe;
3365
if (n < (cv->c_xe - cv->c_xs + 1) / 2)
3366
n = (cv->c_xe - cv->c_xs + 1) / 2;
3367
if (cv->c_xoff - n + cv->c_layer->l_width - 1 < cv->c_xe)
3368
n = cv->c_xoff + cv->c_layer->l_width - 1 - cv->c_xe;
3370
RethinkViewportOffsets(cv);
3371
if (n > cv->c_layer->l_width)
3372
n = cv->c_layer->l_width;
3374
LayRedisplayLine(-1, -1, -1, 1);
3375
for (i = 0; i < flayer->l_height; i++)
3377
LScrollH(flayer, n, i, 0, flayer->l_width - 1, 0, 0);
3378
LayRedisplayLine(i, flayer->l_width - n, flayer->l_width - 1, 1);
3380
if (cv == cv->c_display->d_forecv)
3387
for (display = displays; display; display = display->d_next)
3389
if (D_status == STATUS_ON_WIN || D_cvlist == 0 || D_cvlist->c_next == 0)
3391
debug1("serv_select_fn: Restore on cv %#x\n", (int)D_forecv);
3392
CV_CALL(D_forecv, LayRestore();LaySetCursor());
3397
logflush_fn(ev, data)
3405
if (!islogfile(NULL))
3406
return; /* no more logfiles */
3408
n = log_flush ? log_flush : (logtstamp_after + 4) / 5;
3411
SetTimeout(ev, n * 1000);
3412
evenq(ev); /* re-enqueue ourself */
3416
/* write fancy time-stamp */
3417
for (p = windows; p; p = p->w_next)
3421
p->w_logsilence += n;
3422
if (p->w_logsilence < logtstamp_after)
3424
if (p->w_logsilence - n >= logtstamp_after)
3426
buf = MakeWinMsg(logtstamp_string, p, '%');
3427
logfwrite(p->w_log, buf, strlen(buf));
3432
* Interprets ^?, ^@ and other ^-control-char notation.
3433
* Interprets \ddd octal notation
3435
* The result is placed in *cp, p is advanced behind the parsed expression and
3444
if (*p == '^' && p[1])
3454
else if (*p == '\\' && *++p <= '7' && *p >= '0')
3458
*cp = *cp * 8 + *p - '0';
3459
while (*++p <= '7' && *p >= '0');
3470
unsigned char buf[2];
3473
SetEscape((struct acluser *)0, -1, -1);
3476
if ((p = ParseChar(p, (char *)buf)) == NULL ||
3477
(p = ParseChar(p, (char *)buf+1)) == NULL || *p)
3479
SetEscape((struct acluser *)0, buf[0], buf[1]);