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
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 3, or (at your option)
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program (see the file COPYING); if not, see
25 * https://www.gnu.org/licenses/, or contact Free Software Foundation, Inc.,
26 * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
28 ****************************************************************
44 #include <sys/ioctl.h>
46 #include <sys/types.h>
51 #if defined(HAVE_LANGINFO_H)
55 #include "logfile.h" /* islogfile, logfflush, logfopen/logfclose */
57 #include "list_generic.h"
62 extern char **environ
;
75 static struct passwd
*getpwbyname(char *, struct passwd
*);
76 static void SigChldHandler(void);
77 static void SigChld(int);
78 static void SigInt(int);
79 static void CoreDump(int);
80 static void FinitHandler(int);
81 static void DoWait(void);
82 static void serv_read_fn(Event
*, void *);
83 static void serv_select_fn(Event
*, void *);
84 static void logflush_fn(Event
*, void *);
85 static int IsSymbol(char *, char *);
86 static char *ParseChar(char *, char *);
87 static int ParseEscape(char *);
88 static void SetTtyname(bool fatal
, struct stat
*st
);
90 int nversion
; /* numerical version, used for secondary DA */
99 struct mode attach_Mode
;
101 /* Indicator whether the current tty exists in another namespace. */
102 bool attach_tty_is_in_new_ns
= false;
104 /* Content of the tty symlink when attach_tty_is_in_new_ns == true. */
105 char attach_tty_name_in_ns
[MAXPATHLEN
];
107 char SocketPath
[MAXPATHLEN
];
108 char *SocketName
; /* SocketName is pointer in SocketPath */
109 char *SocketMatch
= NULL
; /* session id command line argument */
110 int ServerSocket
= -1;
115 char **NewEnv
= NULL
;
116 char *RcFileName
= NULL
;
119 char *screenlogfile
; /* filename layout */
120 int log_flush
= 10; /* flush interval in seconds */
121 bool logtstamp_on
= false; /* tstamp disabled */
122 char *logtstamp_string
; /* stamp layout */
123 int logtstamp_after
= 120; /* first tstamp after 120s */
124 char *hardcopydir
= NULL
;
126 char *VisualBellString
;
127 char *ActivityString
;
129 char *PowDetachString
;
134 bool auto_detach
= true;
144 bool hastruecolor
= false;
149 int tty_oldmode
= -1;
151 char HostName
[MAXSTR
];
160 bool default_startup
;
161 int ZombieKey_destroy
;
162 int ZombieKey_resurrect
;
163 int ZombieKey_onerror
;
164 char *preselect
= NULL
; /* only used in Attach() */
166 char *screenencodings
;
173 Window
*first_window
;
175 Window
*console_window
;
184 #include "attacher.h"
185 #include "encoding.h"
193 char strnomem
[] = "Out of memory.";
195 static int InterruptPlease
;
196 static int GotSigChld
;
198 /********************************************************************/
199 /********************************************************************/
200 /********************************************************************/
202 static struct passwd
*getpwbyname(char *name
, struct passwd
*ppp
)
206 if (!ppp
&& !(ppp
= getpwnam(name
)))
209 /* Do password sanity check..., allow ##user for SUN_C2 security */
211 if (ppp
->pw_passwd
[0] == '#' && ppp
->pw_passwd
[1] == '#' && strcmp(ppp
->pw_passwd
+ 2, ppp
->pw_name
) == 0)
213 for (; n
< 13; n
++) {
214 char c
= ppp
->pw_passwd
[n
];
215 if (!(c
== '.' || c
== '/' || c
== '$' ||
216 (c
>= '0' && c
<= '9') || (c
>= 'a' && c
<= 'z') || (c
>= 'A' && c
<= 'Z')))
221 ppp
->pw_passwd
= NULL
;
222 if (ppp
->pw_passwd
&& strlen(ppp
->pw_passwd
) == 13 + 11)
223 ppp
->pw_passwd
[13] = 0; /* beware of linux's long passwords */
228 static char *locale_name(void)
232 s
= getenv("LC_ALL");
234 s
= getenv("LC_CTYPE");
242 static void exit_with_usage(char *myname
, char *message
, char *arg
)
244 printf("Use: %s [-opts] [cmd [args]]\n", myname
);
245 printf(" or: %s -r [host.tty]\n\nOptions:\n", myname
);
247 printf("-4 Resolve hostnames only to IPv4 addresses.\n");
248 printf("-6 Resolve hostnames only to IPv6 addresses.\n");
250 printf("-a Force all capabilities into each window's termcap.\n");
251 printf("-A -[r|R] Adapt all windows to the new display width & height.\n");
252 printf("-c file Read configuration file instead of '.screenrc'.\n");
253 printf("-d (-r) Detach the elsewhere running screen (and reattach here).\n");
254 printf("-dmS name Start as daemon: Screen session in detached mode.\n");
255 printf("-D (-r) Detach and logout remote (and reattach here).\n");
256 printf("-D -RR Do whatever is needed to get a screen session.\n");
257 printf("-e xy Change command characters.\n");
258 printf("-f Flow control on, -fn = off, -fa = auto.\n");
259 printf("-h lines Set the size of the scrollback history buffer.\n");
260 printf("-i Interrupt output sooner when flow control is on.\n");
261 #if defined(ENABLE_UTMP)
262 printf("-l Login mode on (update %s), -ln = off.\n", UTMPXFILE
);
264 printf("-ls [match] or\n");
265 printf("-list Do nothing, just list our SocketDir [on possible matches].\n");
266 printf("-L Turn on output logging.\n");
267 printf("-Logfile file Set logfile name.\n");
268 printf("-m ignore $STY variable, do create a new screen session.\n");
269 printf("-O Choose optimal output rather than exact vt100 emulation.\n");
270 printf("-p window Preselect the named window if it exists.\n");
271 printf("-P Tell screen to enable authentication.\n");
272 printf("-q Quiet startup. Exits with non-zero return code if unsuccessful.\n");
273 printf("-Q Commands will send the response to the stdout of the querying process.\n");
274 printf("-r [session] Reattach to a detached screen process.\n");
275 printf("-R Reattach if possible, otherwise start a new session.\n");
276 printf("-s shell Shell to execute rather than $SHELL.\n");
277 printf("-S sockname Name this session <pid>.sockname instead of <pid>.<tty>.<host>.\n");
278 printf("-t title Set title. (window's name).\n");
279 printf("-T term Use term as $TERM for windows, rather than \"screen\".\n");
280 printf("-U Tell screen to use UTF-8 encoding.\n");
281 printf("-v Print \"Screen version %s\".\n", version
);
282 printf("-wipe [match] Do nothing, just clean up SocketDir [on possible matches].\n");
283 printf("-x Attach to a not detached screen. (Multi display mode).\n");
284 printf("-X Execute <cmd> as a screen command in the specified session.\n");
285 if (message
&& *message
) {
287 printf(message
, arg
);
294 int main(int argc
, char **argv
)
299 char socknamebuf
[FILENAME_MAX
+ 1];
301 char *myname
= (argc
== 0) ? "screen" : argv
[0];
305 struct NewWindow nwin
;
306 bool detached
= false; /* start up detached */
309 char *multi_home
= NULL
;
313 * First, close all unused descriptors
314 * (otherwise, we might have problems with the select() call)
317 snprintf(version
, 59, "%d.%d.%d (build on %s) ", VERSION_MAJOR
, VERSION_MINOR
, VERSION_REVISION
, BUILD_DATE
);
318 nversion
= VERSION_MAJOR
* 10000 + VERSION_MINOR
* 100 + VERSION_REVISION
;
320 BellString
= SaveStr("Bell in window %n");
321 VisualBellString
= SaveStr(" Wuff, Wuff!! ");
322 ActivityString
= SaveStr("Activity in window %n");
323 screenlogfile
= SaveStr("screenlog.%n");
324 logtstamp_string
= SaveStr("-- %n:%t -- time-stamp -- %M/%d/%y %c:%s --\n");
325 hstatusstring
= SaveStr("%h");
326 captionstring
= SaveStr("%4n %t");
327 wlisttit
= SaveStr(" Num Name%=Flags");
328 wliststr
= SaveStr("%4n %t%=%f");
329 BufferFile
= SaveStr(DEFAULT_BUFFERFILE
);
331 PowDetachString
= NULL
;
332 default_startup
= (argc
> 1) ? false : true;
334 VBellWait
= VBELLWAIT
* 1000;
335 MsgWait
= MSGWAIT
* 1000;
336 MsgMinWait
= MSGMINWAIT
* 1000;
337 SilenceWait
= SILENCEWAIT
;
338 zmodem_sendcmd
= SaveStr("!!! sz -vv -b ");
339 zmodem_recvcmd
= SaveStr("!!! rz -vv -b -E");
341 CompileKeys(NULL
, 0, mark_key_tab
);
343 screenencodings
= SaveStr(SCREENENCODINGS
);
346 nwin_options
= nwin_undef
;
347 strncpy(screenterm
, "screen", MAXTERMLEN
);
348 screenterm
[MAXTERMLEN
] = '\0';
359 /* if this is a login screen, assume -RR */
363 ShellProg
= SaveStr(DefaultShell
); /* to prevent nasty circles */
368 if (--argc
> 0 && *ap
== '-') {
369 if (ap
[1] == '-' && ap
[2] == 0) {
374 if (ap
[1] == '-' && !strncmp(ap
, "--version", 9)) {
375 printf("Screen version %s\n", version
);
378 if (ap
[1] == '-' && !strncmp(ap
, "--help", 6))
379 exit_with_usage(myname
, NULL
, NULL
);
380 while (ap
&& *ap
&& *++ap
) {
391 nwin_options
.aflag
= true;
396 case 'p': /* preselect */
401 exit_with_usage(myname
, "Specify a window to preselect with -p",
415 exit_with_usage(myname
,
416 "Specify an alternate rc-filename with -c",
418 RcFileName
= *++argv
;
425 exit_with_usage(myname
, "Specify command characters with -e",
430 Panic(0, "Two characters are required with -e option, not '%s'.", ap
);
438 nwin_options
.flowflag
= FLOW_ON
;
445 nwin_options
.flowflag
= FLOW_OFF
;
448 nwin_options
.flowflag
= FLOW_AUTOFLAG
;
451 exit_with_usage(myname
, "Unknown flow option -%s", --ap
);
456 exit_with_usage(myname
, NULL
, NULL
);
457 nwin_options
.histheight
= atoi(*++argv
);
458 if (nwin_options
.histheight
< 0)
459 exit_with_usage(myname
, "-h: %s: negative scrollback size?", *argv
);
464 case 't': /* title, the former AkA == -k */
466 exit_with_usage(myname
, "Specify a new window-name with -t", NULL
);
467 nwin_options
.aka
= *++argv
;
474 nwin_options
.lflag
= 0;
481 nwin_options
.lflag
= 1;
484 nwin_options
.lflag
= 3;
487 case 'i': /* -list */
489 if (argc
> 1 && !SocketMatch
) {
490 SocketMatch
= *++argv
;
496 exit_with_usage(myname
, "%s: Unknown suboption to -l", --ap
);
500 if (strcmp(ap
+ 1, "ipe"))
501 exit_with_usage(myname
, "Unknown option %s", --ap
);
504 if (argc
> 1 && !SocketMatch
) {
505 SocketMatch
= *++argv
;
510 if (!strcmp(ap
+ 1, "ogfile")) {
512 exit_with_usage(myname
, "Specify logfile path with -Logfile", NULL
);
514 if (strlen(*++argv
) > PATH_MAX
)
515 Panic(1, "-Logfile name too long. (max. %d char)", PATH_MAX
);
517 free(screenlogfile
); /* we already set it up while starting */
518 screenlogfile
= SaveStr(*argv
);
521 } else if (!strcmp(ap
, "L"))
522 nwin_options
.Lflag
= 1;
528 case 'O': /* to be (or not to be?) deleted. jw. */
533 exit_with_usage(myname
, "Specify terminal-type with -T", NULL
);
534 if (strlen(*++argv
) < MAXTERMLEN
) {
535 strncpy(screenterm
, *argv
, MAXTERMLEN
);
536 screenterm
[MAXTERMLEN
] = '\0';
538 Panic(0, "-T: terminal name too long. (max. %d char)", MAXTERMLEN
);
539 nwin_options
.term
= screenterm
;
551 if (argc
> 1 && *argv
[1] != '-' && !SocketMatch
) {
552 SocketMatch
= *++argv
;
559 rflag
+= (*ap
== 'R') ? 2 : 1;
568 if (*argv
[1] != '-' && !SocketMatch
) {
569 SocketMatch
= *++argv
;
576 exit_with_usage(myname
, "Specify shell with -s", NULL
);
579 ShellProg
= SaveStr(*++argv
);
584 exit_with_usage(myname
, "Specify session-name with -S", NULL
);
585 SocketMatch
= *++argv
;
588 exit_with_usage(myname
, "Empty session-name?", NULL
);
589 if (strlen(SocketMatch
) > 80)
590 exit_with_usage(myname
, "Session-name is too long (max length is 80 symbols)", NULL
);
596 printf("Screen version %s\n", version
);
599 nwin_options
.encoding
= UTF8
;
602 exit_with_usage(myname
, "Unknown option %s", --ap
);
609 xsignal(SIGSEGV
, CoreDump
);
611 setlocale(LC_ALL
, "");
612 if (nwin_options
.encoding
== -1) {
613 /* ask locale if we should start in UTF-8 mode */
614 #ifdef HAVE_LANGINFO_H
615 nwin_options
.encoding
= FindEncoding(nl_langinfo(CODESET
));
618 if ((s
= locale_name()) && strstr(s
, "UTF-8"))
619 nwin_options
.encoding
= UTF8
;
624 if ((s
= locale_name())) {
625 if (!strncmp(s
, "zh_", 3) || !strncmp(s
, "ja_", 3) || !strncmp(s
, "ko_", 3)) {
630 if (nwin_options
.aka
) {
631 if (nwin_options
.encoding
> 0) {
632 size_t len
= strlen(nwin_options
.aka
);
634 char *newbuf
= malloc(3 * len
);
636 Panic(0, "%s", strnomem
);
637 newsz
= RecodeBuf((unsigned char *)nwin_options
.aka
, len
,
638 nwin_options
.encoding
, 0, (unsigned char *)newbuf
);
639 newbuf
[newsz
] = '\0';
640 nwin_options
.aka
= newbuf
;
642 /* If we just use the original value from av,
643 subsequent shelltitle invocations will attempt to free
644 space we don't own... */
645 nwin_options
.aka
= SaveStr(nwin_options
.aka
);
649 if (SocketMatch
&& strlen(SocketMatch
) >= MAXSTR
)
650 Panic(0, "Ridiculously long socketname - try again.");
651 if (cmdflag
&& !rflag
&& !dflag
&& !xflag
)
653 if (!cmdflag
&& dflag
&& mflag
&& !(rflag
|| xflag
))
656 nwin
.encoding
= nwin_undef
.encoding
; /* let screenrc overwrite it */
664 sh
= getenv("SHELL");
665 ShellProg
= SaveStr(sh
? sh
: DefaultShell
);
667 ShellArgs
[0] = ShellProg
;
668 home
= getenv("HOME");
669 if (!mflag
&& !SocketMatch
) {
671 if (sty
&& *sty
== 0)
675 own_uid
= multi_uid
= real_uid
;
676 if (SocketMatch
&& (sockp
= strchr(SocketMatch
, '/'))) {
679 SocketMatch
= sockp
+ 1;
682 if ((mppp
= getpwnam(multi
)) == NULL
)
683 Panic(0, "Cannot identify account '%s'.", multi
);
684 multi_uid
= mppp
->pw_uid
;
685 multi_home
= SaveStr(mppp
->pw_dir
);
686 if (strlen(multi_home
) > MAXPATHLEN
- 10)
687 Panic(0, "home directory path too long");
688 /* always fake multi attach mode */
694 /* Special case: effective user is multiuser. */
695 if (eff_uid
&& (multi_uid
!= eff_uid
))
696 Panic(0, "Must run suid root for multiuser support.");
698 if (SocketMatch
&& *SocketMatch
== 0)
701 if ((LoginName
= getlogin()) != NULL
) {
702 if ((ppp
= getpwnam(LoginName
)) != NULL
)
703 if (ppp
->pw_uid
!= real_uid
)
707 if ((ppp
= getpwuid(real_uid
)) == NULL
) {
708 Panic(0, "getpwuid() can't identify your account!");
711 LoginName
= ppp
->pw_name
;
713 LoginName
= SaveStr(LoginName
);
715 ppp
= getpwbyname(LoginName
, ppp
);
717 #if !defined(SOCKET_DIR)
718 if (multi
&& !multiattach
) {
719 if (home
&& strcmp(home
, ppp
->pw_dir
))
720 Panic(0, "$HOME must match passwd entry for multiuser screens.");
724 #define SET_GUID() do \
726 if (setgid(real_gid)) \
727 Panic(0, "setgid"); \
728 if (setuid(real_uid)) \
729 Panic(0, "setuid"); \
730 eff_uid = real_uid; \
731 eff_gid = real_gid; \
734 if (home
== NULL
|| *home
== '\0')
736 if (strlen(LoginName
) > MAXLOGINLEN
)
737 Panic(0, "LoginName too long - sorry.");
738 if (multi
&& strlen(multi
) > MAXLOGINLEN
)
739 Panic(0, "Screen owner name too long - sorry.");
740 if (strlen(home
) > MAXPATHLEN
)
741 Panic(0, "$HOME too long - sorry.");
744 if (!detached
&& !lsflag
&& !cmdflag
&& !(dflag
&& !mflag
&& !rflag
&& !xflag
)
745 && !(sty
&& !SocketMatch
&& !mflag
&& !rflag
&& !xflag
)) {
748 /* ttyname implies isatty */
749 SetTtyname(true, &st
);
750 tty_mode
= (int)st
.st_mode
& 0777;
752 fl
= fcntl(0, F_GETFL
, 0);
753 if (fl
!= -1 && (fl
& (O_RDWR
| O_RDONLY
| O_WRONLY
)) == O_RDWR
)
756 if (attach_fd
== -1) {
757 if ((n
= secopen(attach_tty
, O_RDWR
| O_NONBLOCK
, 0)) < 0)
758 Panic(0, "Cannot open your terminal '%s' - please check.", attach_tty
);
759 /* In case the pts device exists in another namespace we directly operate
760 * on the symbolic link itself. However, this means that we need to keep
761 * the fd open since we have no direct way of identifying the associated
762 * pts device accross namespaces. This is ok though since keeping fds open
763 * is done in the codebase already.
765 if (attach_tty_is_in_new_ns
)
771 if ((attach_term
= getenv("TERM")) == NULL
|| *attach_term
== 0)
772 Panic(0, "Please set a terminal type.");
773 if (strlen(attach_term
) > MAXTERMLEN
)
774 Panic(0, "$TERM too long - sorry.");
775 GetTTY(0, &attach_Mode
);
778 oumask
= umask(0); /* well, unsigned never fails? jw. */
780 SocketDir
= getenv("SCREENDIR");
782 if (strlen(SocketDir
) >= MAXPATHLEN
- 1)
783 Panic(0, "Ridiculously long $SCREENDIR - try again.");
785 Panic(0, "No $SCREENDIR with multi screens, please.");
789 sprintf(SocketPath
, "%s/.screen", multi_home
);
791 SocketDir
= SOCKET_DIR
;
792 sprintf(SocketPath
, "%s/S-%s", SocketDir
, multi
);
796 if (SocketDir
== NULL
) {
797 if (strlen(home
) > MAXPATHLEN
- 8 - 1)
798 Panic(0, "$HOME too long - sorry.");
799 sprintf(SocketPath
, "%s/.screen", home
);
800 SocketDir
= SocketPath
;
804 if (access(SocketDir
, F_OK
)) {
805 if (UserContext() > 0) {
806 if (mkdir(SocketDir
, 0700))
810 if (UserStatus() <= 0)
811 Panic(0, "Cannot make directory '%s'.", SocketDir
);
813 if (SocketDir
!= SocketPath
)
814 strncpy(SocketPath
, SocketDir
, ARRAY_SIZE(SocketPath
));
818 SocketDir
= SOCKET_DIR
;
819 if (stat(SocketDir
, &st
)) {
820 n
= (eff_uid
== 0 && (real_uid
|| eff_gid
== real_gid
)) ? 0755 :
821 (eff_gid
!= real_gid
) ? 0775 :
827 if (mkdir(SocketDir
, n
) == -1)
828 Panic(errno
, "Cannot make directory '%s'", SocketDir
);
830 if (!S_ISDIR(st
.st_mode
))
831 Panic(0, "'%s' must be a directory.", SocketDir
);
832 if (eff_uid
== 0 && real_uid
&& st
.st_uid
!= eff_uid
)
833 Panic(0, "Directory '%s' must be owned by root.", SocketDir
);
834 n
= (eff_uid
== 0 && (real_uid
|| (st
.st_mode
& 0775) != 0775)) ? 0755 :
835 (eff_gid
== st
.st_gid
&& eff_gid
!= real_gid
) ? 0775 : 0777;
836 if (((int)st
.st_mode
& 0777) != n
)
837 Panic(0, "Directory '%s' must have mode %03o.", SocketDir
, n
);
839 sprintf(SocketPath
, "%s/S-%s", SocketDir
, LoginName
);
840 if (access(SocketPath
, F_OK
)) {
841 if (mkdir(SocketPath
, 0700) == -1 && errno
!= EEXIST
)
842 Panic(errno
, "Cannot make directory '%s'", SocketPath
);
843 (void)chown(SocketPath
, real_uid
, real_gid
);
849 if (stat(SocketPath
, &st
) == -1)
850 Panic(errno
, "Cannot access %s", SocketPath
);
851 else if (!S_ISDIR(st
.st_mode
))
852 Panic(0, "%s is not a directory.", SocketPath
);
854 if (st
.st_uid
!= multi_uid
)
855 Panic(0, "%s is not the owner of %s.", multi
, SocketPath
);
857 #ifdef SOCKET_DIR /* if SOCKETDIR is not defined, the socket is in $HOME.
858 in that case it does not make sense to compare uids. */
859 if (st
.st_uid
!= real_uid
)
860 Panic(0, "You are not the owner of %s.", SocketPath
);
863 if ((st
.st_mode
& 0777) != 0700)
864 Panic(0, "Directory %s must have mode 700.", SocketPath
);
865 if (SocketMatch
&& strchr(SocketMatch
, '/'))
866 Panic(0, "Bad session name '%s'", SocketMatch
);
867 SocketName
= SocketPath
+ strlen(SocketPath
) + 1;
871 (void)gethostname(HostName
, MAXSTR
);
872 HostName
[MAXSTR
- 1] = '\0';
873 if ((ap
= strchr(HostName
, '.')) != NULL
)
880 real_uid
= multi_uid
;
882 i
= FindSocket((int *)NULL
, &fo
, &oth
, SocketMatch
);
887 exit(9 + (fo
|| oth
? 1 : 0) + fo
);
890 Panic(0, "No Sockets found in %s.\n", SocketPath
);
891 Msg(0, "%d Socket%s in %s.", fo
, fo
> 1 ? "s" : "", SocketPath
);
894 xsignal(SIG_BYE
, AttacherFinit
); /* prevent races */
896 /* attach_tty is not mandatory */
899 real_uid
= multi_uid
;
901 SetTtyname(false, &st
);
903 Panic(0, "Please specify a command.");
905 SendCmdMessage(sty
, SocketMatch
, argv
, queryflag
>= 0);
907 } else if (rflag
|| xflag
) {
908 if (Attach(MSG_ATTACH
)) {
913 Panic(0, "Can't create sessions of other users.");
914 } else if (dflag
&& !mflag
) {
915 SetTtyname(false, &st
);
917 Msg(0, "[%s %sdetached.]\n", SocketName
, (dflag
> 1 ? "power " : ""));
921 if (!SocketMatch
&& !mflag
&& sty
) {
922 /* attach_tty is not mandatory */
923 SetTtyname(false, &st
);
925 nwin_options
.args
= argv
;
926 SendCreateMsg(sty
, &nwin
);
930 nwin_compose(&nwin_default
, &nwin_options
, &nwin_default
);
932 if (!detached
|| dflag
!= 2)
939 Panic(errno
, "fork");
947 sprintf(socknamebuf
, "%d.%s", MasterPid
, SocketMatch
);
949 sprintf(socknamebuf
, "%d.%s.%s", MasterPid
, stripdev(attach_tty
), HostName
);
950 for (ap
= socknamebuf
; *ap
; ap
++)
953 if (strlen(socknamebuf
) > FILENAME_MAX
)
954 socknamebuf
[FILENAME_MAX
- 1] = 0;
955 snprintf(SocketPath
+ strlen(SocketPath
), sizeof(SocketPath
) - strlen(SocketPath
), "/%s", socknamebuf
);
962 PanicPid
= getppid();
964 if (DefaultEsc
== -1)
965 DefaultEsc
= Ctrl('a');
966 if (DefaultMetaEsc
== -1)
967 DefaultMetaEsc
= 'a';
969 ap
= av0
+ strlen(av0
) - 1;
971 if (!strncmp("screen", ap
, 6)) {
972 memcpy(ap
, "SCREEN", 6); /* name this process "SCREEN-BACKEND" */
981 if (attach_fd
== -1) {
982 if ((n
= secopen(attach_tty
, O_RDWR
| O_NONBLOCK
, 0)) < 0)
983 Panic(0, "Cannot reopen '%s' - please check.", attach_tty
);
990 * This guarantees that the session owner is listed, even when we
991 * start detached. From now on we should not refer to 'LoginName'
992 * any more, use users->u_name instead.
994 if (UserAdd(LoginName
, NULL
) < 0)
995 Panic(0, "Could not create user info");
997 if (MakeDisplay(LoginName
, attach_tty
, attach_term
, n
, getppid(), &attach_Mode
) == NULL
)
998 Panic(0, "Could not alloc display");
1000 D_encoding
= nwin_options
.encoding
> 0 ? nwin_options
.encoding
: 0;
1003 if (!freopen("/dev/null", "r", stdin
) ||
1004 !freopen("/dev/null", "w", stdout
) ||
1005 !freopen("/dev/null", "w", stderr
))
1006 Panic(0, "Cannot reassociate std streams");
1009 /* user started us with -S option */
1010 sprintf(socknamebuf
, "%d.%s", (int)getpid(), SocketMatch
);
1012 sprintf(socknamebuf
, "%d.%s.%s", (int)getpid(), stripdev(attach_tty
), HostName
);
1014 for (ap
= socknamebuf
; *ap
; ap
++)
1017 if (strlen(socknamebuf
) > FILENAME_MAX
) {
1018 socknamebuf
[FILENAME_MAX
] = 0;
1020 snprintf(SocketPath
+ strlen(SocketPath
), sizeof(SocketPath
) - strlen(SocketPath
), "/%s", socknamebuf
);
1022 ServerSocket
= MakeServerSocket();
1023 #ifdef SYSTEM_SCREENRC
1024 (void)StartRc(SYSTEM_SCREENRC
, 0);
1026 (void)StartRc(RcFileName
, 0);
1029 #endif /* ENABLE_UTMP */
1031 if (InitTermcap(0, 0)) {
1032 fcntl(D_userfd
, F_SETFL
, 0); /* Flush sets FNBLOCK */
1035 Kill(D_userpid
, SIG_BYE
);
1038 MakeDefaultCanvas();
1047 xsignal(SIGHUP
, SigHup
);
1048 xsignal(SIGINT
, FinitHandler
);
1049 xsignal(SIGQUIT
, FinitHandler
);
1050 xsignal(SIGTERM
, FinitHandler
);
1051 xsignal(SIGTTIN
, SIG_IGN
);
1052 xsignal(SIGTTOU
, SIG_IGN
);
1056 SetMode(&D_OldMode
, &D_NewMode
, D_flow
, iflag
);
1057 /* Note: SetMode must be called _before_ FinishRc. */
1058 SetTTY(D_userfd
, &D_NewMode
);
1059 if (fcntl(D_userfd
, F_SETFL
, FNBLOCK
))
1060 Msg(errno
, "Warning: NBLOCK fcntl failed");
1062 brktty(-1); /* just try */
1063 xsignal(SIGCHLD
, SigChld
);
1064 #ifdef SYSTEM_SCREENRC
1065 FinishRc(SYSTEM_SCREENRC
);
1067 FinishRc(RcFileName
);
1069 if (mru_window
== NULL
) {
1070 if (MakeWindow(&nwin
) == -1) {
1071 struct pollfd pfd
[1];
1074 pfd
[0].events
= POLLIN
;
1076 Msg(0, "Sorry, could not find a PTY or TTY.");
1077 /* allow user to exit early by pressing any key. */
1078 poll(pfd
, ARRAY_SIZE(pfd
), MsgWait
);
1082 } else if (argc
) { /* Screen was invoked with a command */
1086 if (display
&& default_startup
)
1088 xsignal(SIGINT
, SigInt
);
1089 if (rflag
&& (rflag
& 1) == 0 && !quietflag
) {
1090 Msg(0, "New screen...");
1094 serv_read
.type
= EV_READ
;
1095 serv_read
.fd
= ServerSocket
;
1096 serv_read
.handler
= serv_read_fn
;
1099 serv_select
.priority
= -10;
1100 serv_select
.type
= EV_ALWAYS
;
1101 serv_select
.handler
= serv_select_fn
;
1102 evenq(&serv_select
);
1104 logflushev
.type
= EV_TIMEOUT
;
1105 logflushev
.handler
= logflush_fn
;
1112 static void SigChldHandler(void)
1115 while (GotSigChld
) {
1119 if (stat(SocketPath
, &st
) == -1) {
1120 if (!RecoverSocket()) {
1126 static void SigChld(int sigsig
)
1128 (void)sigsig
; /* unused */
1132 void SigHup(int sigsig
)
1134 (void)sigsig
; /* unused */
1135 /* Hangup all displays */
1136 while ((display
= displays
))
1141 * the backend's Interrupt handler
1142 * we cannot insert the intrc directly, as we never know
1145 static void SigInt(int sigsig
)
1147 (void)sigsig
; /* unused */
1149 xsignal(SIGINT
, SigInt
);
1151 InterruptPlease
= 1;
1154 static void CoreDump(int sigsig
)
1156 /* if running with s-bit, we must reset the s-bit, so that we get a
1163 (void)sigsig
; /* unused */
1165 if (setgid(getgid()))
1167 if (setuid(getuid()))
1171 sprintf(buf
, "\r\n[screen caught a fatal signal. (core dumped)]\r\n");
1173 for (disp
= displays
; disp
; disp
= disp
->d_next
) {
1174 if (disp
->d_nonblock
< -1 || disp
->d_nonblock
> 1000000)
1176 fcntl(disp
->d_userfd
, F_SETFL
, 0);
1177 SetTTY(disp
->d_userfd
, &D_OldMode
);
1178 write(disp
->d_userfd
, buf
, strlen(buf
));
1179 Kill(disp
->d_userpid
, SIG_BYE
);
1185 static void DoWait(void)
1191 while ((pid
= waitpid(-1, &wstat
, WNOHANG
| WUNTRACED
)) > 0)
1193 for (win
= mru_window
; win
; win
= win
->w_prev_mru
) {
1194 if ((win
->w_pid
&& pid
== win
->w_pid
) || (win
->w_deadpid
&& pid
== win
->w_deadpid
)) {
1195 /* child has ceased to exist */
1198 if (WIFSTOPPED(wstat
)) {
1200 if (WSTOPSIG(wstat
) == SIGTTIN
) {
1201 Msg(0, "Suspended (tty input)");
1206 if (WSTOPSIG(wstat
) == SIGTTOU
) {
1207 Msg(0, "Suspended (tty output)");
1211 /* Try to restart process */
1212 Msg(0, "Child has been stopped, restarting.");
1213 if (killpg(pid
, SIGCONT
))
1216 /* Screen will detect the window has died when the window's
1217 * file descriptor signals EOF (which it will do when the process in
1218 * the window terminates). So do this in a timeout of 10 seconds.
1219 * (not doing this at all might also work)
1220 * See #27061 for more details.
1222 win
->w_destroyev
.data
= (char *)win
;
1223 win
->w_exitstatus
= wstat
;
1224 SetTimeout(&win
->w_destroyev
, 10 * 1000);
1225 evenq(&win
->w_destroyev
);
1229 if (win
->w_pwin
&& pid
== win
->w_pwin
->p_pid
) {
1237 static void FinitHandler(int sigsig
)
1239 (void)sigsig
; /* unused */
1246 xsignal(SIGCHLD
, SIG_DFL
);
1247 xsignal(SIGHUP
, SIG_IGN
);
1249 while (mru_window
) {
1250 Window
*p
= mru_window
;
1251 mru_window
= mru_window
->w_prev_mru
;
1254 if (ServerSocket
!= -1) {
1257 (void)unlink(SocketPath
);
1261 for (display
= displays
; display
; display
= display
->d_next
) {
1268 AddStr("[screen is terminating]\r\n");
1270 SetTTY(D_userfd
, &D_OldMode
);
1271 fcntl(D_userfd
, F_SETFL
, 0);
1273 Kill(D_userpid
, SIG_BYE
);
1276 * we _cannot_ call eexit(i) here,
1277 * instead of playing with the Socket above. Sigh.
1284 if (ServerSocket
!= -1) {
1285 if (setgid(real_gid
))
1286 AddStr("Failed to set gid\r\n");
1287 if (setuid(real_uid
))
1288 AddStr("Failed to set uid\r\n");
1289 if (unlink(SocketPath
))
1290 AddStr("Failed to remove socket\r\n");
1297 if (display
== NULL
)
1299 if (D_userfd
>= 0) {
1303 if (auto_detach
|| displays
->d_next
)
1310 * Detach now has the following modes:
1311 *D_DETACH SIG_BYE detach backend and exit attacher
1312 *D_HANGUP SIG_BYE detach backend and exit attacher
1313 *D_STOP SIG_STOP stop attacher (and detach backend)
1314 *D_REMOTE SIG_BYE remote detach -- reattach to new attacher
1315 *D_POWER SIG_POWER_BYE power detach -- attacher kills his parent
1316 *D_REMOTE_POWER SIG_POWER_BYE remote power detach -- both
1317 *D_LOCK SIG_LOCK lock the attacher
1319 * we always remove our utmp slots. (even when "lock" or "stop")
1320 * Note: Take extra care here, we may be called by interrupt!
1322 void Detach(int mode
)
1329 if (display
== NULL
)
1332 #define AddStrSocket(msg) do { \
1335 AddStr("[" msg " from "); \
1336 AddStr(SocketName); \
1340 AddStr("[" msg "]\r\n"); \
1343 xsignal(SIGHUP
, SIG_IGN
);
1354 AddStrSocket("detached");
1361 AddStrSocket("remote detached");
1365 AddStrSocket("power detached");
1366 if (PowDetachString
) {
1367 AddStr(PowDetachString
);
1370 sign
= SIG_POWER_BYE
;
1372 case D_REMOTE_POWER
:
1373 AddStrSocket("remote power detached");
1374 if (PowDetachString
) {
1375 AddStr(PowDetachString
);
1378 sign
= SIG_POWER_BYE
;
1382 ClearScrollbackBuffer();
1384 /* tell attacher to lock terminal with a lockprg. */
1388 if (displays
->d_next
== NULL
) {
1389 for (p
= mru_window
; p
; p
= p
->w_prev_mru
) {
1390 if (p
->w_slot
!= (slot_t
) - 1 && !(p
->w_lflag
& 2)) {
1393 * Set the slot to 0 to get the window
1396 p
->w_slot
= (slot_t
) 0;
1400 if (mode
!= D_HANGUP
)
1403 if (displays
->d_next
== NULL
&& console_window
) {
1404 if (TtyGrabConsole(console_window
->w_ptyfd
, false, "detach")) {
1405 KillWindow(console_window
);
1406 display
= displays
; /* restore display */
1410 ReleaseAutoWritelock(display
, D_fore
);
1411 D_user
->u_detachwin
= D_fore
->w_number
;
1412 D_user
->u_detachotherwin
= D_other
? D_other
->w_number
: D_fore
->w_number
;
1414 AutosaveLayout(D_layout
);
1415 layout_last
= D_layout
;
1416 for (cv
= D_cvlist
; cv
; cv
= cv
->c_next
) {
1417 p
= Layer2Window(cv
->c_layer
);
1418 SetCanvasWindow(cv
, NULL
);
1420 WindowChanged(p
, 'u');
1425 if (displays
== NULL
)
1426 /* Flag detached-ness */
1429 * tell father what to do. We do that after we
1430 * freed the tty, thus getty feels more comfortable on hpux
1431 * if it was a power detach.
1434 xsignal(SIGHUP
, SigHup
);
1438 static int IsSymbol(char *e
, char *s
)
1443 return strncmp(e
, s
, l
) == 0 && e
[l
] == '=';
1446 void MakeNewEnv(void)
1449 static char stybuf
[MAXSTR
];
1451 for (op
= environ
; *op
; ++op
) ;
1453 free((char *)NewEnv
);
1454 NewEnv
= np
= malloc((unsigned)(op
- environ
+ 7 + 1) * sizeof(char *));
1456 Panic(0, "%s", strnomem
);
1457 sprintf(stybuf
, "STY=%s", strlen(SocketName
) <= MAXSTR
- 5 ? SocketName
: "?");
1458 *np
++ = stybuf
; /* NewEnv[0] */
1459 *np
++ = Term
; /* NewEnv[1] */
1460 np
++; /* room for SHELL */
1461 np
+= 2; /* room for TERMCAP and WINDOW */
1463 for (op
= environ
; *op
; ++op
) {
1464 if (!IsSymbol(*op
, "TERM") && !IsSymbol(*op
, "TERMCAP")
1465 && !IsSymbol(*op
, "STY") && !IsSymbol(*op
, "WINDOW")
1466 && !IsSymbol(*op
, "SCREENCAP") && !IsSymbol(*op
, "SHELL")
1467 && !IsSymbol(*op
, "LINES") && !IsSymbol(*op
, "COLUMNS")
1474 #define PROCESS_MESSAGE(B) do { \
1477 va_start(ap, fmt); \
1478 (void)vsnprintf(p, sizeof(B) - 100, fmt, ap); \
1485 strncpy(p, strerror(err), B + sizeof(B) - p - 1); \
1486 B[sizeof(B) - 1] = 0; \
1490 void Msg(int err
, const char *fmt
, ...)
1492 char buf
[MAXPATHLEN
* 2];
1493 PROCESS_MESSAGE(buf
);
1495 if (display
&& displays
)
1497 else if (displays
) {
1498 for (display
= displays
; display
; display
= display
->d_next
)
1500 } else if (display
) {
1501 /* no displays but a display - must have forked.
1502 * send message to backend!
1504 char *tty
= D_usertty
;
1505 Display
*olddisplay
= display
;
1506 display
= NULL
; /* only send once */
1507 SendErrorMsg(tty
, buf
);
1508 display
= olddisplay
;
1510 printf("%s\r\n", buf
);
1513 write(queryflag
, buf
, strlen(buf
));
1517 * Call FinitTerm for all displays, write a message to each and call eexit();
1519 void Panic(int err
, const char *fmt
, ...)
1521 char buf
[MAXPATHLEN
* 2];
1522 PROCESS_MESSAGE(buf
);
1524 if (displays
== NULL
&& display
== NULL
) {
1525 printf("%s\r\n", buf
);
1527 Kill(PanicPid
, SIG_BYE
);
1528 } else if (displays
== NULL
) {
1529 /* no displays but a display - must have forked.
1530 * send message to backend!
1532 char *tty
= D_usertty
;
1534 SendErrorMsg(tty
, buf
);
1538 for (display
= displays
; display
; display
= display
->d_next
) {
1546 SetTTY(D_userfd
, &D_OldMode
);
1547 fcntl(D_userfd
, F_SETFL
, 0);
1548 write(D_userfd
, buf
, strlen(buf
));
1549 write(D_userfd
, "\n", 1);
1552 Kill(D_userpid
, SIG_BYE
);
1554 if (tty_oldmode
>= 0) {
1555 #if defined(HAVE_SETEUID)
1556 if (setuid(own_uid
))
1557 xseteuid(own_uid
); /* may be a loop. sigh. */
1561 chmod(attach_tty
, tty_oldmode
);
1566 void QueryMsg(int err
, const char *fmt
, ...)
1568 char buf
[MAXPATHLEN
* 2];
1573 PROCESS_MESSAGE(buf
);
1574 write(queryflag
, buf
, strlen(buf
));
1577 void Dummy(int err
, const char *fmt
, ...)
1579 (void)err
; /* unused */
1580 (void)fmt
; /* unused */
1583 #undef PROCESS_MESSAGE
1586 * '^' is allowed as an escape mechanism for control characters. jw.
1588 * Added time insertion using ideas/code from /\ndy Jones
1589 * (andy@lingua.cltr.uq.OZ.AU) - thanks a lot!
1593 void PutWinMsg(char *s
, int start
, int max
)
1598 struct mchar rendstack
[MAX_WINMSG_REND
];
1601 if (s
!= g_winmsg
->buf
) {
1602 /* sorry, no fancy coloring available */
1615 for (i
= 0; i
< g_winmsg
->numrend
&& max
> 0; i
++) {
1616 if (p
> g_winmsg
->rendpos
[i
] || g_winmsg
->rendpos
[i
] > l
)
1618 if (p
< g_winmsg
->rendpos
[i
]) {
1619 n
= g_winmsg
->rendpos
[i
] - p
;
1631 r
= g_winmsg
->rend
[i
];
1634 rend
= rendstack
[--rendstackn
];
1636 rendstack
[rendstackn
++] = rend
;
1637 ApplyAttrColor(r
, &rend
);
1639 SetRendition(&rend
);
1654 static void serv_read_fn(Event
*event
, void *data
)
1656 (void)event
; /* unused */
1657 (void)data
; /* unused */
1662 static void serv_select_fn(Event
*event
, void *data
)
1666 (void)event
; /* unused */
1667 (void)data
; /* unused */
1669 /* XXX: messages?? */
1673 if (InterruptPlease
) {
1674 /* This approach is rather questionable in a multi-display
1676 if (fore
&& displays
) {
1677 char ibuf
= displays
->d_OldMode
.tio
.c_cc
[VINTR
];
1678 write(W_UWP(fore
) ? fore
->w_pwin
->p_ptyfd
: fore
->w_ptyfd
, &ibuf
, 1);
1680 InterruptPlease
= 0;
1683 for (p
= mru_window
; p
; p
= p
->w_prev_mru
) {
1684 if (p
->w_bell
== BELL_FOUND
|| p
->w_bell
== BELL_VISUAL
) {
1686 int visual
= p
->w_bell
== BELL_VISUAL
|| visual_bell
;
1687 p
->w_bell
= BELL_ON
;
1688 for (display
= displays
; display
; display
= display
->d_next
) {
1689 for (cv
= D_cvlist
; cv
; cv
= cv
->c_next
)
1690 if (cv
->c_layer
->l_bottom
== &p
->w_layer
)
1693 p
->w_bell
= BELL_DONE
;
1694 Msg(0, "%s", MakeWinMsg(BellString
, p
, '%'));
1695 } else if (visual
&& !D_VB
&& (!D_status
|| !D_status_bell
)) {
1696 Msg(0, "%s", VisualBellString
);
1699 SetTimeout(&D_statusev
, VBellWait
);
1703 /* don't annoy the user with two messages */
1704 if (p
->w_monitor
== MON_FOUND
)
1705 p
->w_monitor
= MON_DONE
;
1706 WindowChanged(p
, WINESC_WFLAGS
);
1708 if (p
->w_monitor
== MON_FOUND
) {
1710 p
->w_monitor
= MON_ON
;
1711 for (display
= displays
; display
; display
= display
->d_next
) {
1712 for (cv
= D_cvlist
; cv
; cv
= cv
->c_next
)
1713 if (cv
->c_layer
->l_bottom
== &p
->w_layer
)
1716 continue; /* user already sees window */
1717 if (!(ACLBYTE(p
->w_mon_notify
, D_user
->u_id
) & ACLBIT(D_user
->u_id
)))
1718 continue; /* user doesn't care */
1719 Msg(0, "%s", MakeWinMsg(ActivityString
, p
, '%'));
1720 p
->w_monitor
= MON_DONE
;
1722 WindowChanged(p
, WINESC_WFLAGS
);
1724 if (p
->w_silence
== SILENCE_FOUND
) {
1725 /* Unset the flag if the user switched to this window. */
1726 if (p
->w_layer
.l_cvlist
) {
1727 p
->w_silence
= SILENCE_ON
;
1728 WindowChanged(p
, WINESC_WFLAGS
);
1733 for (display
= displays
; display
; display
= display
->d_next
) {
1735 if (D_status
== STATUS_ON_WIN
)
1737 /* XXX: should use display functions! */
1738 for (cv
= D_cvlist
; cv
; cv
= cv
->c_next
) {
1741 /* normalize window, see resize.c */
1742 lx
= cv
->c_layer
->l_x
;
1743 ly
= cv
->c_layer
->l_y
;
1744 if (lx
== cv
->c_layer
->l_width
)
1746 if (ly
+ cv
->c_yoff
< cv
->c_ys
) {
1747 int i
, n
= cv
->c_ys
- (ly
+ cv
->c_yoff
);
1748 cv
->c_yoff
= cv
->c_ys
- ly
;
1749 RethinkViewportOffsets(cv
);
1750 if (n
> cv
->c_layer
->l_height
)
1751 n
= cv
->c_layer
->l_height
;
1753 LScrollV(flayer
, -n
, 0, flayer
->l_height
- 1, 0);
1754 LayRedisplayLine(-1, -1, -1, 1); for (i
= 0; i
< n
; i
++)
1755 LayRedisplayLine(i
, 0, flayer
->l_width
- 1, 1);
1756 if (cv
== cv
->c_display
->d_forecv
)
1758 } else if (ly
+ cv
->c_yoff
> cv
->c_ye
) {
1759 int i
, n
= ly
+ cv
->c_yoff
- cv
->c_ye
;
1760 cv
->c_yoff
= cv
->c_ye
- ly
;
1761 RethinkViewportOffsets(cv
);
1762 if (n
> cv
->c_layer
->l_height
)
1763 n
= cv
->c_layer
->l_height
;
1765 LScrollV(flayer
, n
, 0, cv
->c_layer
->l_height
- 1, 0);
1766 LayRedisplayLine(-1, -1, -1, 1); for (i
= 0; i
< n
; i
++)
1767 LayRedisplayLine(i
+ flayer
->l_height
- n
, 0, flayer
->l_width
- 1, 1);
1768 if (cv
== cv
->c_display
->d_forecv
)
1771 if (lx
+ cv
->c_xoff
< cv
->c_xs
) {
1772 int i
, n
= cv
->c_xs
- (lx
+ cv
->c_xoff
);
1773 if (n
< (cv
->c_xe
- cv
->c_xs
+ 1) / 2)
1774 n
= (cv
->c_xe
- cv
->c_xs
+ 1) / 2;
1775 if (cv
->c_xoff
+ n
> cv
->c_xs
)
1776 n
= cv
->c_xs
- cv
->c_xoff
;
1778 RethinkViewportOffsets(cv
);
1779 if (n
> cv
->c_layer
->l_width
)
1780 n
= cv
->c_layer
->l_width
;
1781 CV_CALL(cv
, LayRedisplayLine(-1, -1, -1, 1); for (i
= 0; i
< flayer
->l_height
; i
++) {
1782 LScrollH(flayer
, -n
, i
, 0, flayer
->l_width
- 1, 0, NULL
);
1783 LayRedisplayLine(i
, 0, n
- 1, 1);}
1784 if (cv
== cv
->c_display
->d_forecv
)
1786 } else if (lx
+ cv
->c_xoff
> cv
->c_xe
) {
1787 int i
, n
= lx
+ cv
->c_xoff
- cv
->c_xe
;
1788 if (n
< (cv
->c_xe
- cv
->c_xs
+ 1) / 2)
1789 n
= (cv
->c_xe
- cv
->c_xs
+ 1) / 2;
1790 if (cv
->c_xoff
- n
+ cv
->c_layer
->l_width
- 1 < cv
->c_xe
)
1791 n
= cv
->c_xoff
+ cv
->c_layer
->l_width
- 1 - cv
->c_xe
;
1793 RethinkViewportOffsets(cv
);
1794 if (n
> cv
->c_layer
->l_width
)
1795 n
= cv
->c_layer
->l_width
;
1796 CV_CALL(cv
, LayRedisplayLine(-1, -1, -1, 1); for (i
= 0; i
< flayer
->l_height
; i
++) {
1797 LScrollH(flayer
, n
, i
, 0, flayer
->l_width
- 1, 0, NULL
);
1798 LayRedisplayLine(i
, flayer
->l_width
- n
, flayer
->l_width
- 1, 1);}
1799 if (cv
== cv
->c_display
->d_forecv
)
1805 for (display
= displays
; display
; display
= display
->d_next
) {
1806 if (D_status
== STATUS_ON_WIN
|| D_cvlist
== NULL
|| D_cvlist
->c_next
== NULL
)
1808 CV_CALL(D_forecv
, LayRestore();
1813 static void logflush_fn(Event
*event
, void *data
)
1819 (void)data
; /* unused */
1821 if (!islogfile(NULL
))
1822 return; /* no more logfiles */
1824 n
= log_flush
? log_flush
: (logtstamp_after
+ 4) / 5;
1826 SetTimeout(event
, n
* 1000);
1827 evenq(event
); /* re-enqueue ourself */
1831 /* write fancy time-stamp */
1832 for (p
= mru_window
; p
; p
= p
->w_prev_mru
) {
1835 p
->w_logsilence
+= n
;
1836 if (p
->w_logsilence
< logtstamp_after
)
1838 if (p
->w_logsilence
- n
>= logtstamp_after
)
1840 buf
= MakeWinMsg(logtstamp_string
, p
, '%');
1841 logfwrite(p
->w_log
, buf
, strlen(buf
));
1846 * Interprets ^?, ^@ and other ^-control-char notation.
1847 * Interprets \ddd octal notation
1849 * The result is placed in *cp, p is advanced behind the parsed expression and
1852 static char *ParseChar(char *p
, char *cp
)
1856 if (*p
== '^' && p
[1]) {
1864 } else if (*p
== '\\' && *++p
<= '7' && *p
>= '0') {
1867 *cp
= *cp
* 8 + *p
- '0';
1868 while (*++p
<= '7' && *p
>= '0');
1874 static int ParseEscape(char *p
)
1876 unsigned char buf
[2];
1879 SetEscape(NULL
, -1, -1);
1881 if ((p
= ParseChar(p
, (char *)buf
)) == NULL
|| (p
= ParseChar(p
, (char *)buf
+ 1)) == NULL
|| *p
)
1883 SetEscape(NULL
, buf
[0], buf
[1]);
1888 static void SetTtyname(bool fatal
, struct stat
*st
)
1891 int saved_errno
= 0;
1893 attach_tty_is_in_new_ns
= false;
1894 memset(&attach_tty_name_in_ns
, 0, ARRAY_SIZE(attach_tty_name_in_ns
));
1897 attach_tty
= ttyname(0);
1899 if (errno
== ENODEV
) {
1900 saved_errno
= errno
;
1901 attach_tty
= "/proc/self/fd/0";
1902 attach_tty_is_in_new_ns
= true;
1903 ret
= readlink(attach_tty
, attach_tty_name_in_ns
, ARRAY_SIZE(attach_tty_name_in_ns
));
1904 if (ret
< 0 || (size_t)ret
>= ARRAY_SIZE(attach_tty_name_in_ns
))
1905 Panic(0, "Bad tty '%s'", attach_tty
);
1907 Panic(0, "Must be connected to a terminal.");
1913 if (attach_tty
&& strcmp(attach_tty
, "")) {
1914 if (stat(attach_tty
, st
))
1915 Panic(errno
, "Cannot access '%s'", attach_tty
);
1917 if (strlen(attach_tty
) >= MAXPATHLEN
)
1918 Panic(0, "TtyName too long - sorry.");
1920 /* Only call CheckTtyname() if the device does not exist in
1921 * another namespace.
1923 if (saved_errno
!= ENODEV
&& CheckTtyname(attach_tty
))
1924 Panic(0, "Bad tty '%s'", attach_tty
);