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;
259
char strnomem[] = "Out of memory.";
262
static int InterruptPlease;
263
static int GotSigChld;
266
lf_secreopen(name, wantfd, l)
274
if (((got_fd = secopen(name, O_WRONLY | O_CREAT | O_APPEND, 0666)) < 0) ||
275
lf_move_fd(got_fd, wantfd) < 0)
278
debug1("lf_secreopen: failed for %s\n", name);
281
l->st->st_ino = l->st->st_dev = 0;
282
debug2("lf_secreopen: %d = %s\n", wantfd, name);
286
/********************************************************************/
287
/********************************************************************/
288
/********************************************************************/
291
static struct passwd *
292
getpwbyname(name, ppp)
298
struct spwd *sss = NULL;
299
static char *spw = NULL;
302
if (!ppp && !(ppp = getpwnam(name)))
305
/* Do password sanity check..., allow ##user for SUN_C2 security */
310
if (ppp->pw_passwd[0] == '#' && ppp->pw_passwd[1] == '#' &&
311
strcmp(ppp->pw_passwd + 2, ppp->pw_name) == 0)
315
char c = ppp->pw_passwd[n];
316
if (!(c == '.' || c == '/' || c == '$' ||
317
(c >= '0' && c <= '9') ||
318
(c >= 'a' && c <= 'z') ||
319
(c >= 'A' && c <= 'Z')))
324
/* try to determine real password */
325
if (n < 13 && sss == 0)
327
sss = getspnam(ppp->pw_name);
332
ppp->pw_passwd = spw = SaveStr(sss->sp_pwdp);
333
endspent(); /* this should delete all buffers ... */
336
endspent(); /* this should delete all buffers ... */
342
if (ppp->pw_passwd && strlen(ppp->pw_passwd) == 13 + 11)
343
ppp->pw_passwd[13] = 0; /* beware of linux's long passwords */
356
s = getenv("LC_ALL");
358
s = getenv("LC_CTYPE");
373
char socknamebuf[2 * MAXSTR];
375
char *myname = (ac == 0) ? "screen" : av[0];
378
#ifdef _MODE_T /* (jw) */
383
#if defined(SYSV) && !defined(ISC)
384
struct utsname utsnam;
386
struct NewWindow nwin;
387
int detached = 0; /* start up detached */
393
#if (defined(AUX) || defined(_AUX_SOURCE)) && defined(POSIX)
394
setcompat(COMPAT_POSIX|COMPAT_BSDPROT); /* turn on seteuid support */
396
#if defined(sun) && defined(SVR4)
398
/* Solaris' login blocks SIGHUP! This is _very bad_ */
401
sigprocmask(SIG_SETMASK, &sset, 0);
406
* First, close all unused descriptors
407
* (otherwise, we might have problems with the select() call)
413
snprintf(version, 59, "%d.%.2d.%.2d%s (%s%s) %s", REV, VERS,
414
PATCHLEVEL, STATE, ORIGIN, GIT_REV, DATE);
415
nversion = REV * 10000 + VERS * 100 + PATCHLEVEL;
416
debug2("-- screen debug started %s (%s)\n", *av, version);
430
debug("NAMEDPIPE\n");
432
#if defined(SIGWINCH) && defined(TIOCGWINSZ)
433
debug("Window size changing enabled\n");
463
debug1("NAME_MAX = %d\n", NAME_MAX);
466
BellString = SaveStr("Bell in window %n");
467
VisualBellString = SaveStr(" Wuff, Wuff!! ");
468
ActivityString = SaveStr("Activity in window %n");
469
screenlogfile = SaveStr("screenlog.%n");
470
logtstamp_string = SaveStr("-- %n:%t -- time-stamp -- %M/%d/%y %c:%s --\n");
471
hstatusstring = SaveStr("%h");
472
captionstring = SaveStr("%4n %t");
473
timestring = SaveStr("%c:%s %M %d %H%? %l%?");
474
wlisttit = SaveStr(" Num Name%=Flags");
475
wliststr = SaveStr("%4n %t%=%f");
477
BufferFile = SaveStr(DEFAULT_BUFFERFILE);
483
default_startup = (ac > 1) ? 0 : 1;
485
VBellWait = VBELLWAIT * 1000;
486
MsgWait = MSGWAIT * 1000;
487
MsgMinWait = MSGMINWAIT * 1000;
488
SilenceWait = SILENCEWAIT;
493
zmodem_sendcmd = SaveStr("!!! sz -vv -b ");
494
zmodem_recvcmd = SaveStr("!!! rz -vv -b -E");
498
CompileKeys((char *)0, 0, mark_key_tab);
502
screenencodings = SaveStr(SCREENENCODINGS);
508
nwin_options = nwin_undef;
509
strcpy(screenterm, "screen");
511
logreopen_register(lf_secreopen);
514
/* if this is a login screen, assume -RR */
523
ShellProg = SaveStr(DefaultShell); /* to prevent nasty circles */
528
if (--ac > 0 && *ap == '-')
530
if (ap[1] == '-' && ap[2] == 0)
536
if (ap[1] == '-' && !strcmp(ap, "--version"))
537
Panic(0, "Screen version %s", version);
538
if (ap[1] == '-' && !strcmp(ap, "--help"))
539
exit_with_usage(myname, NULL, NULL);
540
while (ap && *ap && *++ap)
545
nwin_options.aflag = 1;
550
case 'p': /* preselect */
556
exit_with_usage(myname, "Specify a window to preselect with -p", NULL);
563
bd.bd_start_braille = 1;
572
exit_with_usage(myname, "Specify an alternate rc-filename with -c", NULL);
581
exit_with_usage(myname, "Specify command characters with -e", NULL);
585
Panic(0, "Two characters are required with -e option, not '%s'.", ap);
594
nwin_options.flowflag = FLOW_NOW * 0;
601
nwin_options.flowflag = FLOW_NOW * 1;
604
nwin_options.flowflag = FLOW_AUTOFLAG;
607
exit_with_usage(myname, "Unknown flow option -%s", --ap);
612
exit_with_usage(myname, NULL, NULL);
613
nwin_options.histheight = atoi(*++av);
614
if (nwin_options.histheight < 0)
615
exit_with_usage(myname, "-h: %s: negative scrollback size?", *av);
620
case 't': /* title, the former AkA == -k */
622
exit_with_usage(myname, "Specify a new window-name with -t", NULL);
623
nwin_options.aka = *++av;
631
nwin_options.lflag = 0;
638
nwin_options.lflag = 1;
641
nwin_options.lflag = 3;
644
case 'i': /* -list */
646
if (ac > 1 && !SockMatch)
654
exit_with_usage(myname, "%s: Unknown suboption to -l", --ap);
658
if (strcmp(ap+1, "ipe"))
659
exit_with_usage(myname, "Unknown option %s", --ap);
662
if (ac > 1 && !SockMatch)
669
nwin_options.Lflag = 1;
674
case 'O': /* to be (or not to be?) deleted. jw. */
679
exit_with_usage(myname, "Specify terminal-type with -T", NULL);
680
if (strlen(*++av) < 20)
681
strcpy(screenterm, *av);
683
Panic(0, "-T: terminal name too long. (max. 20 char)");
684
nwin_options.term = screenterm;
698
if (ac > 1 && *av[1] != '-' && !SockMatch)
702
debug2("rflag=%d, SockMatch=%s\n", dflag, SockMatch);
710
rflag += (*ap == 'R') ? 2 : 1;
721
if (*av[1] != '-' && !SockMatch)
725
debug2("dflag=%d, SockMatch=%s\n", dflag, SockMatch);
732
exit_with_usage(myname, "Specify shell with -s", NULL);
735
ShellProg = SaveStr(*++av);
736
debug1("ShellProg: '%s'\n", ShellProg);
742
exit_with_usage(myname, "Specify session-name with -S", NULL);
746
exit_with_usage(myname, "Empty session-name?", NULL);
752
Panic(0, "Screen version %s", version);
756
nwin_options.encoding = nwin_options.encoding == -1 ? UTF8 : 0;
760
exit_with_usage(myname, "Unknown option %s", --ap);
773
#ifdef SIGBUS /* OOPS, linux has no bus errors! */
774
signal(SIGBUS, CoreDump);
776
signal(SIGSEGV, CoreDump);
780
setlocale(LC_ALL, "");
783
if (nwin_options.encoding == -1)
785
/* ask locale if we should start in UTF-8 mode */
786
# ifdef HAVE_NL_LANGINFO
788
setlocale(LC_CTYPE, "");
790
nwin_options.encoding = FindEncoding(nl_langinfo(CODESET));
791
debug1("locale says encoding = %d\n", nwin_options.encoding);
795
if ((s = locale_name()) && InStr(s, "UTF-8"))
796
nwin_options.encoding = UTF8;
798
debug1("environment says encoding=%d\n", nwin_options.encoding);
804
if ((s = locale_name()))
806
if(!strncmp(s, "zh_", 3) || !strncmp(s, "ja_", 3) || !strncmp(s, "ko_", 3))
814
if (nwin_options.aka)
817
if (nwin_options.encoding > 0)
819
size_t len = strlen(nwin_options.aka);
821
char *newbuf = malloc(3 * len);
823
Panic(0, "%s", strnomem);
824
newsz = RecodeBuf((unsigned char *)nwin_options.aka, len,
825
nwin_options.encoding, 0, (unsigned char *)newbuf);
826
newbuf[newsz] = '\0';
827
nwin_options.aka = newbuf;
832
/* If we just use the original value from av,
833
subsequent shelltitle invocations will attempt to free
834
space we don't own... */
835
nwin_options.aka = SaveStr(nwin_options.aka);
839
if (SockMatch && strlen(SockMatch) >= MAXSTR)
840
Panic(0, "Ridiculously long socketname - try again.");
841
if (cmdflag && !rflag && !dflag && !xflag)
843
if (!cmdflag && dflag && mflag && !(rflag || xflag))
847
nwin.encoding = nwin_undef.encoding; /* let screenrc overwrite it */
852
/* make the write() calls return -1 on all errors */
855
* Ronald F. Guilmette, Oct 29 '94, bug-gnu-utils@prep.ai.mit.edu:
856
* It appears that in System V Release 4, UNIX, if you are writing
857
* an output file and you exceed the currently set file size limit,
858
* you _don't_ just get the call to `write' returning with a
859
* failure code. Rather, you get a signal called `SIGXFSZ' which,
860
* if neither handled nor ignored, will cause your program to crash
863
signal(SIGXFSZ, SIG_IGN);
867
signal(SIGPIPE, SIG_IGN);
874
sh = getenv("SHELL");
875
ShellProg = SaveStr(sh ? sh : DefaultShell);
877
ShellArgs[0] = ShellProg;
878
home = getenv("HOME");
879
if (!mflag && !SockMatch)
882
if (sty && *sty == 0)
887
if (!(nethackflag = (getenv("NETHACKOPTIONS") != NULL)))
889
char nethackrc[MAXPATHLEN];
891
if (home && (strlen(home) < (MAXPATHLEN - 20)))
893
sprintf(nethackrc,"%s/.nethackrc", home);
894
nethackflag = !access(nethackrc, F_OK);
900
own_uid = multi_uid = real_uid;
901
if (SockMatch && (sockp = index(SockMatch, '/')))
905
SockMatch = sockp + 1;
909
if ((mppp = getpwnam(multi)) == (struct passwd *)0)
910
Panic(0, "Cannot identify account '%s'.", multi);
911
multi_uid = mppp->pw_uid;
912
multi_home = SaveStr(mppp->pw_dir);
913
if (strlen(multi_home) > MAXPATHLEN - 10)
914
Panic(0, "home directory path too long");
916
/* always fake multi attach mode */
923
/* Special case: effective user is multiuser. */
924
if (eff_uid && (multi_uid != eff_uid))
925
Panic(0, "Must run suid root for multiuser support.");
927
if (SockMatch && *SockMatch == 0)
929
#endif /* MULTIUSER */
931
if ((LoginName = getlogin()) && LoginName[0] != '\0')
933
if ((ppp = getpwnam(LoginName)) != (struct passwd *) 0)
934
if ((int)ppp->pw_uid != real_uid)
935
ppp = (struct passwd *) 0;
939
if ((ppp = getpwuid(real_uid)) == 0)
941
Panic(0, "getpwuid() can't identify your account!");
944
LoginName = ppp->pw_name;
946
LoginName = SaveStr(LoginName);
948
ppp = getpwbyname(LoginName, ppp);
950
#if !defined(SOCKDIR) && defined(MULTIUSER)
951
if (multi && !multiattach)
953
if (home && strcmp(home, ppp->pw_dir))
954
Panic(0, "$HOME must match passwd entry for multiuser screens.");
958
#define SET_GUID() do \
962
eff_uid = real_uid; \
963
eff_gid = real_gid; \
966
#define SET_TTYNAME(fatal) do \
968
if (!(attach_tty = ttyname(0))) \
971
Panic(0, "Must be connected to a terminal."); \
975
else if (stat(attach_tty, &st)) \
976
Panic(errno, "Cannot access '%s'", attach_tty); \
977
if (strlen(attach_tty) >= MAXPATHLEN) \
978
Panic(0, "TtyName too long - sorry."); \
981
if (home == 0 || *home == '\0')
983
if (strlen(LoginName) > 20)
984
Panic(0, "LoginName too long - sorry.");
986
if (multi && strlen(multi) > 20)
987
Panic(0, "Screen owner name too long - sorry.");
989
if (strlen(home) > MAXPATHLEN - 25)
990
Panic(0, "$HOME too long - sorry.");
993
if (!detached && !lsflag && !cmdflag && !(dflag && !mflag && !rflag && !xflag) && !(sty && !SockMatch && !mflag && !rflag && !xflag))
999
/* ttyname implies isatty */
1002
tty_mode = (int)st.st_mode & 0777;
1006
fl = fcntl(0, F_GETFL, 0);
1007
if (fl != -1 && (fl & (O_RDWR|O_RDONLY|O_WRONLY)) == O_RDWR)
1010
if (attach_fd == -1)
1012
if ((n = secopen(attach_tty, O_RDWR | O_NONBLOCK, 0)) < 0)
1013
Panic(0, "Cannot open your terminal '%s' - please check.", attach_tty);
1016
debug2("attach_tty is %s, attach_fd is %d\n", attach_tty, attach_fd);
1018
if ((attach_term = getenv("TERM")) == 0 || *attach_term == 0)
1019
Panic(0, "Please set a terminal type.");
1020
if (strlen(attach_term) > sizeof(D_termname) - 1)
1021
Panic(0, "$TERM too long - sorry.");
1022
GetTTY(0, &attach_Mode);
1023
#ifdef DEBUGGGGGGGGGGGGGGG
1024
DebugTTY(&attach_Mode);
1029
oumask = umask(0); /* well, unsigned never fails? jw. */
1031
if ((oumask = (int)umask(0)) == -1)
1032
Panic(errno, "Cannot change umask to zero");
1034
SockDir = getenv("SCREENDIR");
1037
if (strlen(SockDir) >= MAXPATHLEN - 1)
1038
Panic(0, "Ridiculously long $SCREENDIR - try again.");
1041
Panic(0, "No $SCREENDIR with multi screens, please.");
1048
sprintf(SockPath, "%s/.screen", multi_home);
1052
sprintf(SockPath, "%s/S-%s", SockDir, multi);
1061
sprintf(SockPath, "%s/.screen", home);
1067
if (access(SockDir, F_OK))
1069
debug1("SockDir '%s' missing ...\n", SockDir);
1070
if (UserContext() > 0)
1072
if (mkdir(SockDir, 0700))
1076
if (UserStatus() <= 0)
1077
Panic(0, "Cannot make directory '%s'.", SockDir);
1079
if (SockDir != SockPath)
1080
strcpy(SockPath, SockDir);
1086
if (stat(SockDir, &st))
1088
n = (eff_uid == 0 && (real_uid || eff_gid == real_gid)) ? 0755 :
1089
(eff_gid != real_gid) ? 0775 :
1095
if (mkdir(SockDir, n) == -1)
1096
Panic(errno, "Cannot make directory '%s'", SockDir);
1100
if (!S_ISDIR(st.st_mode))
1101
Panic(0, "'%s' must be a directory.", SockDir);
1102
if (eff_uid == 0 && real_uid && (int)st.st_uid != eff_uid)
1103
Panic(0, "Directory '%s' must be owned by root.", SockDir);
1104
n = (eff_uid == 0 && (real_uid || (st.st_mode & 0775) != 0775)) ? 0755 :
1105
(eff_gid == (int)st.st_gid && eff_gid != real_gid) ? 0775 :
1107
if (((int)st.st_mode & 0777) != n)
1108
Panic(0, "Directory '%s' must have mode %03o.", SockDir, n);
1110
sprintf(SockPath, "%s/S-%s", SockDir, LoginName);
1111
if (access(SockPath, F_OK))
1113
if (mkdir(SockPath, 0700) == -1)
1114
Panic(errno, "Cannot make directory '%s'", SockPath);
1115
(void) chown(SockPath, real_uid, real_gid);
1121
if (stat(SockPath, &st) == -1)
1122
Panic(errno, "Cannot access %s", SockPath);
1124
if (!S_ISDIR(st.st_mode))
1125
Panic(0, "%s is not a directory.", SockPath);
1129
if ((int)st.st_uid != multi_uid)
1130
Panic(0, "%s is not the owner of %s.", multi, SockPath);
1135
if ((int)st.st_uid != real_uid)
1136
Panic(0, "You are not the owner of %s.", SockPath);
1138
if ((st.st_mode & 0777) != 0700)
1139
Panic(0, "Directory %s must have mode 700.", SockPath);
1140
if (SockMatch && index(SockMatch, '/'))
1141
Panic(0, "Bad session name '%s'", SockMatch);
1142
SockName = SockPath + strlen(SockPath) + 1;
1144
(void) umask(oumask);
1145
debug2("SockPath: %s SockMatch: %s\n", SockPath, SockMatch ? SockMatch : "NULL");
1147
#if defined(SYSV) && !defined(ISC)
1148
if (uname(&utsnam) == -1)
1149
Panic(errno, "uname");
1150
strncpy(HostName, utsnam.nodename, sizeof(utsnam.nodename) < MAXSTR ? sizeof(utsnam.nodename) : MAXSTR - 1);
1151
HostName[sizeof(utsnam.nodename) < MAXSTR ? sizeof(utsnam.nodename) : MAXSTR - 1] = '\0';
1153
(void) gethostname(HostName, MAXSTR);
1154
HostName[MAXSTR - 1] = '\0';
1156
if ((ap = index(HostName, '.')) != NULL)
1165
real_uid = multi_uid;
1168
i = FindSocket((int *)NULL, &fo, &oth, SockMatch);
1173
exit(9 + (fo || oth ? 1 : 0) + fo);
1176
Panic(0, "No Sockets found in %s.\n", SockPath);
1177
Panic(0, "%d Socket%s in %s.\n", fo, fo > 1 ? "s" : "", SockPath);
1180
signal(SIG_BYE, AttacherFinit); /* prevent races */
1183
/* attach_tty is not mandatory */
1186
Panic(0, "Please specify a command.");
1188
SendCmdMessage(sty, SockMatch, av, queryflag >= 0);
1191
else if (rflag || xflag)
1193
debug("screen -r: - is there anybody out there?\n");
1194
if (Attach(MSG_ATTACH))
1201
Panic(0, "Can't create sessions of other users.");
1203
debug("screen -r: backend not responding -- still crying\n");
1205
else if (dflag && !mflag)
1209
Msg(0, "[%s %sdetached.]\n", SockName, (dflag > 1 ? "power " : ""));
1213
if (!SockMatch && !mflag && sty)
1215
/* attach_tty is not mandatory */
1218
nwin_options.args = av;
1219
SendCreateMsg(sty, &nwin);
1223
nwin_compose(&nwin_default, &nwin_options, &nwin_default);
1225
if (!detached || dflag != 2)
1233
Panic(errno, "fork");
1241
sprintf(socknamebuf, "%d.%s", MasterPid, SockMatch);
1243
sprintf(socknamebuf, "%d.%s.%s", MasterPid, stripdev(attach_tty), HostName);
1244
for (ap = socknamebuf; *ap; ap++)
1248
if (strlen(socknamebuf) > NAME_MAX)
1249
socknamebuf[NAME_MAX] = 0;
1251
sprintf(SockPath + strlen(SockPath), "/%s", socknamebuf);
1258
PanicPid = getppid();
1260
if (DefaultEsc == -1)
1261
DefaultEsc = Ctrl('a');
1262
if (DefaultMetaEsc == -1)
1263
DefaultMetaEsc = 'a';
1265
ap = av0 + strlen(av0) - 1;
1268
if (!strncmp("screen", ap, 6))
1270
strncpy(ap, "SCREEN", 6); /* name this process "SCREEN-BACKEND" */
1282
if (dfp && dfp != stderr)
1284
sprintf(buf, "%s/SCREEN.%d", DEBUGDIR, (int)getpid());
1285
if ((dfp = fopen(buf, "w")) == NULL)
1288
(void) chmod(buf, 0666);
1293
if (attach_fd == -1)
1295
if ((n = secopen(attach_tty, O_RDWR, 0)) < 0)
1296
Panic(0, "Cannot reopen '%s' - please check.", attach_tty);
1303
freopen("/dev/null", "r", stdin);
1304
freopen("/dev/null", "w", stdout);
1309
freopen("/dev/null", "w", stderr);
1310
debug("-- screen.back debug started\n");
1313
* This guarantees that the session owner is listed, even when we
1314
* start detached. From now on we should not refer to 'LoginName'
1315
* any more, use users->u_name instead.
1317
if (UserAdd(LoginName, (char *)0, (struct acluser **)0) < 0)
1318
Panic(0, "Could not create user info");
1321
if (MakeDisplay(LoginName, attach_tty, attach_term, n, getppid(), &attach_Mode) == 0)
1322
Panic(0, "Could not alloc display");
1325
D_encoding = nwin_options.encoding > 0 ? nwin_options.encoding : 0;
1326
debug1("D_encoding = %d\n", D_encoding);
1332
/* user started us with -S option */
1333
sprintf(socknamebuf, "%d.%s", (int)getpid(), SockMatch);
1337
sprintf(socknamebuf, "%d.%s.%s", (int)getpid(), stripdev(attach_tty),
1340
for (ap = socknamebuf; *ap; ap++)
1344
if (strlen(socknamebuf) > NAME_MAX)
1346
debug2("Socketname %s truncated to %d chars\n", socknamebuf, NAME_MAX);
1347
socknamebuf[NAME_MAX] = 0;
1350
sprintf(SockPath + strlen(SockPath), "/%s", socknamebuf);
1352
ServerSocket = MakeServerSocket();
1355
# ifdef ALLOW_SYSSCREENRC
1356
if ((ap = getenv("SYSSCREENRC")))
1357
(void)StartRc(ap, 0);
1360
(void)StartRc(ETCSCREENRC, 0);
1362
(void)StartRc(RcFileName, 0);
1366
# endif /* UTNOKEEP */
1367
# endif /* UTMPOK */
1370
if (InitTermcap(0, 0))
1372
debug("Could not init termcap - exiting\n");
1373
fcntl(D_userfd, F_SETFL, 0); /* Flush sets FNBLOCK */
1376
Kill(D_userpid, SIG_BYE);
1379
MakeDefaultCanvas();
1391
signal(SIGHUP, SigHup);
1392
signal(SIGINT, FinitHandler);
1393
signal(SIGQUIT, FinitHandler);
1394
signal(SIGTERM, FinitHandler);
1396
signal(SIGTTIN, SIG_IGN);
1397
signal(SIGTTOU, SIG_IGN);
1403
SetMode(&D_OldMode, &D_NewMode, D_flow, iflag);
1404
/* Note: SetMode must be called _before_ FinishRc. */
1405
SetTTY(D_userfd, &D_NewMode);
1406
if (fcntl(D_userfd, F_SETFL, FNBLOCK))
1407
Msg(errno, "Warning: NBLOCK fcntl failed");
1410
brktty(-1); /* just try */
1411
signal(SIGCHLD, SigChld);
1413
# ifdef ALLOW_SYSSCREENRC
1414
if ((ap = getenv("SYSSCREENRC")))
1418
FinishRc(ETCSCREENRC);
1420
FinishRc(RcFileName);
1422
debug2("UID %d EUID %d\n", (int)getuid(), (int)geteuid());
1423
if (windows == NULL)
1425
debug("We open one default window, as screenrc did not specify one.\n");
1426
if (MakeWindow(&nwin) == -1)
1429
struct timeval tv = { MsgWait/1000, 1000*(MsgWait%1000) };
1432
Msg(0, "Sorry, could not find a PTY or TTY.");
1433
// allow user to exit early by pressing any key.
1434
select(1, &rfd, NULL, NULL, &tv);
1439
else if (ac) /* Screen was invoked with a command */
1448
if (display && default_startup)
1449
display_copyright();
1450
signal(SIGINT, SigInt);
1451
if (rflag && (rflag & 1) == 0 && !quietflag)
1453
Msg(0, "New screen...");
1457
serv_read.type = EV_READ;
1458
serv_read.fd = ServerSocket;
1459
serv_read.handler = serv_read_fn;
1462
serv_select.pri = -10;
1463
serv_select.type = EV_ALWAYS;
1464
serv_select.handler = serv_select_fn;
1465
evenq(&serv_select);
1467
logflushev.type = EV_TIMEOUT;
1468
logflushev.handler = logflush_fn;
1476
WindowDied(p, wstat, wstat_valid)
1487
if (p->w_destroyev.data == (char *)p)
1489
wstat = p->w_exitstatus;
1491
evdeq(&p->w_destroyev);
1492
p->w_destroyev.data = 0;
1495
#if defined(BSDJOBS) && !defined(BSDWAIT)
1496
if (!wstat_valid && p->w_pid > 0)
1498
/* EOF on file descriptor. The process is probably also dead.
1500
if (waitpid(p->w_pid, &wstat, WNOHANG | WUNTRACED) == p->w_pid)
1507
if (ZombieKey_destroy && ZombieKey_onerror && wstat_valid &&
1508
WIFEXITED(wstat) && WEXITSTATUS(wstat) == 0)
1511
if (ZombieKey_destroy && !killit)
1513
char buf[100], *s, reason[100];
1517
if (WIFEXITED(wstat))
1518
if (WEXITSTATUS(wstat))
1519
sprintf(reason, "terminated with exit status %d", WEXITSTATUS(wstat));
1521
sprintf(reason, "terminated normally");
1522
else if (WIFSIGNALED(wstat))
1523
sprintf(reason, "terminated with signal %d%s", WTERMSIG(wstat),
1525
WCOREDUMP(wstat) ? " (core file generated)" : "");
1530
sprintf(reason, "detached from window");
1535
s[strlen(s) - 1] = '\0';
1536
debug3("window %d (%s) going into zombie state fd %d",
1537
p->w_number, p->w_title, p->w_ptyfd);
1539
if (p->w_slot != (slot_t)0 && p->w_slot != (slot_t)-1)
1542
p->w_slot = 0; /* "detached" */
1547
p->w_deadpid = p->w_pid;
1550
/* p->w_y = p->w_bot; */
1551
p->w_y = MFindUsedLine(p, p->w_bot, 1);
1552
sprintf(buf, "\n\r=== Command %s (%s) ===", reason, s ? s : "?");
1553
WriteString(p, buf, strlen(buf));
1554
WindowChanged(p, 'f');
1575
signal(SIGCHLD, SigChld);
1578
if (stat(SockPath, &st) == -1)
1580
debug1("SigChldHandler: Yuck! cannot stat '%s'\n", SockPath);
1581
if (!RecoverSocket())
1583
debug("SCREEN cannot recover from corrupt Socket, bye\n");
1587
debug1("'%s' reconstructed\n", SockPath);
1590
debug2("SigChldHandler: stat '%s' o.k. (%03o)\n", SockPath, (int)st.st_mode);
1596
debug("SigChld()\n");
1604
/* Hangup all displays */
1605
while ((display = displays) != 0)
1611
* the backend's Interrupt handler
1612
* we cannot insert the intrc directly, as we never know
1621
debug("SigInt()\n");
1622
if (fore && displays)
1624
# if defined(TERMIO) || defined(POSIX)
1625
ibuf = displays->d_OldMode.tio.c_cc[VINTR];
1627
ibuf = displays->d_OldMode.m_tchars.t_intrc;
1630
write(fore->w_ptyfd, &ibuf, 1);
1633
signal(SIGINT, SigInt);
1634
debug("SigInt() careful\n");
1635
InterruptPlease = 1;
1643
/* if running with s-bit, we must reset the s-bit, so that we get a
1647
struct display *disp;
1650
char *dump_msg = " (core dumped)";
1652
int running_w_s_bit = getuid() != geteuid();
1653
#if defined(SHADOWPW) && !defined(DEBUG) && !defined(DUMPSHADOW)
1654
if (running_w_s_bit)
1658
#if defined(SYSVSIGS) && defined(SIGHASARG)
1659
signal(sigsig, SIG_IGN);
1666
sprintf(buf, "\r\n[screen caught signal %d.%s]\r\n", sigsig, dump_msg);
1668
sprintf(buf, "\r\n[screen caught a fatal signal.%s]\r\n", dump_msg);
1671
for (disp = displays; disp; disp = disp->d_next)
1673
if (disp->d_nonblock < -1 || disp->d_nonblock > 1000000)
1675
fcntl(disp->d_userfd, F_SETFL, 0);
1676
SetTTY(disp->d_userfd, &D_OldMode);
1677
write(disp->d_userfd, buf, strlen(buf));
1678
Kill(disp->d_userpid, SIG_BYE);
1681
if (running_w_s_bit)
1683
#if defined(SHADOWPW) && !defined(DEBUG) && !defined(DUMPSHADOW)
1684
Kill(getpid(), SIGKILL);
1686
#else /* SHADOWPW && !DEBUG */
1688
#endif /* SHADOWPW && !DEBUG */
1699
struct win *p, *next;
1708
while ((pid = waitpid(-1, &wstat, WNOHANG | WUNTRACED)) > 0)
1712
* From: rouilj@sni-usa.com (John Rouillard)
1713
* note that WUNTRACED is not documented to work, but it is defined in
1714
* /usr/include/sys/wait.h, so it may work
1716
while ((pid = wait2(&wstat, WNOHANG | WUNTRACED )) > 0)
1717
# else /* USE_WAIT2 */
1718
while ((pid = wait3(&wstat, WNOHANG | WUNTRACED, (struct rusage *) 0)) > 0)
1719
# endif /* USE_WAIT2 */
1722
while ((pid = wait(&wstat)) < 0)
1726
#endif /* BSDJOBS */
1728
for (p = windows; p; p = next)
1731
if ( (p->w_pid && pid == p->w_pid) ||
1732
(p->w_deadpid && pid == p->w_deadpid) )
1734
/* child has ceased to exist */
1738
if (WIFSTOPPED(wstat))
1740
debug3("Window %d pid %d: WIFSTOPPED (sig %d)\n", p->w_number, pid, WSTOPSIG(wstat));
1742
if (WSTOPSIG(wstat) == SIGTTIN)
1744
Msg(0, "Suspended (tty input)");
1749
if (WSTOPSIG(wstat) == SIGTTOU)
1751
Msg(0, "Suspended (tty output)");
1755
/* Try to restart process */
1756
Msg(0, "Child has been stopped, restarting.");
1757
if (killpg(pid, SIGCONT))
1763
/* Screen will detect the window has died when the window's
1764
* file descriptor signals EOF (which it will do when the process in
1765
* the window terminates). So do this in a timeout of 10 seconds.
1766
* (not doing this at all might also work)
1767
* See #27061 for more details.
1769
p->w_destroyev.data = (char *)p;
1770
p->w_exitstatus = wstat;
1771
SetTimeout(&p->w_destroyev, 10 * 1000);
1772
evenq(&p->w_destroyev);
1777
if (p->w_pwin && pid == p->w_pwin->p_pid)
1779
debug2("pseudo of win Nr %d died. pid == %d\n", p->w_number, p->w_pwin->p_pid);
1787
debug1("pid %d not found - hope that's ok\n", pid);
1794
FinitHandler SIGDEFARG
1797
debug1("FinitHandler called, sig %d.\n", sigsig);
1799
debug("FinitHandler called.\n");
1809
signal(SIGCHLD, SIG_DFL);
1810
signal(SIGHUP, SIG_IGN);
1811
debug1("Finit(%d);\n", i);
1814
struct win *p = windows;
1815
windows = windows->w_next;
1818
if (ServerSocket != -1)
1820
debug1("we unlink(%s)\n", SockPath);
1825
(void) unlink(SockPath);
1831
for (display = displays; display; display = display->d_next)
1839
AddStr("[screen is terminating]\r\n");
1841
SetTTY(D_userfd, &D_OldMode);
1842
fcntl(D_userfd, F_SETFL, 0);
1844
Kill(D_userpid, SIG_BYE);
1847
* we _cannot_ call eexit(i) here,
1848
* instead of playing with the Socket above. Sigh.
1858
if (ServerSocket != -1)
1860
debug1("we unlink(%s)\n", SockPath);
1863
(void) unlink(SockPath);
1873
debug1("Hangup %x\n", display);
1879
if (auto_detach || displays->d_next)
1886
* Detach now has the following modes:
1887
*D_DETACH SIG_BYE detach backend and exit attacher
1888
*D_HANGUP SIG_BYE detach backend and exit attacher
1889
*D_STOP SIG_STOP stop attacher (and detach backend)
1890
*D_REMOTE SIG_BYE remote detach -- reattach to new attacher
1891
*D_POWER SIG_POWER_BYE power detach -- attacher kills his parent
1892
*D_REMOTE_POWER SIG_POWER_BYE remote power detach -- both
1893
*D_LOCK SIG_LOCK lock the attacher
1895
* we always remove our utmp slots. (even when "lock" or "stop")
1896
* Note: Take extra care here, we may be called by interrupt!
1909
#define AddStrSock(msg) do { \
1912
AddStr("[" msg " from "); \
1917
AddStr("[" msg "]\r\n"); \
1920
signal(SIGHUP, SIG_IGN);
1921
debug1("Detach(%d)\n", mode);
1933
AddStrSock("detached");
1941
#ifdef REMOTE_DETACH
1943
AddStrSock("remote detached");
1949
AddStrSock("power detached");
1950
if (PowDetachString)
1952
AddStr(PowDetachString);
1955
sign = SIG_POWER_BYE;
1957
#ifdef REMOTE_DETACH
1958
case D_REMOTE_POWER:
1959
AddStrSock("remote power detached");
1960
if (PowDetachString)
1962
AddStr(PowDetachString);
1965
sign = SIG_POWER_BYE;
1972
/* tell attacher to lock terminal with a lockprg. */
1976
if (displays->d_next == 0)
1978
for (p = windows; p; p = p->w_next)
1980
if (p->w_slot != (slot_t) -1 && !(p->w_lflag & 2))
1984
* Set the slot to 0 to get the window
1987
p->w_slot = (slot_t) 0;
1991
if (mode != D_HANGUP)
1994
if (displays->d_next == 0 && console_window)
1996
if (TtyGrabConsole(console_window->w_ptyfd, 0, "detach"))
1998
debug("could not release console - killing window\n");
1999
KillWindow(console_window);
2000
display = displays; /* restore display */
2006
ReleaseAutoWritelock(display, D_fore);
2008
D_user->u_detachwin = D_fore->w_number;
2009
D_user->u_detachotherwin = D_other ? D_other->w_number : -1;
2011
AutosaveLayout(D_layout);
2012
layout_last = D_layout;
2013
for (cv = D_cvlist; cv; cv = cv->c_next)
2015
p = Layer2Window(cv->c_layer);
2016
SetCanvasWindow(cv, 0);
2018
WindowChanged(p, 'u');
2022
debug2("display: %#x displays: %#x\n", (unsigned int)display, (unsigned int)displays);
2025
/* Flag detached-ness */
2028
* tell father what to do. We do that after we
2029
* freed the tty, thus getty feels more comfortable on hpux
2030
* if it was a power detach.
2033
debug2("Detach: Signal %d to Attacher(%d)!\n", sign, pid);
2034
debug("Detach returns, we are successfully detached.\n");
2035
signal(SIGHUP, SigHup);
2046
return strncmp(e, s, l) == 0 && e[l] == '=';
2052
register char **op, **np;
2053
static char stybuf[MAXSTR];
2055
for (op = environ; *op; ++op)
2058
free((char *)NewEnv);
2059
NewEnv = np = (char **) malloc((unsigned) (op - environ + 7 + 1) * sizeof(char **));
2061
Panic(0, "%s", strnomem);
2062
sprintf(stybuf, "STY=%s", strlen(SockName) <= MAXSTR - 5 ? SockName : "?");
2063
*np++ = stybuf; /* NewEnv[0] */
2064
*np++ = Term; /* NewEnv[1] */
2065
np++; /* room for SHELL */
2067
np += 2; /* room for TERMCAP and WINDOW */
2069
np += 4; /* room for TERMCAP WINDOW LINES COLUMNS */
2072
for (op = environ; *op; ++op)
2074
if (!IsSymbol(*op, "TERM") && !IsSymbol(*op, "TERMCAP")
2075
&& !IsSymbol(*op, "STY") && !IsSymbol(*op, "WINDOW")
2076
&& !IsSymbol(*op, "SCREENCAP") && !IsSymbol(*op, "SHELL")
2077
&& !IsSymbol(*op, "LINES") && !IsSymbol(*op, "COLUMNS")
2084
#if defined(USEVARARGS) && defined(__STDC__)
2085
#define DEFINE_VARARGS_FN(fnname) void fnname (int err, const char *fmt, VA_DOTS)
2087
#define DEFINE_VARARGS_FN(fnname) void fnname(err, fmt, VA_DOTS) \
2093
#define PROCESS_MESSAGE(B) do { \
2096
VA_START(ap, fmt); \
2098
(void)vsnprintf(p, sizeof(B) - 100, fmt, VA_ARGS(ap)); \
2105
strncpy(p, strerror(err), B + sizeof(B) - p - 1); \
2106
B[sizeof(B) - 1] = 0; \
2110
DEFINE_VARARGS_FN(Msg)
2112
char buf[MAXPATHLEN*2];
2113
PROCESS_MESSAGE(buf);
2115
debug2("Msg('%s') (%#x);\n", buf, (unsigned int)display);
2117
if (display && displays)
2121
for (display = displays; display; display = display->d_next)
2126
/* no displays but a display - must have forked.
2127
* send message to backend!
2129
char *tty = D_usertty;
2130
struct display *olddisplay = display;
2131
display = 0; /* only send once */
2132
SendErrorMsg(tty, buf);
2133
display = olddisplay;
2136
printf("%s\r\n", buf);
2139
write(queryflag, buf, strlen(buf));
2143
* Call FinitTerm for all displays, write a message to each and call eexit();
2145
DEFINE_VARARGS_FN(Panic)
2147
char buf[MAXPATHLEN*2];
2148
PROCESS_MESSAGE(buf);
2150
debug3("Panic('%s'); display=%x displays=%x\n", buf, display, displays);
2151
if (displays == 0 && display == 0)
2153
printf("%s\r\n", buf);
2155
Kill(PanicPid, SIG_BYE);
2157
else if (displays == 0)
2159
/* no displays but a display - must have forked.
2160
* send message to backend!
2162
char *tty = D_usertty;
2164
SendErrorMsg(tty, buf);
2169
for (display = displays; display; display = display->d_next)
2178
SetTTY(D_userfd, &D_OldMode);
2179
fcntl(D_userfd, F_SETFL, 0);
2180
write(D_userfd, buf, strlen(buf));
2181
write(D_userfd, "\n", 1);
2184
Kill(D_userpid, SIG_BYE);
2187
if (tty_oldmode >= 0)
2190
if (setuid(own_uid))
2191
xseteuid(own_uid); /* may be a loop. sigh. */
2195
debug1("Panic: changing back modes from %s\n", attach_tty);
2196
chmod(attach_tty, tty_oldmode);
2202
DEFINE_VARARGS_FN(QueryMsg)
2204
char buf[MAXPATHLEN*2];
2209
PROCESS_MESSAGE(buf);
2210
write(queryflag, buf, strlen(buf));
2213
DEFINE_VARARGS_FN(Dummy)
2216
#undef PROCESS_MESSAGE
2217
#undef DEFINE_VARARGS_FN
2220
* '^' is allowed as an escape mechanism for control characters. jw.
2222
* Added time insertion using ideas/code from /\ndy Jones
2223
* (andy@lingua.cltr.uq.OZ.AU) - thanks a lot!
2228
static const char days[] = "SunMonTueWedThuFriSat";
2229
static const char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
2232
static char winmsg_buf[MAXSTR];
2233
#define MAX_WINMSG_REND 256 /* rendition changes */
2234
static int winmsg_rend[MAX_WINMSG_REND];
2235
static int winmsg_rendpos[MAX_WINMSG_REND];
2236
static int winmsg_numrend;
2239
pad_expand(buf, p, numpad, padlen)
2248
padlen = padlen - (p - buf); /* space for rent */
2251
pn2 = pn = p + padlen;
2255
if (r && *p != 127 && p - buf == winmsg_rendpos[r - 1])
2257
winmsg_rendpos[--r] = pn - buf;
2264
i = numpad > 0 ? (padlen + numpad - 1) / numpad : 0;
2269
if (r && p - buf == winmsg_rendpos[r - 1])
2270
winmsg_rendpos[--r] = pn - buf;
2277
struct backtick *next;
2282
char result[MAXSTR];
2289
struct backtick *backticks;
2293
struct backtick *bt;
2298
for (p = q = bt->result; (c = (unsigned char)*p++) != 0;)
2302
if (c >= ' ' || c == '\005')
2309
backtick_fn(ev, data)
2313
struct backtick *bt;
2316
bt = (struct backtick *)data;
2317
debug1("backtick_fn for #%d\n", bt->num);
2319
l = read(ev->fd, bt->buf + i, MAXSTR - i);
2322
debug1("EOF on backtick #%d\n", bt->num);
2328
debug1("read %d bytes\n", l);
2330
for (j = 0; j < l; j++)
2331
if (bt->buf[i - j - 1] == '\n')
2335
for (k = i - j - 2; k >= 0; k--)
2336
if (bt->buf[k] == '\n')
2339
bcopy(bt->buf + k, bt->result, i - j - k);
2340
bt->result[i - j - k - 1] = 0;
2341
backtick_filter(bt);
2342
WindowChanged(0, '`');
2344
if (j == l && i == MAXSTR)
2352
bcopy(bt->buf + i - j, bt->buf, j);
2359
setbacktick(num, lifespan, tick, cmdv)
2365
struct backtick **btp, *bt;
2368
debug1("setbacktick called for backtick #%d\n", num);
2369
for (btp = &backticks; (bt = *btp) != 0; btp = &bt->next)
2376
for (v = bt->cmdv; *v; v++)
2393
bt = (struct backtick *)malloc(sizeof *bt);
2396
Msg(0, "%s", strnomem);
2399
bzero(bt, sizeof(*bt));
2405
bt->lifespan = lifespan;
2412
if (bt->tick == 0 && bt->lifespan == 0)
2414
debug("setbacktick: continuous mode\n");
2415
bt->buf = (char *)malloc(MAXSTR);
2418
Msg(0, "%s", strnomem);
2419
setbacktick(num, 0, 0, (char **)0);
2422
bt->ev.type = EV_READ;
2423
bt->ev.fd = readpipe(bt->cmdv);
2424
bt->ev.handler = backtick_fn;
2425
bt->ev.data = (char *)bt;
2432
runbacktick(bt, tickp, now)
2433
struct backtick *bt;
2440
debug1("runbacktick called for backtick #%d\n", bt->num);
2441
if (bt->tick && (!*tickp || bt->tick < *tickp))
2443
if ((bt->lifespan == 0 && bt->tick == 0) || now < bt->bestbefore)
2445
debug1("returning old result (%d)\n", bt->lifespan);
2448
f = readpipe(bt->cmdv);
2452
while ((l = read(f, bt->result + i, sizeof(bt->result) - i)) > 0)
2454
debug1("runbacktick: read %d bytes\n", l);
2456
for (j = 1; j < l; j++)
2457
if (bt->result[i - j - 1] == '\n')
2459
if (j == l && i == sizeof(bt->result))
2461
j = sizeof(bt->result) / 2;
2466
bcopy(bt->result + i - j, bt->result, j);
2471
bt->result[sizeof(bt->result) - 1] = '\n';
2472
if (i && bt->result[i - 1] == '\n')
2474
debug1("runbacktick: finished, %d bytes\n", i);
2476
backtick_filter(bt);
2478
bt->bestbefore = now2 + bt->lifespan;
2483
AddWinMsgRend(str, r)
2487
if (winmsg_numrend >= MAX_WINMSG_REND || str < winmsg_buf ||
2488
str >= winmsg_buf + MAXSTR)
2491
winmsg_rend[winmsg_numrend] = r;
2492
winmsg_rendpos[winmsg_numrend] = str - winmsg_buf;
2499
MakeWinMsgEv(str, win, esc, padlen, ev, rec)
2509
register char *p = winmsg_buf;
2519
int qmflag = 0, omflag = 0, qmnumrend = 0;
2526
struct backtick *bt = NULL;
2528
if (winmsg_numrend >= 0)
2531
winmsg_numrend = -winmsg_numrend;
2536
gettimeofday(&now, NULL);
2537
for (; *s && (l = winmsg_buf + MAXSTR - 1 - p) > 0; s++, p++)
2543
if (*s != '^' && *s >= 64)
2568
if (*++s == esc) /* double escape ? */
2570
if ((plusflg = *s == '+') != 0)
2572
if ((minusflg = *s == '-') != 0)
2574
if ((zeroflg = *s == '0') != 0)
2577
while(*s >= '0' && *s <= '9')
2578
num = num * 10 + (*s++ - '0');
2579
if ((longflg = *s == 'L') != 0)
2587
if ((!qmflag && !omflag) || omflag == 1)
2590
if (qmnumrend < winmsg_numrend)
2591
winmsg_numrend = qmnumrend;
2597
qmnumrend = winmsg_numrend;
2598
qmflag = omflag = 0;
2604
if (qmflag && omflag != 1)
2608
qmnumrend = winmsg_numrend;
2613
if (qmnumrend < winmsg_numrend)
2614
winmsg_numrend = qmnumrend;
2618
case 'd': case 'D': case 'm': case 'M': case 'y': case 'Y':
2619
case 'a': case 'A': case 's': case 'c': case 'C':
2624
time_t nowsec = now.tv_sec;
2625
tm = localtime(&nowsec);
2628
if (!tick || tick > 3600)
2633
sprintf(p, "%02d", tm->tm_mday % 100);
2637
strftime(p, l, (longflg ? "%A" : "%a"), tm);
2639
sprintf(p, "%3.3s", days + 3 * tm->tm_wday);
2643
sprintf(p, "%02d", tm->tm_mon + 1);
2647
strftime(p, l, (longflg ? "%B" : "%b"), tm);
2649
sprintf(p, "%3.3s", months + 3 * tm->tm_mon);
2653
sprintf(p, "%02d", tm->tm_year % 100);
2656
sprintf(p, "%04d", tm->tm_year + 1900);
2659
sprintf(p, tm->tm_hour >= 12 ? "pm" : "am");
2662
sprintf(p, tm->tm_hour >= 12 ? "PM" : "AM");
2665
sprintf(p, "%02d", tm->tm_sec);
2669
sprintf(p, zeroflg ? "%02d:%02d" : "%2d:%02d", tm->tm_hour, tm->tm_min);
2670
if (!tick || tick > 60)
2674
sprintf(p, zeroflg ? "%02d:%02d" : "%2d:%02d", (tm->tm_hour + 11) % 12 + 1, tm->tm_min);
2675
if (!tick || tick > 60)
2695
if (!tick || tick > 60)
2704
if (rec >= 10 || (*s == 'h' && (win == 0 || win->w_hstatus == 0 || *win->w_hstatus == 0)))
2711
for (bt = backticks; bt; bt = bt->next)
2721
char savebuf[sizeof(winmsg_buf)];
2723
int oldnumrend = winmsg_numrend;
2726
strcpy(savebuf, winmsg_buf);
2727
winmsg_numrend = -winmsg_numrend;
2728
MakeWinMsgEv(*s == 'h' ? win->w_hstatus : runbacktick(bt, &oldtick, now.tv_sec), win, '\005', 0, (struct event *)0, rec + 1);
2729
debug2("oldtick=%d tick=%d\n", oldtick, tick);
2730
if (!tick || oldtick < tick)
2732
if ((int)strlen(winmsg_buf) < l)
2733
strcat(savebuf, winmsg_buf);
2734
strcpy(winmsg_buf, savebuf);
2735
while (oldnumrend < winmsg_numrend)
2736
winmsg_rendpos[oldnumrend++] += p - winmsg_buf;
2745
struct win *oldfore = 0;
2753
ss = AddWindows(p, l - 1, (*s == 'w' ? 0 : 1) | (longflg ? 0 : 2) | (plusflg ? 4 : 0) | (minusflg ? 8 : 0), win ? win->w_number : -1);
2764
AddOtherUsers(p, l - 1, win);
2772
AddWindowFlags(p, l - 1, win);
2779
if (win && (int)strlen(win->w_title) < l)
2781
strcpy(p, win->w_title);
2791
for (i = 0; i < 127; i++)
2792
if (s[i] && s[i] != '}')
2796
if (s[i] == '}' && winmsg_numrend < MAX_WINMSG_REND)
2800
debug1("MakeWinMsg attrcolor %s\n", rbuf);
2801
if (i != 1 || rbuf[0] != '-')
2802
r = ParseAttrColor(rbuf, (char *)0, 0);
2803
if (r != -1 || (i == 1 && rbuf[0] == '-'))
2805
winmsg_rend[winmsg_numrend] = r;
2806
winmsg_rendpos[winmsg_numrend] = p - winmsg_buf;
2816
if ((int)strlen(HostName) < l)
2818
strcpy(p, HostName);
2828
session_name = strchr(SockName, '.') + 1;
2829
if ((int)strlen(session_name) < l)
2831
strcpy(p, session_name);
2840
sprintf(p, "%d", (plusflg && display) ? D_userpid : getpid());
2847
if (display && ((ev && ev == &D_forecv->c_captev) || (!ev && win && win == D_fore)))
2848
minusflg = !minusflg;
2855
if (display && ev && ev != &D_hstatusev) /* Hack */
2857
/* Is the layer in the current canvas in copy mode? */
2858
struct canvas *cv = (struct canvas *)ev->data;
2859
if (ev == &cv->c_captev && cv->c_layer->l_layfn == &MarkLf)
2866
if (display && D_ESCseen)
2870
truncpos = p - winmsg_buf;
2871
truncper = num > 100 ? 100 : num;
2872
trunclong = longflg;
2878
if (num || zeroflg || plusflg || longflg || (*s != '='))
2880
/* expand all pads */
2883
num = (plusflg ? lastpad : padlen) - num;
2884
if (!plusflg && padlen == 0)
2885
num = p - winmsg_buf;
2890
if (*s != '=' && num == 0 && !plusflg)
2895
num = p - winmsg_buf;
2897
num = (padlen - (plusflg ? lastpad : 0)) * num / 100;
2903
if (num > MAXSTR - 1)
2906
p = pad_expand(winmsg_buf, p, numpad, num);
2908
if (p - winmsg_buf > num && !longflg)
2917
trunc = lastpad + truncper * (num - lastpad) / 100;
2920
if (trunc < lastpad)
2922
left = truncpos - trunc;
2923
if (left > p - winmsg_buf - num)
2924
left = p - winmsg_buf - num;
2925
debug1("lastpad = %d, ", lastpad);
2926
debug3("truncpos = %d, trunc = %d, left = %d\n", truncpos, trunc, left);
2929
if (left + lastpad > p - winmsg_buf)
2930
left = p - winmsg_buf - lastpad;
2931
if (p - winmsg_buf - lastpad - left > 0)
2932
bcopy(winmsg_buf + lastpad + left, winmsg_buf + lastpad, p - winmsg_buf - lastpad - left);
2935
while (r && winmsg_rendpos[r - 1] > lastpad)
2938
winmsg_rendpos[r] -= left;
2939
if (winmsg_rendpos[r] < lastpad)
2940
winmsg_rendpos[r] = lastpad;
2944
if (p - winmsg_buf > lastpad)
2945
winmsg_buf[lastpad] = '.';
2946
if (p - winmsg_buf > lastpad + 1)
2947
winmsg_buf[lastpad + 1] = '.';
2948
if (p - winmsg_buf > lastpad + 2)
2949
winmsg_buf[lastpad + 2] = '.';
2952
if (p - winmsg_buf > num)
2954
p = winmsg_buf + num;
2957
if (num - 1 >= lastpad)
2959
if (num - 2 >= lastpad)
2961
if (num - 3 >= lastpad)
2965
while (r && winmsg_rendpos[r - 1] > num)
2966
winmsg_rendpos[--r] = num;
2970
if (lastpad > p - winmsg_buf)
2971
lastpad = p - winmsg_buf;
2972
debug1("lastpad now %d\n", lastpad);
2976
while (p - winmsg_buf < num)
2978
lastpad = p - winmsg_buf;
2981
debug1("lastpad2 now %d\n", lastpad);
2987
*p = 127; /* internal pad representation */
3001
sprintf(p, "%*s", num, num > 1 ? "--" : "-");
3003
sprintf(p, "%*d", num, win->w_number);
3010
if (qmpos && !qmflag)
3015
if (padlen > MAXSTR - 1)
3016
padlen = MAXSTR - 1;
3017
p = pad_expand(winmsg_buf, p, numpad, padlen);
3021
evdeq(ev); /* just in case */
3022
ev->timeout.tv_sec = 0;
3023
ev->timeout.tv_usec = 0;
3027
now.tv_usec = 100000;
3031
now.tv_sec += tick - (now.tv_sec % tick);
3033
debug2("NEW timeout %d %d\n", ev->timeout.tv_sec, tick);
3039
MakeWinMsg(s, win, esc)
3044
return MakeWinMsgEv(s, win, esc, 0, (struct event *)0, 0);
3048
PutWinMsg(s, start, max)
3054
struct mchar rendstack[MAX_WINMSG_REND];
3057
if (s != winmsg_buf)
3059
/* sorry, no fancy coloring available */
3060
debug1("PutWinMsg %s plain\n", s);
3073
debug2("PutWinMsg %s start attr %x\n", s, rend.attr);
3074
for (i = 0; i < winmsg_numrend && max > 0; i++)
3076
if (p > winmsg_rendpos[i] || winmsg_rendpos[i] > l)
3078
if (p < winmsg_rendpos[i])
3080
n = winmsg_rendpos[i] - p;
3097
rend = rendstack[--rendstackn];
3101
rendstack[rendstackn++] = rend;
3102
ApplyAttrColor(r, &rend);
3104
SetRendition(&rend);
3132
if ((j = open("/dev/null", 0)) >= 0)
3139
while (dup(++i) < 0 && errno != EBADF)
3141
debug1(" [%d]\n", i);
3154
serv_read_fn(ev, data)
3158
debug("Knock - knock!\n");
3163
serv_select_fn(ev, data)
3169
debug("serv_select_fn called\n");
3170
/* XXX: messages?? */
3175
if (InterruptPlease)
3177
debug("Backend received interrupt\n");
3178
/* This approach is rather questionable in a multi-display
3180
if (fore && displays)
3182
#if defined(TERMIO) || defined(POSIX)
3183
char ibuf = displays->d_OldMode.tio.c_cc[VINTR];
3185
char ibuf = displays->d_OldMode.m_tchars.t_intrc;
3188
write(W_UWP(fore) ? fore->w_pwin->p_ptyfd : fore->w_ptyfd,
3190
debug1("Backend wrote interrupt to %d", fore->w_number);
3191
debug1("%s\n", W_UWP(fore) ? " (pseudowin)" : "");
3193
write(fore->w_ptyfd, &ibuf, 1);
3194
debug1("Backend wrote interrupt to %d\n", fore->w_number);
3197
InterruptPlease = 0;
3200
for (p = windows; p; p = p->w_next)
3202
if (p->w_bell == BELL_FOUND || p->w_bell == BELL_VISUAL)
3205
int visual = p->w_bell == BELL_VISUAL || visual_bell;
3206
p->w_bell = BELL_ON;
3207
for (display = displays; display; display = display->d_next)
3209
for (cv = D_cvlist; cv; cv = cv->c_next)
3210
if (cv->c_layer->l_bottom == &p->w_layer)
3214
p->w_bell = BELL_DONE;
3215
Msg(0, "%s", MakeWinMsg(BellString, p, '%'));
3217
else if (visual && !D_VB && (!D_status || !D_status_bell))
3219
Msg(0, "%s", VisualBellString);
3223
debug1("using vbell timeout %d\n", VBellWait);
3224
SetTimeout(&D_statusev, VBellWait );
3228
/* don't annoy the user with two messages */
3229
if (p->w_monitor == MON_FOUND)
3230
p->w_monitor = MON_DONE;
3231
WindowChanged(p, 'f');
3233
if (p->w_monitor == MON_FOUND)
3236
p->w_monitor = MON_ON;
3237
for (display = displays; display; display = display->d_next)
3239
for (cv = D_cvlist; cv; cv = cv->c_next)
3240
if (cv->c_layer->l_bottom == &p->w_layer)
3243
continue; /* user already sees window */
3245
if (!(ACLBYTE(p->w_mon_notify, D_user->u_id) & ACLBIT(D_user->u_id)))
3246
continue; /* user doesn't care */
3248
Msg(0, "%s", MakeWinMsg(ActivityString, p, '%'));
3249
p->w_monitor = MON_DONE;
3251
WindowChanged(p, 'f');
3253
if (p->w_silence == SILENCE_FOUND)
3255
/* Unset the flag if the user switched to this window. */
3256
if (p->w_layer.l_cvlist)
3258
p->w_silence = SILENCE_ON;
3259
WindowChanged(p, 'f');
3264
for (display = displays; display; display = display->d_next)
3267
if (D_status == STATUS_ON_WIN)
3269
/* XXX: should use display functions! */
3270
for (cv = D_cvlist; cv; cv = cv->c_next)
3274
/* normalize window, see resize.c */
3275
lx = cv->c_layer->l_x;
3276
ly = cv->c_layer->l_y;
3277
if (lx == cv->c_layer->l_width)
3279
if (ly + cv->c_yoff < cv->c_ys)
3281
int i, n = cv->c_ys - (ly + cv->c_yoff);
3282
cv->c_yoff = cv->c_ys - ly;
3283
RethinkViewportOffsets(cv);
3284
if (n > cv->c_layer->l_height)
3285
n = cv->c_layer->l_height;
3287
LScrollV(flayer, -n, 0, flayer->l_height - 1, 0);
3288
LayRedisplayLine(-1, -1, -1, 1);
3289
for (i = 0; i < n; i++)
3290
LayRedisplayLine(i, 0, flayer->l_width - 1, 1);
3291
if (cv == cv->c_display->d_forecv)
3295
else if (ly + cv->c_yoff > cv->c_ye)
3297
int i, n = ly + cv->c_yoff - cv->c_ye;
3298
cv->c_yoff = cv->c_ye - ly;
3299
RethinkViewportOffsets(cv);
3300
if (n > cv->c_layer->l_height)
3301
n = cv->c_layer->l_height;
3303
LScrollV(flayer, n, 0, cv->c_layer->l_height - 1, 0);
3304
LayRedisplayLine(-1, -1, -1, 1);
3305
for (i = 0; i < n; i++)
3306
LayRedisplayLine(i + flayer->l_height - n, 0, flayer->l_width - 1, 1);
3307
if (cv == cv->c_display->d_forecv)
3311
if (lx + cv->c_xoff < cv->c_xs)
3313
int i, n = cv->c_xs - (lx + cv->c_xoff);
3314
if (n < (cv->c_xe - cv->c_xs + 1) / 2)
3315
n = (cv->c_xe - cv->c_xs + 1) / 2;
3316
if (cv->c_xoff + n > cv->c_xs)
3317
n = cv->c_xs - cv->c_xoff;
3319
RethinkViewportOffsets(cv);
3320
if (n > cv->c_layer->l_width)
3321
n = cv->c_layer->l_width;
3323
LayRedisplayLine(-1, -1, -1, 1);
3324
for (i = 0; i < flayer->l_height; i++)
3326
LScrollH(flayer, -n, i, 0, flayer->l_width - 1, 0, 0);
3327
LayRedisplayLine(i, 0, n - 1, 1);
3329
if (cv == cv->c_display->d_forecv)
3333
else if (lx + cv->c_xoff > cv->c_xe)
3335
int i, n = lx + cv->c_xoff - cv->c_xe;
3336
if (n < (cv->c_xe - cv->c_xs + 1) / 2)
3337
n = (cv->c_xe - cv->c_xs + 1) / 2;
3338
if (cv->c_xoff - n + cv->c_layer->l_width - 1 < cv->c_xe)
3339
n = cv->c_xoff + cv->c_layer->l_width - 1 - cv->c_xe;
3341
RethinkViewportOffsets(cv);
3342
if (n > cv->c_layer->l_width)
3343
n = cv->c_layer->l_width;
3345
LayRedisplayLine(-1, -1, -1, 1);
3346
for (i = 0; i < flayer->l_height; i++)
3348
LScrollH(flayer, n, i, 0, flayer->l_width - 1, 0, 0);
3349
LayRedisplayLine(i, flayer->l_width - n, flayer->l_width - 1, 1);
3351
if (cv == cv->c_display->d_forecv)
3358
for (display = displays; display; display = display->d_next)
3360
if (D_status == STATUS_ON_WIN || D_cvlist == 0 || D_cvlist->c_next == 0)
3362
debug1("serv_select_fn: Restore on cv %#x\n", (int)D_forecv);
3363
CV_CALL(D_forecv, LayRestore();LaySetCursor());
3368
logflush_fn(ev, data)
3376
if (!islogfile(NULL))
3377
return; /* no more logfiles */
3379
n = log_flush ? log_flush : (logtstamp_after + 4) / 5;
3382
SetTimeout(ev, n * 1000);
3383
evenq(ev); /* re-enqueue ourself */
3387
/* write fancy time-stamp */
3388
for (p = windows; p; p = p->w_next)
3392
p->w_logsilence += n;
3393
if (p->w_logsilence < logtstamp_after)
3395
if (p->w_logsilence - n >= logtstamp_after)
3397
buf = MakeWinMsg(logtstamp_string, p, '%');
3398
logfwrite(p->w_log, buf, strlen(buf));
3403
* Interprets ^?, ^@ and other ^-control-char notation.
3404
* Interprets \ddd octal notation
3406
* The result is placed in *cp, p is advanced behind the parsed expression and
3415
if (*p == '^' && p[1])
3425
else if (*p == '\\' && *++p <= '7' && *p >= '0')
3429
*cp = *cp * 8 + *p - '0';
3430
while (*++p <= '7' && *p >= '0');
3441
unsigned char buf[2];
3444
SetEscape((struct acluser *)0, -1, -1);
3447
if ((p = ParseChar(p, (char *)buf)) == NULL ||
3448
(p = ParseChar(p, (char *)buf+1)) == NULL || *p)
3450
SetEscape((struct acluser *)0, buf[0], buf[1]);