1 /* Copyright (c) 2010
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 #ifdef HAVE_BRAILLE
14 * Modified by:
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
18 *
19 * Modifications Copyright (c) 1995 by
20 * Science Access Project, Oregon State University.
21 #endif
22 *
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)
26 * any later version.
27 *
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.
32 *
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 * https://www.gnu.org/licenses/, or contact Free Software Foundation, Inc.,
36 * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
37 *
38 ****************************************************************
39 */
40
41 #include <sys/types.h>
42 #ifdef _AIX
43 #include <sys/socket.h>
44 #endif
45 #include <ctype.h>
46 #include <stdbool.h>
47 #include <string.h>
48 #include <unistd.h>
49 #include <fcntl.h>
50
51 #if defined(__sun)
52 # include <limits.h>
53 #endif
54
55 #ifdef sgi
56 # include <sys/sysmacros.h>
57 #endif
58
59 #include <sys/stat.h>
60 #ifndef sun
61 # include <sys/ioctl.h>
62 #endif
63
64 #ifndef SIGINT
65 # include <signal.h>
66 #endif
67
68 #include "config.h"
69
70 #ifdef HAVE_STROPTS_H
71 # include <sys/stropts.h>
72 #endif
73
74 #if defined(SYSV) && !defined(ISC)
75 # include <sys/utsname.h>
76 #endif
77
78 #if defined(sequent) || defined(SVR4)
79 # include <sys/resource.h>
80 #endif /* sequent || SVR4 */
81
82 #ifdef ISC
83 # include <sys/tty.h>
84 # include <sys/sioctl.h>
85 # include <sys/pty.h>
86 #endif /* ISC */
87
88 #if (defined(AUX) || defined(_AUX_SOURCE)) && defined(POSIX)
89 # include <compat.h>
90 #endif
91 #if defined(USE_LOCALE) || defined(ENCODINGS)
92 # include <locale.h>
93 #endif
94 #if defined(HAVE_NL_LANGINFO) && defined(ENCODINGS)
95 # include <langinfo.h>
96 #endif
97
98 #include "screen.h"
99 #ifdef HAVE_BRAILLE
100 # include "braille.h"
101 #endif
102
103 #include "patchlevel.h"
104
105 /*
106 * At the moment we only need the real password if the
107 * builtin lock is used. Therefore disable SHADOWPW if
108 * we do not really need it (kind of security thing).
109 */
110 #ifndef LOCK
111 # undef SHADOWPW
112 #endif
113
114 #include <pwd.h>
115 #ifdef SHADOWPW
116 # include <shadow.h>
117 #endif /* SHADOWPW */
118
119 #include "logfile.h" /* islogfile, logfflush, logfopen/logfclose */
120
121 #ifdef DEBUG
122 FILE *dfp;
123 #endif
124
125
126 extern char Term[], screenterm[], **environ, Termcap[];
127 int force_vt = 1;
128 int VBellWait, MsgWait, MsgMinWait, SilenceWait;
129
130 extern struct acluser *users;
131 extern struct display *displays, *display;
132 extern struct LayFuncs MarkLf;
133
134 extern int visual_bell;
135 #ifdef COPY_PASTE
136 extern unsigned char mark_key_tab[];
137 #endif
138 extern char version[];
139 extern char DefaultShell[];
140 #ifdef ZMODEM
141 extern char *zmodem_sendcmd;
142 extern char *zmodem_recvcmd;
143 #endif
144 extern struct layout *layout_last;
145
146 char *ShellProg;
147 char *ShellArgs[2];
148
149 extern struct NewWindow nwin_undef, nwin_default, nwin_options;
150 struct backtick;
151
152 static struct passwd *getpwbyname __P((char *, struct passwd *));
153 static void SigChldHandler __P((void));
154 static sigret_t SigChld __P(SIGPROTOARG);
155 static sigret_t SigInt __P(SIGPROTOARG);
156 static sigret_t CoreDump __P(SIGPROTOARG);
157 static sigret_t FinitHandler __P(SIGPROTOARG);
158 static void DoWait __P((void));
159 static void serv_read_fn __P((struct event *, char *));
160 static void serv_select_fn __P((struct event *, char *));
161 static void logflush_fn __P((struct event *, char *));
162 static void backtick_filter __P((struct backtick *));
163 static void backtick_fn __P((struct event *, char *));
164 static char *runbacktick __P((struct backtick *, int *, time_t));
165 static int IsSymbol __P((char *, char *));
166 static char *ParseChar __P((char *, char *));
167 static int ParseEscape __P((char *));
168 static char *pad_expand __P((char *, char *, int, int));
169 static void SetTtyname __P((bool, struct stat *));
170 #ifdef DEBUG
171 static void fds __P((void));
172 #endif
173
174 int nversion; /* numerical version, used for secondary DA */
175
176 /* the attacher */
177 struct passwd *ppp;
178 char *attach_tty;
179 /* Indicator whether the current tty exists in another namespace. */
180 bool attach_tty_is_in_new_ns = false;
181 /* Content of the tty symlink when attach_tty_is_in_new_ns == true. */
182 char attach_tty_name_in_ns[MAXPATHLEN];
183 int attach_fd = -1;
184 char *attach_term;
185 char *LoginName;
186 struct mode attach_Mode;
187
188 char SockPath[MAXPATHLEN + 2 * MAXSTR];
189 char *SockName; /* SockName is pointer in SockPath */
190 char *SockMatch = NULL; /* session id command line argument */
191 int ServerSocket = -1;
192 struct event serv_read;
193 struct event serv_select;
194 struct event logflushev;
195
196 char **NewEnv = NULL;
197
198 char *RcFileName = NULL;
199 char *home;
200
201 char *screenlogfile; /* filename layout */
202 int log_flush = 10; /* flush interval in seconds */
203 int logtstamp_on = 0; /* tstamp disabled */
204 char *logtstamp_string; /* stamp layout */
205 int logtstamp_after = 120; /* first tstamp after 120s */
206 char *hardcopydir = NULL;
207 char *BellString;
208 char *VisualBellString;
209 char *ActivityString;
210 #ifdef COPY_PASTE
211 char *BufferFile;
212 #endif
213 #ifdef POW_DETACH
214 char *PowDetachString;
215 #endif
216 char *hstatusstring;
217 char *captionstring;
218 char *timestring;
219 char *wliststr;
220 char *wlisttit;
221 int auto_detach = 1;
222 int iflag, rflag, dflag, lsflag, quietflag, wipeflag, xflag;
223 int cmdflag;
224 int queryflag = -1;
225 int adaptflag;
226
227 #ifdef MULTIUSER
228 char *multi;
229 char *multi_home;
230 int multi_uid;
231 int own_uid;
232 int multiattach;
233 int tty_mode;
234 int tty_oldmode = -1;
235 #endif
236
237 char HostName[MAXSTR];
238 int MasterPid, PanicPid;
239 int real_uid, real_gid, eff_uid, eff_gid;
240 int default_startup;
241 int ZombieKey_destroy, ZombieKey_resurrect, ZombieKey_onerror;
242 char *preselect = NULL; /* only used in Attach() */
243
244 #ifdef UTF8
245 char *screenencodings;
246 #endif
247
248 #ifdef DW_CHARS
249 int cjkwidth;
250 #endif
251
252 #ifdef NETHACK
253 int nethackflag = 0;
254 #endif
255 int maxwin;
256
257 struct layer *flayer;
258 struct win *fore;
259 struct win *windows;
260 struct win *console_window;
261
262 #ifdef BUILTIN_TELNET
263 int af;
264 #endif
265
266 /*
267 * Do this last
268 */
269 #include "extern.h"
270
271 char strnomem[] = "Out of memory.";
272
273 static int InterruptPlease;
274 static int GotSigChld;
275
lf_secreopen(name,wantfd,l)276 static int lf_secreopen(name, wantfd, l)
277 char *name;
278 int wantfd;
279
280 struct logfile *l;
281 {
282 int got_fd;
283 close(wantfd);
284 if (((got_fd = secopen(name, O_WRONLY | O_CREAT | O_APPEND, 0666)) < 0) || lf_move_fd(got_fd, wantfd) < 0) {
285 logfclose(l);
286 debug1("lf_secreopen: failed for %s\n", name);
287 return -1;
288 }
289 l->st->st_ino = l->st->st_dev = 0;
290 debug2("lf_secreopen: %d = %s\n", wantfd, name);
291 return 0;
292 }
293
294 /********************************************************************/
295 /********************************************************************/
296 /********************************************************************/
297
getpwbyname(name,ppp)298 static struct passwd * getpwbyname(name, ppp)
299 char *name;
300
301 struct passwd *ppp;
302 {
303 int n;
304 #ifdef SHADOWPW
305 struct spwd *sss = NULL;
306 static char *spw = NULL;
307 #endif
308
309 if (!ppp && !(ppp = getpwnam(name)))
310 return NULL;
311
312 /* Do password sanity check..., allow ##user for SUN_C2 security */
313 #ifdef SHADOWPW
314 pw_try_again:
315 #endif
316 n = 0;
317 if (ppp->pw_passwd[0] == '#' && ppp->pw_passwd[1] == '#' && strcmp(ppp->pw_passwd + 2, ppp->pw_name) == 0)
318 n = 13;
319 for (; n < 13; n++) {
320 char c = ppp->pw_passwd[n];
321 if (!(c == '.' || c == '/' || c == '$' ||
322 (c >= '0' && c <= '9') ||
323 (c >= 'a' && c <= 'z') ||
324 (c >= 'A' && c <= 'Z')))
325 break;
326 }
327
328 #ifdef SHADOWPW
329 /* try to determine real password */
330 if (n < 13 && sss == 0) {
331 sss = getspnam(ppp->pw_name);
332 if (sss) {
333 if (spw)
334 free(spw);
335 ppp->pw_passwd = spw = SaveStr(sss->sp_pwdp);
336 endspent(); /* this should delete all buffers ... */
337 goto pw_try_again;
338 }
339 endspent(); /* this should delete all buffers ... */
340 }
341 #endif
342 if (n < 13)
343 ppp->pw_passwd = 0;
344 #ifdef linux
345 if (ppp->pw_passwd && strlen(ppp->pw_passwd) == 13 + 11)
346 ppp->pw_passwd[13] = 0; /* beware of linux's long passwords */
347 #endif
348
349 return ppp;
350 }
351
locale_name(void)352 static char *locale_name(void)
353 {
354 static char *s;
355 if (!s) {
356 s = getenv("LC_ALL");
357 if (s == NULL)
358 s = getenv("LC_CTYPE");
359 if (s == NULL)
360 s = getenv("LANG");
361 }
362 return s;
363 }
364
main(int ac,char ** av)365 int main(int ac, char** av)
366 {
367 register int n;
368 char *ap;
369 char *av0;
370 char socknamebuf[2 * MAXSTR];
371 int mflag = 0;
372 char *myname = (ac == 0) ? "screen" : av[0];
373 char *SockDir;
374 struct stat st;
375 #ifdef _MODE_T /* (jw) */
376 mode_t oumask;
377 #else
378 int oumask;
379 #endif
380 #if defined(SYSV) && !defined(ISC)
381 struct utsname utsnam;
382 #endif
383 struct NewWindow nwin;
384 int detached = 0; /* start up detached */
385 #ifdef MULTIUSER
386 char *sockp;
387 #endif
388 char *sty = 0;
389
390 #if (defined(AUX) || defined(_AUX_SOURCE)) && defined(POSIX)
391 setcompat(COMPAT_POSIX|COMPAT_BSDPROT); /* turn on seteuid support */
392 #endif
393 #if defined(sun) && defined(SVR4)
394 {
395 /* Solaris' login blocks SIGHUP! This is _very bad_ */
396 sigset_t sset;
397 sigemptyset(&sset);
398 sigprocmask(SIG_SETMASK, &sset, 0);
399 }
400 #endif
401
402 /*
403 * First, close all unused descriptors
404 * (otherwise, we might have problems with the select() call)
405 */
406 closeallfiles(0);
407 #ifdef DEBUG
408 opendebug(1, 0);
409 #endif
410 snprintf(version, 59, "%d.%.2d.%.2d%s (%s%s) %s", REV, VERS,
411 PATCHLEVEL, STATE, ORIGIN, GIT_REV, DATE);
412 nversion = REV * 10000 + VERS * 100 + PATCHLEVEL;
413 debug2("-- screen debug started %s (%s)\n", *av, version);
414 #ifdef POSIX
415 debug("POSIX\n");
416 #endif
417 #ifdef TERMIO
418 debug("TERMIO\n");
419 #endif
420 #ifdef SYSV
421 debug("SYSV\n");
422 #endif
423 #ifdef SYSVSIGS
424 debug("SYSVSIGS\n");
425 #endif
426 #if defined(SIGWINCH) && defined(TIOCGWINSZ)
427 debug("Window size changing enabled\n");
428 #endif
429 #ifdef HAVE_SETREUID
430 debug("SETREUID\n");
431 #endif
432 #ifdef HAVE_SETEUID
433 debug("SETEUID\n");
434 #endif
435 #ifdef hpux
436 debug("hpux\n");
437 #endif
438 #ifdef USEBCOPY
439 debug("USEBCOPY\n");
440 #endif
441 #ifdef UTMPOK
442 debug("UTMPOK\n");
443 #endif
444 #ifdef LOADAV
445 debug("LOADAV\n");
446 #endif
447 #ifdef NETHACK
448 debug("NETHACK\n");
449 #endif
450 #ifdef TERMINFO
451 debug("TERMINFO\n");
452 #endif
453 #ifdef SHADOWPW
454 debug("SHADOWPW\n");
455 #endif
456 #ifdef NAME_MAX
457 debug1("NAME_MAX = %d\n", NAME_MAX);
458 #endif
459
460 BellString = SaveStr("Bell in window %n");
461 VisualBellString = SaveStr(" Wuff, Wuff!! ");
462 ActivityString = SaveStr("Activity in window %n");
463 screenlogfile = SaveStr("screenlog.%n");
464 logtstamp_string = SaveStr("-- %n:%t -- time-stamp -- %M/%d/%y %c:%s --\n");
465 hstatusstring = SaveStr("%h");
466 captionstring = SaveStr("%4n %t");
467 timestring = SaveStr("%c:%s %M %d %H%? %l%?");
468 wlisttit = SaveStr(" Num Name%=Flags");
469 wliststr = SaveStr("%4n %t%=%f");
470 #ifdef COPY_PASTE
471 BufferFile = SaveStr(DEFAULT_BUFFERFILE);
472 #endif
473 ShellProg = NULL;
474 #ifdef POW_DETACH
475 PowDetachString = 0;
476 #endif
477 default_startup = (ac > 1) ? 0 : 1;
478 adaptflag = 0;
479 VBellWait = VBELLWAIT * 1000;
480 MsgWait = MSGWAIT * 1000;
481 MsgMinWait = MSGMINWAIT * 1000;
482 SilenceWait = SILENCEWAIT;
483 #ifdef HAVE_BRAILLE
484 InitBraille();
485 #endif
486 #ifdef ZMODEM
487 zmodem_sendcmd = SaveStr("!!! sz -vv -b ");
488 zmodem_recvcmd = SaveStr("!!! rz -vv -b -E");
489 #endif
490
491 #ifdef COPY_PASTE
492 CompileKeys((char *)0, 0, mark_key_tab);
493 #endif
494 #ifdef UTF8
495 InitBuiltinTabs();
496 screenencodings = SaveStr(SCREENENCODINGS);
497 #endif
498 #ifdef DW_CHARS
499 cjkwidth = 0;
500 #endif
501 nwin = nwin_undef;
502 nwin_options = nwin_undef;
503 strncpy(screenterm, "screen", MAXTERMLEN);
504 screenterm[MAXTERMLEN] = '\0';
505 #ifdef BUILTIN_TELNET
506 af = AF_UNSPEC;
507 #endif
508
509 real_uid = getuid();
510 real_gid = getgid();
511 eff_uid = geteuid();
512 eff_gid = getegid();
513
514 logreopen_register(lf_secreopen);
515
516 av0 = *av; /* if this is a login screen, assume -RR */
517 if (*av0 == '-') {
518 rflag = 4;
519 #ifdef MULTI
520 xflag = 1;
521 #else
522 dflag = 1;
523 #endif
524 ShellProg = SaveStr(DefaultShell); /* to prevent nasty circles */
525 }
526
527 while (ac > 0){
528 ap = *++av;
529 if (--ac > 0 && *ap == '-') {
530 if (ap[1] == '-' && ap[2] == 0) {
531 av++;
532 ac--;
533 break;
534 }
535
536 if (ap[1] == '-' && !strcmp(ap, "--version")) {
537 printf("Screen version %s\n", version);
538 exit(0);
539 }
540 if (ap[1] == '-' && !strcmp(ap, "--help"))
541 exit_with_usage(myname, NULL, NULL);
542
543 while (ap && *ap && *++ap) {
544 switch (*ap) {
545
546 #ifdef BUILTIN_TELNET
547 case '4':
548 af = AF_INET;
549 break;
550
551 case '6':
552 af = AF_INET6;
553 break;
554 #endif
555
556 case 'a':
557 nwin_options.aflag = 1;
558 break;
559
560 case 'A':
561 adaptflag = 1;
562 break;
563
564 case 'p': /* preselect */
565 if (*++ap)
566 preselect = ap;
567 else {
568 if (!--ac)
569 exit_with_usage(myname, "Specify a window to preselect with -p", NULL);
570 preselect = *++av;
571 }
572 ap = NULL;
573 break;
574
575 #ifdef HAVE_BRAILLE
576 case 'B':
577 bd.bd_start_braille = 1;
578 break;
579 #endif
580
581 case 'c':
582 if (*++ap)
583 RcFileName = ap;
584 else {
585 if (--ac == 0)
586 exit_with_usage(myname, "Specify an alternate rc-filename with -c", NULL);
587 RcFileName = *++av;
588 }
589 ap = NULL;
590 break;
591
592 case 'e':
593 if (!*++ap) {
594 if (--ac == 0)
595 exit_with_usage(myname, "Specify command characters with -e", NULL);
596 ap = *++av;
597 }
598 if (ParseEscape(ap))
599 Panic(0, "Two characters are required with -e option, not '%s'.", ap);
600 ap = NULL;
601 break;
602
603 case 'f':
604 ap++;
605 switch (*ap++) {
606 case 'n':
607 case '0':
608 nwin_options.flowflag = FLOW_NOW * 0;
609 break;
610 case '\0':
611 ap--;
612 /* FALLTHROUGH */
613 case 'y':
614 case '1':
615 nwin_options.flowflag = FLOW_NOW * 1;
616 break;
617 case 'a':
618 nwin_options.flowflag = FLOW_AUTOFLAG;
619 break;
620 default:
621 exit_with_usage(myname, "Unknown flow option -%s", --ap);
622 }
623 break;
624
625 case 'h':
626 if (--ac == 0)
627 exit_with_usage(myname, NULL, NULL);
628 nwin_options.histheight = atoi(*++av);
629 if (nwin_options.histheight < 0)
630 exit_with_usage(myname, "-h: %s: negative scrollback size?", *av);
631 break;
632
633 case 'i':
634 iflag = 1;
635 break;
636
637 case 't': /* title, the former AkA == -k */
638 if (--ac == 0)
639 exit_with_usage(myname, "Specify a new window-name with -t", NULL);
640 nwin_options.aka = *++av;
641 break;
642
643 case 'l':
644 ap++;
645 switch (*ap++) {
646 case 'n':
647 case '0':
648 nwin_options.lflag = 0;
649 break;
650 case '\0':
651 ap--;
652 /* FALLTHROUGH */
653 case 'y':
654 case '1':
655 nwin_options.lflag = 1;
656 break;
657 case 'a':
658 nwin_options.lflag = 3;
659 break;
660 case 's': /* -ls */
661 case 'i': /* -list */
662 lsflag = 1;
663 if (ac > 1 && !SockMatch) {
664 SockMatch = *++av;
665 ac--;
666 }
667 ap = NULL;
668 break;
669 default:
670 exit_with_usage(myname, "%s: Unknown suboption to -l", --ap);
671 }
672 break;
673
674 case 'w':
675 if (strcmp(ap+1, "ipe"))
676 exit_with_usage(myname, "Unknown option %s", --ap);
677 lsflag = 1;
678 wipeflag = 1;
679 if (ac > 1 && !SockMatch) {
680 SockMatch = *++av;
681 ac--;
682 }
683 break;
684
685 case 'L':
686 if (!strcmp(ap + 1, "ogfile")) {
687 if (--ac == 0)
688 exit_with_usage(myname, "Specify logfile path with -Logfile", NULL);
689
690 if (strlen(*++av) > PATH_MAX)
691 Panic(1, "-Logfile name too long. (max. %d char)", PATH_MAX);
692
693 free(screenlogfile); /* we already set it up while starting */
694 screenlogfile = SaveStr(*av);
695
696 ap = NULL;
697 } else if (!strcmp(ap, "L"))
698 nwin_options.Lflag = 1;
699
700 break;
701
702 case 'm':
703 mflag = 1;
704 break;
705
706 case 'O': /* to be (or not to be?) deleted. jw. */
707 force_vt = 0;
708 break;
709
710 case 'T':
711 if (--ac == 0)
712 exit_with_usage(myname, "Specify terminal-type with -T", NULL);
713 if (strlen(*++av) < MAXTERMLEN) {
714 strncpy(screenterm, *av, MAXTERMLEN);
715 screenterm[MAXTERMLEN] = '\0';
716 } else
717 Panic(0, "-T: terminal name too long. (max. %d char)", MAXTERMLEN);
718 nwin_options.term = screenterm;
719 break;
720
721 case 'q':
722 quietflag = 1;
723 break;
724
725 case 'Q':
726 queryflag = 1;
727 cmdflag = 1;
728 break;
729
730 case 'r':
731 case 'R':
732 #ifdef MULTI
733 case 'x':
734 #endif
735 if (ac > 1 && *av[1] != '-' && !SockMatch) {
736 SockMatch = *++av;
737 ac--;
738 debug2("rflag=%d, SockMatch=%s\n", dflag, SockMatch);
739 }
740 #ifdef MULTI
741 if (*ap == 'x')
742 xflag = 1;
743 #endif
744 if (rflag)
745 rflag = 2;
746 rflag += (*ap == 'R') ? 2 : 1;
747 break;
748
749 #ifdef REMOTE_DETACH
750 case 'd':
751 dflag = 1;
752 /* FALLTHROUGH */
753
754 case 'D':
755 if (!dflag)
756 dflag = 2;
757 if (ac == 2) {
758 if (*av[1] != '-' && !SockMatch) {
759 SockMatch = *++av;
760 ac--;
761 debug2("dflag=%d, SockMatch=%s\n", dflag, SockMatch);
762 }
763 }
764 break;
765 #endif
766
767 case 's':
768 if (--ac == 0)
769 exit_with_usage(myname, "Specify shell with -s", NULL);
770 if (ShellProg)
771 free(ShellProg);
772 ShellProg = SaveStr(*++av);
773 debug1("ShellProg: '%s'\n", ShellProg);
774 break;
775
776 case 'S':
777 if (!SockMatch) {
778 if (--ac == 0)
779 exit_with_usage(myname, "Specify session-name with -S", NULL);
780 SockMatch = *++av;
781 }
782 if (!*SockMatch)
783 exit_with_usage(myname, "Empty session-name?", NULL);
784 break;
785
786 case 'X':
787 cmdflag = 1;
788 break;
789
790 case 'v':
791 printf("Screen version %s\n", version);
792 exit(0);
793
794 #ifdef UTF8
795 case 'U':
796 nwin_options.encoding = nwin_options.encoding == -1 ? UTF8 : 0;
797 break;
798 #endif
799
800 default:
801 exit_with_usage(myname, "Unknown option %s", --ap);
802 }
803 }
804 }
805 else
806 break;
807 }
808
809 #ifdef SIGBUS /* OOPS, linux has no bus errors! */
810 signal(SIGBUS, CoreDump);
811 #endif /* SIGBUS */
812 signal(SIGSEGV, CoreDump);
813
814
815 #ifdef USE_LOCALE
816 setlocale(LC_ALL, "");
817 #endif
818 #ifdef ENCODINGS
819 if (nwin_options.encoding == -1) {
820 /* ask locale if we should start in UTF-8 mode */
821 # ifdef HAVE_NL_LANGINFO
822 # ifndef USE_LOCALE
823 setlocale(LC_CTYPE, "");
824 # endif
825 nwin_options.encoding = FindEncoding(nl_langinfo(CODESET));
826 debug1("locale says encoding = %d\n", nwin_options.encoding);
827 # else
828 # ifdef UTF8
829 char *s;
830 if ((s = locale_name()) && InStr(s, "UTF-8"))
831 nwin_options.encoding = UTF8;
832 # endif
833 debug1("environment says encoding=%d\n", nwin_options.encoding);
834 #endif
835 }
836
837 # ifdef DW_CHARS
838 {
839 char *s;
840 if ((s = locale_name())) {
841 if(!strncmp(s, "zh_", 3) || !strncmp(s, "ja_", 3) || !strncmp(s, "ko_", 3)) {
842 cjkwidth = 1;
843 }
844 }
845 }
846 #endif
847 #endif
848
849 if (nwin_options.aka) {
850 #ifdef ENCODINGS
851 if (nwin_options.encoding > 0) {
852 size_t len = strlen(nwin_options.aka);
853 size_t newsz;
854 char *newbuf = malloc(3 * len);
855 if (!newbuf)
856 Panic(0, "%s", strnomem);
857 newsz = RecodeBuf((unsigned char *)nwin_options.aka, len,
858 nwin_options.encoding, 0, (unsigned char *)newbuf);
859 newbuf[newsz] = '\0';
860 nwin_options.aka = newbuf;
861 } else
862 #endif
863 {
864 /* If we just use the original value from av,
865 subsequent shelltitle invocations will attempt to free
866 space we don't own... */
867 nwin_options.aka = SaveStr(nwin_options.aka);
868 }
869 }
870
871 if (SockMatch && strlen(SockMatch) >= MAXSTR)
872 Panic(0, "Ridiculously long socketname - try again.");
873 if (cmdflag && !rflag && !dflag && !xflag)
874 xflag = 1;
875 if (!cmdflag && dflag && mflag && !(rflag || xflag))
876 detached = 1;
877 nwin = nwin_options;
878
879 #ifdef ENCODINGS
880 nwin.encoding = nwin_undef.encoding; /* let screenrc overwrite it */
881 #endif
882 if (ac)
883 nwin.args = av;
884
885 /* make the write() calls return -1 on all errors */
886
887 #ifdef SIGXFSZ
888 /*
889 * Ronald F. Guilmette, Oct 29 '94, bug-gnu-utils@prep.ai.mit.edu:
890 * It appears that in System V Release 4, UNIX, if you are writing
891 * an output file and you exceed the currently set file size limit,
892 * you _don't_ just get the call to `write' returning with a
893 * failure code. Rather, you get a signal called `SIGXFSZ' which,
894 * if neither handled nor ignored, will cause your program to crash
895 * with a core dump.
896 */
897 signal(SIGXFSZ, SIG_IGN);
898 #endif /* SIGXFSZ */
899
900 #ifdef SIGPIPE
901 signal(SIGPIPE, SIG_IGN);
902 #endif
903
904 if (!ShellProg) {
905 register char *sh;
906 sh = getenv("SHELL");
907 ShellProg = SaveStr(sh ? sh : DefaultShell);
908 }
909 ShellArgs[0] = ShellProg;
910 home = getenv("HOME");
911 if (!mflag && !SockMatch) {
912 sty = getenv("STY");
913 if (sty && *sty == 0)
914 sty = 0;
915 }
916
917 #ifdef NETHACK
918 if (!(nethackflag = (getenv("NETHACKOPTIONS") != NULL))) {
919 char nethackrc[MAXPATHLEN];
920
921 if (home && (strlen(home) < (MAXPATHLEN - 20))) {
922 sprintf(nethackrc,"%s/.nethackrc", home);
923 nethackflag = !access(nethackrc, F_OK);
924 }
925 }
926 #endif
927
928 #ifdef MULTIUSER
929 own_uid = multi_uid = real_uid;
930 if (SockMatch && (sockp = index(SockMatch, '/'))) {
931 *sockp = 0;
932 multi = SockMatch;
933 SockMatch = sockp + 1;
934 if (*multi) {
935 struct passwd *mppp;
936 if ((mppp = getpwnam(multi)) == (struct passwd *)0)
937 Panic(0, "Cannot identify account '%s'.", multi);
938 multi_uid = mppp->pw_uid;
939 multi_home = SaveStr(mppp->pw_dir);
940 if (strlen(multi_home) > MAXPATHLEN - 10)
941 Panic(0, "home directory path too long");
942
943 # ifdef MULTI
944 /* always fake multi attach mode */
945 if (rflag || lsflag)
946 xflag = 1;
947 # endif /* MULTI */
948 detached = 0;
949 multiattach = 1;
950 }
951 /* Special case: effective user is multiuser. */
952 if (eff_uid && (multi_uid != eff_uid))
953 Panic(0, "Must run suid root for multiuser support.");
954 }
955 if (SockMatch && *SockMatch == 0)
956 SockMatch = 0;
957 #endif /* MULTIUSER */
958
959 if ((LoginName = getlogin()) && LoginName[0] != '\0') {
960 if ((ppp = getpwnam(LoginName)) != (struct passwd *) 0)
961 if ((int)ppp->pw_uid != real_uid)
962 ppp = (struct passwd *) 0;
963 }
964 if (ppp == 0) {
965 if ((ppp = getpwuid(real_uid)) == 0) {
966 Panic(0, "getpwuid() can't identify your account!");
967 exit(1);
968 }
969 LoginName = ppp->pw_name;
970 }
971 LoginName = SaveStr(LoginName);
972 ppp = getpwbyname(LoginName, ppp);
973
974 #if !defined(SOCKDIR) && defined(MULTIUSER)
975 if (multi && !multiattach) {
976 if (home && strcmp(home, ppp->pw_dir))
977 Panic(0, "$HOME must match passwd entry for multiuser screens.");
978 }
979 #endif
980
981 #define SET_GUID() do \
982 { \
983 setgid(real_gid); \
984 setuid(real_uid); \
985 eff_uid = real_uid; \
986 eff_gid = real_gid; \
987 } while (0)
988
989 if (home == 0 || *home == '\0')
990 home = ppp->pw_dir;
991 if (strlen(LoginName) > MAXLOGINLEN)
992 Panic(0, "LoginName too long - sorry.");
993
994 #ifdef MULTIUSER
995 if (multi && strlen(multi) > MAXLOGINLEN)
996 Panic(0, "Screen owner name too long - sorry.");
997 #endif
998 if (strlen(home) > MAXPATHLEN - 25)
999 Panic(0, "$HOME too long - sorry.");
1000
1001 attach_tty = "";
1002 if (!detached && !lsflag && !cmdflag && !(dflag && !mflag && !rflag && !xflag) &&
1003 !(sty && !SockMatch && !mflag && !rflag && !xflag)) {
1004 int fl;
1005
1006 /* ttyname implies isatty */
1007 SetTtyname(true, &st);
1008 #ifdef MULTIUSER
1009 tty_mode = (int)st.st_mode & 0777;
1010 #endif
1011
1012 fl = fcntl(0, F_GETFL, 0);
1013 if (fl != -1 && (fl & (O_RDWR|O_RDONLY|O_WRONLY)) == O_RDWR)
1014 attach_fd = 0;
1015
1016
1017 if (attach_fd == -1) {
1018 if ((n = secopen(attach_tty, O_RDWR | O_NONBLOCK, 0)) < 0)
1019 Panic(0, "Cannot open your terminal '%s' - please check.", attach_tty);
1020 /* If the server uses a socket we need an open fd. */
1021 attach_fd = n;
1022 }
1023
1024 debug2("attach_tty is %s, attach_fd is %d\n", attach_tty, attach_fd);
1025
1026 if ((attach_term = getenv("TERM")) == 0 || *attach_term == 0)
1027 Panic(0, "Please set a terminal type.");
1028 if (strlen(attach_term) > MAXTERMLEN)
1029 Panic(0, "$TERM too long - sorry.");
1030
1031 GetTTY(0, &attach_Mode);
1032 #ifdef DEBUG
1033 DebugTTY(&attach_Mode);
1034 #endif /* DEBUG */
1035 }
1036
1037 #ifdef _MODE_T
1038 oumask = umask(0); /* well, unsigned never fails? jw. */
1039 #else
1040 if ((oumask = (int)umask(0)) == -1)
1041 Panic(errno, "Cannot change umask to zero");
1042 #endif
1043
1044 SockDir = getenv("SCREENDIR");
1045 if (SockDir) {
1046 if (strlen(SockDir) >= MAXPATHLEN - 1)
1047 Panic(0, "Ridiculously long $SCREENDIR - try again.");
1048
1049 #ifdef MULTIUSER
1050 if (multi)
1051 Panic(0, "No $SCREENDIR with multi screens, please.");
1052 #endif
1053 }
1054
1055 #ifdef MULTIUSER
1056 if (multiattach) {
1057 # ifndef SOCKDIR
1058 sprintf(SockPath, "%s/.screen", multi_home);
1059 SockDir = SockPath;
1060 # else
1061 SockDir = SOCKDIR;
1062 sprintf(SockPath, "%s/S-%s", SockDir, multi);
1063 # endif
1064 } else
1065 #endif
1066
1067 {
1068 #ifndef SOCKDIR
1069 if (SockDir == 0) {
1070 sprintf(SockPath, "%s/.screen", home);
1071 SockDir = SockPath;
1072 }
1073 #endif
1074
1075 if (SockDir) {
1076 if (access(SockDir, F_OK)) {
1077 debug1("SockDir '%s' missing ...\n", SockDir);
1078 if (UserContext() > 0) {
1079 if (mkdir(SockDir, 0700))
1080 UserReturn(0);
1081 UserReturn(1);
1082 }
1083
1084 if (UserStatus() <= 0)
1085 Panic(0, "Cannot make directory '%s'.", SockDir);
1086 }
1087 if (SockDir != SockPath)
1088 strcpy(SockPath, SockDir);
1089 }
1090
1091 #ifdef SOCKDIR
1092 else {
1093 SockDir = SOCKDIR;
1094 if (stat(SockDir, &st)) {
1095 n = (eff_uid == 0 && (real_uid || eff_gid == real_gid)) ? 0755 :
1096 (eff_gid != real_gid) ? 0775 :
1097 #ifdef S_ISVTX
1098 0777|S_ISVTX;
1099 #else
1100 0777;
1101 #endif
1102
1103 if (mkdir(SockDir, n) == -1)
1104 Panic(errno, "Cannot make directory '%s'", SockDir);
1105 }
1106 else {
1107 if (!S_ISDIR(st.st_mode))
1108 Panic(0, "'%s' must be a directory.", SockDir);
1109 if (eff_uid == 0 && real_uid && (int)st.st_uid != eff_uid)
1110 Panic(0, "Directory '%s' must be owned by root.", SockDir);
1111 n = (eff_uid == 0 && (real_uid || (st.st_mode & 0775) != 0775)) ? 0755 :
1112 (eff_gid == (int)st.st_gid && eff_gid != real_gid) ? 0775 : 0777;
1113 if (((int)st.st_mode & 0777) != n)
1114 Panic(0, "Directory '%s' must have mode %03o.", SockDir, n);
1115 }
1116 sprintf(SockPath, "%s/S-%s", SockDir, LoginName);
1117 if (access(SockPath, F_OK)) {
1118 if (mkdir(SockPath, 0700) == -1 && errno != EEXIST)
1119 Panic(errno, "Cannot make directory '%s'", SockPath);
1120 (void) chown(SockPath, real_uid, real_gid);
1121 }
1122 }
1123 #endif
1124 }
1125
1126 if (stat(SockPath, &st) == -1)
1127 Panic(errno, "Cannot access %s", SockPath);
1128 else
1129 if (!S_ISDIR(st.st_mode))
1130 Panic(0, "%s is not a directory.", SockPath);
1131 #ifdef MULTIUSER
1132 if (multi) {
1133 if ((int)st.st_uid != multi_uid)
1134 Panic(0, "%s is not the owner of %s.", multi, SockPath);
1135 }
1136 else
1137 #endif
1138
1139 {
1140 #ifdef SOCKDIR
1141 /* if SOCKDIR is not defined, the socket is in $HOME.
1142 in that case it does not make sense to compare uids. */
1143
1144 if ((int)st.st_uid != real_uid)
1145 Panic(0, "You are not the owner of %s.", SockPath);
1146 #endif
1147 }
1148
1149 if ((st.st_mode & 0777) != 0700)
1150 Panic(0, "Directory %s must have mode 700.", SockPath);
1151 if (SockMatch && index(SockMatch, '/'))
1152 Panic(0, "Bad session name '%s'", SockMatch);
1153 SockName = SockPath + strlen(SockPath) + 1;
1154 *SockName = 0;
1155 (void) umask(oumask);
1156 debug2("SockPath: %s SockMatch: %s\n", SockPath, SockMatch ? SockMatch : "NULL");
1157
1158 #if defined(SYSV) && !defined(ISC)
1159 if (uname(&utsnam) == -1)
1160 Panic(errno, "uname");
1161 strncpy(HostName, utsnam.nodename, sizeof(utsnam.nodename) < MAXSTR ? sizeof(utsnam.nodename) : MAXSTR - 1);
1162 HostName[sizeof(utsnam.nodename) < MAXSTR ? sizeof(utsnam.nodename) : MAXSTR - 1] = '\0';
1163 #else
1164
1165 (void) gethostname(HostName, MAXSTR);
1166 HostName[MAXSTR - 1] = '\0';
1167 #endif
1168 if ((ap = index(HostName, '.')) != NULL)
1169 *ap = '\0';
1170
1171 if (lsflag) {
1172 int i, fo, oth;
1173 bool sock;
1174
1175 #ifdef MULTIUSER
1176 if (multi)
1177 real_uid = multi_uid;
1178 #endif
1179
1180 SET_GUID();
1181 i = FindSocket((int *)NULL, &fo, &oth, SockMatch, &sock);
1182 if (quietflag) {
1183 if (rflag)
1184 exit(10 + i);
1185 else
1186 exit(9 + (fo || oth ? 1 : 0) + fo);
1187 }
1188 if (fo == 0)
1189 Panic(0, "No Sockets found in %s.\n", SockPath);
1190 Msg(0, "%d Socket%s in %s.", fo, fo > 1 ? "s" : "", SockPath);
1191 eexit(0);
1192 }
1193 signal(SIG_BYE, AttacherFinit); /* prevent races */
1194 if (cmdflag) {
1195 /* attach_tty is not mandatory */
1196 SetTtyname(false, &st);
1197 if (!*av)
1198 Panic(0, "Please specify a command.");
1199 SET_GUID();
1200 SendCmdMessage(sty, SockMatch, av, queryflag >= 0);
1201 exit(0);
1202 }
1203 else if (rflag || xflag) {
1204 debug("screen -r: - is there anybody out there?\n");
1205 if (Attach(MSG_ATTACH)) {
1206 Attacher();
1207 /* NOTREACHED */
1208 }
1209 #ifdef MULTIUSER
1210 if (multiattach)
1211 Panic(0, "Can't create sessions of other users.");
1212 #endif
1213
1214 debug("screen -r: backend not responding -- still crying\n");
1215 }
1216 else if (dflag && !mflag) {
1217 SetTtyname(false, &st);
1218 Attach(MSG_DETACH);
1219 Msg(0, "[%s %sdetached.]\n", SockName, (dflag > 1 ? "power " : ""));
1220 eexit(0);
1221 /* NOTREACHED */
1222 }
1223 if (!SockMatch && !mflag && sty) {
1224 /* attach_tty is not mandatory */
1225 SetTtyname(false, &st);
1226 SET_GUID();
1227 nwin_options.args = av;
1228 SendCreateMsg(sty, &nwin);
1229 exit(0);
1230 /* NOTREACHED */
1231 }
1232 nwin_compose(&nwin_default, &nwin_options, &nwin_default);
1233
1234 if (!detached || dflag != 2)
1235 MasterPid = fork();
1236 else
1237 MasterPid = 0;
1238
1239 switch (MasterPid) {
1240 case -1:
1241 Panic(errno, "fork");
1242 /* NOTREACHED */
1243 case 0:
1244 break;
1245 default:
1246 if (detached)
1247 exit(0);
1248 if (SockMatch)
1249 sprintf(socknamebuf, "%d.%s", MasterPid, SockMatch);
1250 else
1251 sprintf(socknamebuf, "%d.%s.%s", MasterPid, stripdev(attach_tty), HostName);
1252 for (ap = socknamebuf; *ap; ap++)
1253 if (*ap == '/')
1254 *ap = '-';
1255 #ifdef NAME_MAX
1256 if (strlen(socknamebuf) > NAME_MAX)
1257 socknamebuf[NAME_MAX] = 0;
1258 #endif
1259 sprintf(SockPath + strlen(SockPath), "/%s", socknamebuf);
1260 SET_GUID();
1261 Attacher();
1262 /* NOTREACHED */
1263 }
1264
1265 if (!detached)
1266 PanicPid = getppid();
1267
1268 if (DefaultEsc == -1)
1269 DefaultEsc = Ctrl('a');
1270 if (DefaultMetaEsc == -1)
1271 DefaultMetaEsc = 'a';
1272
1273 ap = av0 + strlen(av0) - 1;
1274 while (ap >= av0) {
1275 if (!strncmp("screen", ap, 6)) {
1276 memcpy(ap, "SCREEN", 6); /* name this process "SCREEN-BACKEND" */
1277 break;
1278 }
1279 ap--;
1280 }
1281 if (ap < av0)
1282 *av0 = 'S';
1283
1284 #ifdef DEBUG
1285 {
1286 char buf[256];
1287
1288 if (dfp && dfp != stderr)
1289 fclose(dfp);
1290 sprintf(buf, "%s/SCREEN.%d", DEBUGDIR, (int)getpid());
1291 if ((dfp = fopen(buf, "w")) == NULL)
1292 dfp = stderr;
1293 else
1294 (void) chmod(buf, 0666);
1295 }
1296 #endif
1297
1298 if (!detached) {
1299 if (attach_fd == -1) {
1300 if ((n = secopen(attach_tty, O_RDWR | O_NONBLOCK, 0)) < 0)
1301 Panic(0, "Cannot reopen '%s' - please check.", attach_tty);
1302 }
1303 else
1304 n = dup(attach_fd);
1305 }
1306 else
1307 n = -1;
1308 freopen("/dev/null", "r", stdin);
1309 freopen("/dev/null", "w", stdout);
1310
1311 #ifdef DEBUG
1312 if (dfp != stderr)
1313 #endif
1314 freopen("/dev/null", "w", stderr);
1315 debug("-- screen.back debug started\n");
1316
1317 /* This guarantees that the session owner is listed, even when we
1318 * start detached. From now on we should not refer to 'LoginName'
1319 * any more, use users->u_name instead.
1320 */
1321 if (UserAdd(LoginName, (char *)0, (struct acluser **)0) < 0)
1322 Panic(0, "Could not create user info");
1323 if (!detached) {
1324 if (MakeDisplay(LoginName, attach_tty, attach_term, n, getppid(), &attach_Mode) == 0)
1325 Panic(0, "Could not alloc display");
1326 PanicPid = 0;
1327 #ifdef ENCODINGS
1328 D_encoding = nwin_options.encoding > 0 ? nwin_options.encoding : 0;
1329 debug1("D_encoding = %d\n", D_encoding);
1330 #endif
1331 }
1332
1333 if (SockMatch) {
1334 /* user started us with -S option */
1335 sprintf(socknamebuf, "%d.%s", (int)getpid(), SockMatch);
1336 }
1337 else {
1338 sprintf(socknamebuf, "%d.%s.%s", (int)getpid(), stripdev(attach_tty), HostName);
1339 }
1340 for (ap = socknamebuf; *ap; ap++)
1341 if (*ap == '/')
1342 *ap = '-';
1343
1344 #ifdef NAME_MAX
1345 if (strlen(socknamebuf) > NAME_MAX) {
1346 debug2("Socketname %s truncated to %d chars\n", socknamebuf, NAME_MAX);
1347 socknamebuf[NAME_MAX] = 0;
1348 }
1349 #endif
1350
1351 sprintf(SockPath + strlen(SockPath), "/%s", socknamebuf);
1352 /* Always create sockets. We only allow attaching to fifos not creating
1353 * new ones.
1354 */
1355 ServerSocket = MakeServerSocket(true);
1356
1357 #ifdef ETCSCREENRC
1358 # ifdef ALLOW_SYSSCREENRC
1359 if ((ap = getenv("SYSSCREENRC")))
1360 (void)StartRc(ap, 0);
1361 else
1362 # endif
1363 (void)StartRc(ETCSCREENRC, 0);
1364 #endif
1365 (void)StartRc(RcFileName, 0);
1366 # ifdef UTMPOK
1367 # ifndef UTNOKEEP
1368 InitUtmp();
1369 # endif /* UTNOKEEP */
1370 # endif /* UTMPOK */
1371 if (display) {
1372 if (InitTermcap(0, 0)) {
1373 debug("Could not init termcap - exiting\n");
1374 fcntl(D_userfd, F_SETFL, 0); /* Flush sets FNBLOCK */
1375 freetty();
1376 if (D_userpid)
1377 Kill(D_userpid, SIG_BYE);
1378 eexit(1);
1379 }
1380 MakeDefaultCanvas();
1381 InitTerm(0);
1382 #ifdef UTMPOK
1383 RemoveLoginSlot();
1384 #endif
1385 }
1386 else
1387 MakeTermcap(1);
1388
1389 #ifdef LOADAV
1390 InitLoadav();
1391 #endif /* LOADAV */
1392
1393 InitKeytab();
1394 MakeNewEnv();
1395 signal(SIGHUP, SigHup);
1396 signal(SIGINT, FinitHandler);
1397 signal(SIGQUIT, FinitHandler);
1398 signal(SIGTERM, FinitHandler);
1399 #ifdef BSDJOBS
1400 signal(SIGTTIN, SIG_IGN);
1401 signal(SIGTTOU, SIG_IGN);
1402 #endif
1403
1404 if (display) {
1405 brktty(D_userfd);
1406 SetMode(&D_OldMode, &D_NewMode, D_flow, iflag);
1407 /* Note: SetMode must be called _before_ FinishRc. */
1408 SetTTY(D_userfd, &D_NewMode);
1409 if (fcntl(D_userfd, F_SETFL, FNBLOCK))
1410 Msg(errno, "Warning: NBLOCK fcntl failed");
1411 }
1412 else
1413 brktty(-1); /* just try */
1414 signal(SIGCHLD, SigChld);
1415 #ifdef ETCSCREENRC
1416 # ifdef ALLOW_SYSSCREENRC
1417 if ((ap = getenv("SYSSCREENRC")))
1418 FinishRc(ap);
1419 else
1420 # endif
1421 FinishRc(ETCSCREENRC);
1422 #endif
1423 FinishRc(RcFileName);
1424
1425 debug2("UID %d EUID %d\n", (int)getuid(), (int)geteuid());
1426 if (windows == NULL) {
1427 debug("We open one default window, as screenrc did not specify one.\n");
1428 if (MakeWindow(&nwin) == -1) {
1429 fd_set rfd;
1430 struct timeval tv = { MsgWait/1000, 1000*(MsgWait%1000) };
1431 FD_SET(0, &rfd);
1432
1433 Msg(0, "Sorry, could not find a PTY or TTY.");
1434 // allow user to exit early by pressing any key.
1435 select(1, &rfd, NULL, NULL, &tv);
1436 Finit(0);
1437 /* NOTREACHED */
1438 }
1439 }
1440 else if (ac) /* Screen was invoked with a command */
1441 MakeWindow(&nwin);
1442
1443 #ifdef HAVE_BRAILLE
1444 StartBraille();
1445 #endif
1446
1447 if (display && default_startup)
1448 display_copyright();
1449 signal(SIGINT, SigInt);
1450 if (rflag && (rflag & 1) == 0 && !quietflag) {
1451 Msg(0, "New screen...");
1452 rflag = 0;
1453 }
1454
1455 serv_read.type = EV_READ;
1456 serv_read.fd = ServerSocket;
1457 serv_read.handler = serv_read_fn;
1458 evenq(&serv_read);
1459
1460 serv_select.pri = -10;
1461 serv_select.type = EV_ALWAYS;
1462 serv_select.handler = serv_select_fn;
1463 evenq(&serv_select);
1464
1465 logflushev.type = EV_TIMEOUT;
1466 logflushev.handler = logflush_fn;
1467
1468 sched();
1469 /* NOTREACHED */
1470 return 0;
1471 }
1472
1473 void
WindowDied(p,wstat,wstat_valid)1474 WindowDied(p, wstat, wstat_valid)
1475 struct win *p;
1476 #ifdef BSDWAIT
1477 union wait wstat;
1478 #else
1479 int wstat;
1480 #endif
1481
1482
1483 int wstat_valid;
1484 {
1485 int killit = 0;
1486 if (p->w_destroyev.data == (char *)p) {
1487 wstat = p->w_exitstatus;
1488 wstat_valid = 1;
1489 evdeq(&p->w_destroyev);
1490 p->w_destroyev.data = 0;
1491 }
1492
1493 #if defined(BSDJOBS) && !defined(BSDWAIT)
1494 if (!wstat_valid && p->w_pid > 0) {
1495 /* EOF on file descriptor. The process is probably also dead.
1496 * try a waitpid */
1497 if (waitpid(p->w_pid, &wstat, WNOHANG | WUNTRACED) == p->w_pid) {
1498 p->w_pid = 0;
1499 wstat_valid = 1;
1500 }
1501 }
1502 #endif
1503
1504 if (ZombieKey_destroy && ZombieKey_onerror && wstat_valid &&
1505 WIFEXITED(wstat) && WEXITSTATUS(wstat) == 0)
1506 killit = 1;
1507
1508 if (ZombieKey_destroy && !killit) {
1509 char buf[100], *s, reason[100];
1510 time_t now;
1511
1512 if (wstat_valid) {
1513 if (WIFEXITED(wstat))
1514 if (WEXITSTATUS(wstat))
1515 sprintf(reason, "terminated with exit status %d", WEXITSTATUS(wstat));
1516 else
1517 sprintf(reason, "terminated normally");
1518 else if (WIFSIGNALED(wstat))
1519 sprintf(reason, "terminated with signal %d%s", WTERMSIG(wstat),
1520
1521 #ifdef WCOREDUMP
1522 WCOREDUMP(wstat) ? " (core file generated)" : "");
1523 #else
1524 "");
1525 #endif
1526 } else
1527 sprintf(reason, "detached from window");
1528
1529 (void) time(&now);
1530 s = ctime(&now);
1531 if (s && *s)
1532 s[strlen(s) - 1] = '\0';
1533 debug3("window %d (%s) going into zombie state fd %d", p->w_number, p->w_title, p->w_ptyfd);
1534
1535 #ifdef UTMPOK
1536 if (p->w_slot != (slot_t)0 && p->w_slot != (slot_t)-1) {
1537 RemoveUtmp(p);
1538 p->w_slot = 0; /* "detached" */
1539 }
1540 #endif
1541
1542 CloseDevice(p);
1543 p->w_deadpid = p->w_pid;
1544 p->w_pid = 0;
1545 ResetWindow(p);
1546 /* p->w_y = p->w_bot; */
1547 p->w_y = MFindUsedLine(p, p->w_bot, 1);
1548 sprintf(buf, "\n\r=== Command %s (%s) ===", reason, s ? s : "?");
1549 WriteString(p, buf, strlen(buf));
1550 if (p->w_poll_zombie_timeout) {
1551 debug2("Set zombie poll timeout for window %s to %d\n", p->w_title,
1552 p->w_poll_zombie_timeout);
1553 SetTimeout(&p->w_zombieev, p->w_poll_zombie_timeout * 1000);
1554 evenq(&p->w_zombieev);
1555 }
1556 WindowChanged(p, 'f');
1557 }
1558 else
1559 KillWindow(p);
1560
1561 #ifdef UTMPOK
1562 CarefulUtmp();
1563 #endif
1564 }
1565
SigChldHandler()1566 static void SigChldHandler()
1567 {
1568 struct stat st;
1569 #ifdef DEBUG
1570 fds();
1571 #endif
1572 while (GotSigChld) {
1573 GotSigChld = 0;
1574 DoWait();
1575 #ifdef SYSVSIGS
1576 signal(SIGCHLD, SigChld);
1577 #endif
1578 }
1579 if (stat(SockPath, &st) == -1) {
1580 debug1("SigChldHandler: Yuck! cannot stat '%s'\n", SockPath);
1581 if (!RecoverSocket()) {
1582 debug("SCREEN cannot recover from corrupt Socket, bye\n");
1583 Finit(1);
1584 }
1585 else
1586 debug1("'%s' reconstructed\n", SockPath);
1587 }
1588 else
1589 debug2("SigChldHandler: stat '%s' o.k. (%03o)\n", SockPath, (int)st.st_mode);
1590 }
1591
1592 static sigret_t SigChld SIGDEFARG
1593 {
1594 debug("SigChld()\n");
1595 GotSigChld = 1;
1596 SIGRETURN;
1597 }
1598
1599 sigret_t SigHup SIGDEFARG
1600 {
1601 /* Hangup all displays */
1602 while ((display = displays) != 0)
1603 Hangup();
1604 SIGRETURN;
1605 }
1606
1607 /*
1608 * the backend's Interrupt handler
1609 * we cannot insert the intrc directly, as we never know
1610 * if fore is valid.
1611 */
1612 static sigret_t SigInt SIGDEFARG
1613 {
1614 #if HAZARDOUS
1615 char ibuf;
1616 debug("SigInt()\n");
1617 if (fore && displays) {
1618 # if defined(TERMIO) || defined(POSIX)
1619 ibuf = displays->d_OldMode.tio.c_cc[VINTR];
1620 # else
1621 ibuf = displays->d_OldMode.m_tchars.t_intrc;
1622 # endif
1623 fore->w_inlen = 0;
1624 write(fore->w_ptyfd, &ibuf, 1);
1625 }
1626 #else
1627 signal(SIGINT, SigInt);
1628 debug("SigInt() careful\n");
1629 InterruptPlease = 1;
1630 #endif
1631 SIGRETURN;
1632 }
1633
1634 static sigret_t CoreDump SIGDEFARG
1635 {
1636 /* if running with s-bit, we must reset the s-bit, so that we get a
1637 * core file anyway.
1638 */
1639 struct display *disp;
1640 char buf[80];
1641 char *dump_msg = " (core dumped)";
1642 int running_w_s_bit = getuid() != geteuid();
1643
1644 #if defined(SHADOWPW) && !defined(DEBUG) && !defined(DUMPSHADOW)
1645 if (running_w_s_bit)
1646 dump_msg = "";
1647 #endif
1648
1649 #if defined(SYSVSIGS) && defined(SIGHASARG)
1650 signal(sigsig, SIG_IGN);
1651 #endif
1652 setgid(getgid());
1653 setuid(getuid());
1654 unlink("core");
1655
1656 #ifdef SIGHASARG
1657 sprintf(buf, "\r\n[screen caught signal %d.%s]\r\n", sigsig, dump_msg);
1658 #else
1659 sprintf(buf, "\r\n[screen caught a fatal signal.%s]\r\n", dump_msg);
1660 #endif
1661
1662 for (disp = displays; disp; disp = disp->d_next) {
1663 if (disp->d_nonblock < -1 || disp->d_nonblock > 1000000)
1664 continue;
1665 fcntl(disp->d_userfd, F_SETFL, 0);
1666 SetTTY(disp->d_userfd, &D_OldMode);
1667 write(disp->d_userfd, buf, strlen(buf));
1668 Kill(disp->d_userpid, SIG_BYE);
1669 }
1670
1671 if (running_w_s_bit) {
1672 #if defined(SHADOWPW) && !defined(DEBUG) && !defined(DUMPSHADOW)
1673 Kill(getpid(), SIGKILL);
1674 eexit(11);
1675 #else /* SHADOWPW && !DEBUG */
1676 abort();
1677 #endif /* SHADOWPW && !DEBUG */
1678 }
1679 else
1680 abort();
1681
1682 SIGRETURN;
1683 }
1684
DoWait()1685 static void DoWait()
1686 {
1687 register int pid;
1688 struct win *p, *next;
1689 #ifdef BSDWAIT
1690 union wait wstat;
1691 #else
1692 int wstat;
1693 #endif
1694
1695 #ifdef BSDJOBS
1696
1697 # ifndef BSDWAIT
1698 while ((pid = waitpid(-1, &wstat, WNOHANG | WUNTRACED)) > 0)
1699 # else
1700
1701 # ifdef USE_WAIT2
1702 /*
1703 * From: rouilj@sni-usa.com (John Rouillard)
1704 * note that WUNTRACED is not documented to work, but it is defined in
1705 * /usr/include/sys/wait.h, so it may work
1706 */
1707 while ((pid = wait2(&wstat, WNOHANG | WUNTRACED )) > 0)
1708 # else /* USE_WAIT2 */
1709 while ((pid = wait3(&wstat, WNOHANG | WUNTRACED, (struct rusage *) 0)) > 0)
1710 # endif /* USE_WAIT2 */
1711 # endif
1712
1713 #else /* BSDJOBS */
1714 while ((pid = wait(&wstat)) < 0)
1715 if (errno != EINTR)
1716 break;
1717 if (pid > 0)
1718
1719 #endif /* BSDJOBS */
1720 {
1721 for (p = windows; p; p = next) {
1722 next = p->w_next;
1723 if ((p->w_pid && pid == p->w_pid) || (p->w_deadpid && pid == p->w_deadpid)) {
1724 /* child has ceased to exist */
1725 p->w_pid = 0;
1726
1727 #ifdef BSDJOBS
1728 if (WIFSTOPPED(wstat)) {
1729 debug3("Window %d pid %d: WIFSTOPPED (sig %d)\n", p->w_number, pid, WSTOPSIG(wstat));
1730
1731 #ifdef SIGTTIN
1732 if (WSTOPSIG(wstat) == SIGTTIN) {
1733 Msg(0, "Suspended (tty input)");
1734 continue;
1735 }
1736 #endif
1737
1738
1739 #ifdef SIGTTOU
1740 if (WSTOPSIG(wstat) == SIGTTOU) {
1741 Msg(0, "Suspended (tty output)");
1742 continue;
1743 }
1744 #endif
1745
1746 /* Try to restart process */
1747 Msg(0, "Child has been stopped, restarting.");
1748 if (killpg(pid, SIGCONT))
1749 kill(pid, SIGCONT);
1750 }
1751 else
1752 #endif
1753 {
1754 /* Screen will detect the window has died when the window's
1755 * file descriptor signals EOF (which it will do when the process in
1756 * the window terminates). So do this in a timeout of 10 seconds.
1757 * (not doing this at all might also work)
1758 * See #27061 for more details.
1759 */
1760 p->w_destroyev.data = (char *)p;
1761 p->w_exitstatus = wstat;
1762 SetTimeout(&p->w_destroyev, 10 * 1000);
1763 evenq(&p->w_destroyev);
1764 }
1765 break;
1766 }
1767
1768 #ifdef PSEUDOS
1769 if (p->w_pwin && pid == p->w_pwin->p_pid) {
1770 debug2("pseudo of win Nr %d died. pid == %d\n", p->w_number, p->w_pwin->p_pid);
1771 FreePseudowin(p);
1772 break;
1773 }
1774
1775 #endif
1776 }
1777
1778 if (p == 0) {
1779 debug1("pid %d not found - hope that's ok\n", pid);
1780 }
1781 }
1782 }
1783
1784 static sigret_t FinitHandler SIGDEFARG
1785 {
1786 #ifdef SIGHASARG
1787 debug1("FinitHandler called, sig %d.\n", sigsig);
1788 #else
1789 debug("FinitHandler called.\n");
1790 #endif
1791 Finit(1);
1792 SIGRETURN;
1793 }
1794
Finit(int i)1795 void Finit(int i)
1796 {
1797 signal(SIGCHLD, SIG_DFL);
1798 signal(SIGHUP, SIG_IGN);
1799 debug1("Finit(%d);\n", i);
1800 while (windows) {
1801 struct win *p = windows;
1802 windows = windows->w_next;
1803 FreeWindow(p);
1804 }
1805
1806 if (ServerSocket != -1) {
1807 debug1("we unlink(%s)\n", SockPath);
1808 #ifdef USE_SETEUID
1809 xseteuid(real_uid);
1810 xsetegid(real_gid);
1811 #endif
1812 (void) unlink(SockPath);
1813 #ifdef USE_SETEUID
1814 xseteuid(eff_uid);
1815 xsetegid(eff_gid);
1816 #endif
1817 }
1818
1819 for (display = displays; display; display = display->d_next) {
1820 if (D_status)
1821 RemoveStatus();
1822 FinitTerm();
1823 #ifdef UTMPOK
1824 RestoreLoginSlot();
1825 #endif
1826 AddStr("[screen is terminating]\r\n");
1827 Flush(3);
1828 SetTTY(D_userfd, &D_OldMode);
1829 fcntl(D_userfd, F_SETFL, 0);
1830 freetty();
1831 Kill(D_userpid, SIG_BYE);
1832 }
1833 /*
1834 * we _cannot_ call eexit(i) here,
1835 * instead of playing with the Socket above. Sigh.
1836 */
1837 exit(i);
1838 }
1839
eexit(int e)1840 void eexit(int e)
1841 {
1842 debug("eexit\n");
1843 if (ServerSocket != -1) {
1844 debug1("we unlink(%s)\n", SockPath);
1845 setgid(real_gid);
1846 setuid(real_uid);
1847 (void) unlink(SockPath);
1848 }
1849 exit(e);
1850 }
1851
Hangup()1852 void Hangup()
1853 {
1854 if (display == 0)
1855 return;
1856 debug1("Hangup %x\n", display);
1857 if (D_userfd >= 0) {
1858 close(D_userfd);
1859 D_userfd = -1;
1860 }
1861 if (auto_detach || displays->d_next)
1862 Detach(D_HANGUP);
1863 else
1864 Finit(0);
1865 }
1866
1867 /*
1868 * Detach now has the following modes:
1869 *D_DETACH SIG_BYE detach backend and exit attacher
1870 *D_HANGUP SIG_BYE detach backend and exit attacher
1871 *D_STOP SIG_STOP stop attacher (and detach backend)
1872 *D_REMOTE SIG_BYE remote detach -- reattach to new attacher
1873 *D_POWER SIG_POWER_BYE power detach -- attacher kills his parent
1874 *D_REMOTE_POWER SIG_POWER_BYE remote power detach -- both
1875 *D_LOCK SIG_LOCK lock the attacher
1876 * (jw)
1877 * we always remove our utmp slots. (even when "lock" or "stop")
1878 * Note: Take extra care here, we may be called by interrupt!
1879 */
Detach(int mode)1880 void Detach(int mode)
1881 {
1882 int sign = 0, pid;
1883 struct canvas *cv;
1884 struct win *p;
1885
1886 if (display == 0)
1887 return;
1888
1889 #define AddStrSock(msg) do { \
1890 if (SockName) \
1891 { \
1892 AddStr("[" msg " from "); \
1893 AddStr(SockName); \
1894 AddStr("]\r\n"); \
1895 } \
1896 else \
1897 AddStr("[" msg "]\r\n"); \
1898 } while (0)
1899
1900 signal(SIGHUP, SIG_IGN);
1901 debug1("Detach(%d)\n", mode);
1902 if (D_status)
1903 RemoveStatus();
1904 FinitTerm();
1905 if (!display)
1906 return;
1907 switch (mode) {
1908
1909 case D_HANGUP:
1910 sign = SIG_BYE;
1911 break;
1912
1913 case D_DETACH:
1914 AddStrSock("detached");
1915 sign = SIG_BYE;
1916 break;
1917
1918 #ifdef BSDJOBS
1919 case D_STOP:
1920 sign = SIG_STOP;
1921 break;
1922 #endif
1923
1924 #ifdef REMOTE_DETACH
1925 case D_REMOTE:
1926 AddStrSock("remote detached");
1927 sign = SIG_BYE;
1928 break;
1929 #endif
1930
1931 #ifdef POW_DETACH
1932 case D_POWER:
1933 AddStrSock("power detached");
1934 if (PowDetachString) {
1935 AddStr(PowDetachString);
1936 AddStr("\r\n");
1937 }
1938 sign = SIG_POWER_BYE;
1939 break;
1940
1941 #ifdef REMOTE_DETACH
1942 case D_REMOTE_POWER:
1943 AddStrSock("remote power detached");
1944 if (PowDetachString) {
1945 AddStr(PowDetachString);
1946 AddStr("\r\n");
1947 }
1948 sign = SIG_POWER_BYE;
1949 break;
1950 #endif
1951 #endif
1952
1953 case D_LOCK:
1954 ClearAll();
1955 sign = SIG_LOCK;
1956 /* tell attacher to lock terminal with a lockprg. */
1957 break;
1958 }
1959
1960 #ifdef UTMPOK
1961 if (displays->d_next == 0) {
1962 for (p = windows; p; p = p->w_next) {
1963 if (p->w_slot != (slot_t) -1 && !(p->w_lflag & 2)) {
1964 RemoveUtmp(p);
1965
1966 /* Set the slot to 0 to get the window logged in again. */
1967 p->w_slot = (slot_t) 0;
1968 }
1969 }
1970 }
1971 if (mode != D_HANGUP)
1972 RestoreLoginSlot();
1973 #endif
1974
1975 if (displays->d_next == 0 && console_window) {
1976 if (TtyGrabConsole(console_window->w_ptyfd, 0, "detach")) {
1977 debug("could not release console - killing window\n");
1978 KillWindow(console_window);
1979 display = displays; /* restore display */
1980 }
1981 }
1982 if (D_fore) {
1983 #ifdef MULTIUSER
1984 ReleaseAutoWritelock(display, D_fore);
1985 #endif
1986 D_user->u_detachwin = D_fore->w_number;
1987 D_user->u_detachotherwin = D_other ? D_other->w_number : -1;
1988 }
1989
1990 AutosaveLayout(D_layout);
1991 layout_last = D_layout;
1992 for (cv = D_cvlist; cv; cv = cv->c_next) {
1993 p = Layer2Window(cv->c_layer);
1994 SetCanvasWindow(cv, 0);
1995 if (p)
1996 WindowChanged(p, 'u');
1997 }
1998
1999 pid = D_userpid;
2000 debug2("display: %#x displays: %#x\n", (unsigned int)display, (unsigned int)displays);
2001 FreeDisplay();
2002
2003 if (displays == 0) /* Flag detached-ness */
2004 (void) chsock();
2005
2006 /*
2007 * tell father what to do. We do that after we
2008 * freed the tty, thus getty feels more comfortable on hpux
2009 * if it was a power detach.
2010 */
2011 Kill(pid, sign);
2012 debug2("Detach: Signal %d to Attacher(%d)!\n", sign, pid);
2013 debug("Detach returns, we are successfully detached.\n");
2014 signal(SIGHUP, SigHup);
2015 #undef AddStrSock
2016 }
2017
IsSymbol(char * e,char * s)2018 static int IsSymbol(char *e, char *s)
2019 {
2020 register int l;
2021
2022 l = strlen(s);
2023 return strncmp(e, s, l) == 0 && e[l] == '=';
2024 }
2025
MakeNewEnv()2026 void MakeNewEnv()
2027 {
2028 register char **op, **np;
2029 static char stybuf[MAXSTR];
2030
2031 for (op = environ; *op; ++op)
2032 ;
2033 if (NewEnv)
2034 free((char *)NewEnv);
2035 NewEnv = np = (char **) malloc((unsigned) (op - environ + 7 + 1) * sizeof(char **));
2036 if (!NewEnv)
2037 Panic(0, "%s", strnomem);
2038 sprintf(stybuf, "STY=%s", strlen(SockName) <= MAXSTR - 5 ? SockName : "?");
2039 *np++ = stybuf; /* NewEnv[0] */
2040 *np++ = Term; /* NewEnv[1] */
2041 np++; /* room for SHELL */
2042 #ifdef TIOCSWINSZ
2043 np += 2; /* room for TERMCAP and WINDOW */
2044 #else
2045 np += 4; /* room for TERMCAP WINDOW LINES COLUMNS */
2046 #endif
2047
2048 for (op = environ; *op; ++op) {
2049 if (!IsSymbol(*op, "TERM") && !IsSymbol(*op, "TERMCAP")
2050 && !IsSymbol(*op, "STY") && !IsSymbol(*op, "WINDOW")
2051 && !IsSymbol(*op, "SCREENCAP") && !IsSymbol(*op, "SHELL")
2052 && !IsSymbol(*op, "LINES") && !IsSymbol(*op, "COLUMNS"))
2053 *np++ = *op;
2054 }
2055 *np = 0;
2056 }
2057
2058 #if defined(USEVARARGS) && defined(__STDC__)
2059 #define DEFINE_VARARGS_FN(fnname) void fnname (int err, const char *fmt, VA_DOTS)
2060 #else
2061 #define DEFINE_VARARGS_FN(fnname) void fnname(err, fmt, VA_DOTS) \
2062 int err; \
2063 const char *fmt; \
2064 VA_DECL
2065 #endif
2066
2067 #define PROCESS_MESSAGE(B) do { \
2068 char *p = B; \
2069 VA_LIST(ap) \
2070 VA_START(ap, fmt); \
2071 fmt = DoNLS(fmt); \
2072 (void)vsnprintf(p, sizeof(B) - 100, fmt, VA_ARGS(ap)); \
2073 VA_END(ap); \
2074 if (err) \
2075 { \
2076 p += strlen(p); \
2077 *p++ = ':'; \
2078 *p++ = ' '; \
2079 strncpy(p, strerror(err), B + sizeof(B) - p - 1); \
2080 B[sizeof(B) - 1] = 0; \
2081 } \
2082 } while (0)
2083
DEFINE_VARARGS_FN(Msg)2084 DEFINE_VARARGS_FN(Msg)
2085 {
2086 char buf[MAXPATHLEN*2];
2087 PROCESS_MESSAGE(buf);
2088
2089 debug2("Msg('%s') (%#x);\n", buf, (unsigned int)display);
2090
2091 if (display && displays)
2092 MakeStatus(buf);
2093 else if (displays) {
2094 for (display = displays; display; display = display->d_next)
2095 MakeStatus(buf);
2096 }
2097 else if (display) {
2098 /* no displays but a display - must have forked.
2099 * send message to backend!
2100 */
2101 char *tty = D_usertty;
2102 struct display *olddisplay = display;
2103 display = 0; /* only send once */
2104 SendErrorMsg(tty, buf);
2105 display = olddisplay;
2106 }
2107 else
2108 printf("%s\r\n", buf);
2109
2110 if (queryflag >= 0)
2111 write(queryflag, buf, strlen(buf));
2112 }
2113
2114 /*
2115 * Call FinitTerm for all displays, write a message to each and call eexit();
2116 */
DEFINE_VARARGS_FN(Panic)2117 DEFINE_VARARGS_FN(Panic)
2118 {
2119 char buf[MAXPATHLEN*2];
2120 PROCESS_MESSAGE(buf);
2121
2122 debug3("Panic('%s'); display=%x displays=%x\n", buf, display, displays);
2123 if (displays == 0 && display == 0) {
2124 printf("%s\r\n", buf);
2125 if (PanicPid)
2126 Kill(PanicPid, SIG_BYE);
2127 }
2128 else if (displays == 0) {
2129 /* no displays but a display - must have forked.
2130 * send message to backend!
2131 */
2132 char *tty = D_usertty;
2133 display = 0;
2134 SendErrorMsg(tty, buf);
2135 sleep(2);
2136 _exit(1);
2137 }
2138 else
2139 for (display = displays; display; display = display->d_next) {
2140 if (D_status)
2141 RemoveStatus();
2142 FinitTerm();
2143 Flush(3);
2144 #ifdef UTMPOK
2145 RestoreLoginSlot();
2146 #endif
2147 SetTTY(D_userfd, &D_OldMode);
2148 fcntl(D_userfd, F_SETFL, 0);
2149 write(D_userfd, buf, strlen(buf));
2150 write(D_userfd, "\n", 1);
2151 freetty();
2152 if (D_userpid)
2153 Kill(D_userpid, SIG_BYE);
2154 }
2155 #ifdef MULTIUSER
2156 if (tty_oldmode >= 0) {
2157
2158 # ifdef USE_SETEUID
2159 if (setuid(own_uid))
2160 xseteuid(own_uid); /* may be a loop. sigh. */
2161 # else
2162 setuid(own_uid);
2163 # endif
2164
2165 debug1("Panic: changing back modes from %s\n", attach_tty);
2166 chmod(attach_tty, tty_oldmode);
2167 }
2168 #endif
2169 eexit(1);
2170 }
2171
DEFINE_VARARGS_FN(QueryMsg)2172 DEFINE_VARARGS_FN(QueryMsg)
2173 {
2174 char buf[MAXPATHLEN*2];
2175
2176 if (queryflag < 0)
2177 return;
2178
2179 PROCESS_MESSAGE(buf);
2180 write(queryflag, buf, strlen(buf));
2181 }
2182
DEFINE_VARARGS_FN(Dummy)2183 DEFINE_VARARGS_FN(Dummy)
2184 {}
2185
2186 #undef PROCESS_MESSAGE
2187 #undef DEFINE_VARARGS_FN
2188
2189 /*
2190 * '^' is allowed as an escape mechanism for control characters. jw.
2191 *
2192 * Added time insertion using ideas/code from /\ndy Jones
2193 * (andy@lingua.cltr.uq.OZ.AU) - thanks a lot!
2194 *
2195 */
2196
2197 #ifndef USE_LOCALE
2198 static const char days[] = "SunMonTueWedThuFriSat";
2199 static const char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
2200 #endif
2201
2202 static char winmsg_buf[MAXSTR];
2203 #define MAX_WINMSG_REND 256 /* rendition changes */
2204 static int winmsg_rend[MAX_WINMSG_REND];
2205 static int winmsg_rendpos[MAX_WINMSG_REND];
2206 static int winmsg_numrend;
2207
pad_expand(char * buf,char * p,int numpad,int padlen)2208 static char *pad_expand(char *buf, char *p, int numpad, int padlen)
2209 {
2210 char *pn, *pn2;
2211 int i, r;
2212
2213 padlen = padlen - (p - buf); /* space for rent */
2214 if (padlen < 0)
2215 padlen = 0;
2216 pn2 = pn = p + padlen;
2217 r = winmsg_numrend;
2218 while (p >= buf) {
2219 if (r && p - buf == winmsg_rendpos[r - 1]) {
2220 winmsg_rendpos[--r] = pn - buf;
2221 continue;
2222 }
2223 *pn-- = *p;
2224 if (*p-- == 127) {
2225 pn[1] = ' ';
2226 i = numpad > 0 ? (padlen + numpad - 1) / numpad : 0;
2227 padlen -= i;
2228
2229 while (i-- > 0)
2230 *pn-- = ' ';
2231
2232 numpad--;
2233 if (r && p - buf + 1== winmsg_rendpos[r - 1])
2234 winmsg_rendpos[--r] = pn - buf + 1;
2235 }
2236 }
2237 return pn2;
2238 }
2239
2240 struct backtick {
2241 struct backtick *next;
2242 int num;
2243 int tick;
2244 int lifespan;
2245 time_t bestbefore;
2246 char result[MAXSTR];
2247 char **cmdv;
2248 struct event ev;
2249 char *buf;
2250 int bufi;
2251 };
2252
2253 struct backtick *backticks;
2254
backtick_filter(struct backtick * bt)2255 static void backtick_filter(struct backtick *bt)
2256 {
2257 char *p, *q;
2258 int c;
2259
2260 for (p = q = bt->result; (c = (unsigned char)*p++) != 0;) {
2261 if (c == '\t')
2262 c = ' ';
2263 if (c >= ' ' || c == '\005')
2264 *q++ = c;
2265 }
2266 *q = 0;
2267 }
2268
backtick_fn(struct event * ev,char * data)2269 static void backtick_fn(struct event *ev, char *data)
2270 {
2271 struct backtick *bt;
2272 int i, j, k, l;
2273
2274 bt = (struct backtick *)data;
2275 debug1("backtick_fn for #%d\n", bt->num);
2276 i = bt->bufi;
2277 l = read(ev->fd, bt->buf + i, MAXSTR - i);
2278
2279 if (l <= 0) {
2280 debug1("EOF on backtick #%d\n", bt->num);
2281 evdeq(ev);
2282 close(ev->fd);
2283 ev->fd = -1;
2284 return;
2285 }
2286
2287 debug1("read %d bytes\n", l);
2288 i += l;
2289 for (j = 0; j < l; j++)
2290 if (bt->buf[i - j - 1] == '\n')
2291 break;
2292
2293 if (j < l) {
2294 for (k = i - j - 2; k >= 0; k--)
2295 if (bt->buf[k] == '\n')
2296 break;
2297 k++;
2298 bcopy(bt->buf + k, bt->result, i - j - k);
2299 bt->result[i - j - k - 1] = 0;
2300 backtick_filter(bt);
2301 WindowChanged(0, '`');
2302 }
2303
2304 if (j == l && i == MAXSTR) {
2305 j = MAXSTR/2;
2306 l = j + 1;
2307 }
2308
2309 if (j < l){
2310 if (j)
2311 bcopy(bt->buf + i - j, bt->buf, j);
2312 i = j;
2313 }
2314 bt->bufi = i;
2315 }
2316
setbacktick(int num,int lifespan,int tick,char ** cmdv)2317 void setbacktick(int num, int lifespan, int tick, char **cmdv)
2318 {
2319 struct backtick **btp, *bt;
2320 char **v;
2321
2322 debug1("setbacktick called for backtick #%d\n", num);
2323 for (btp = &backticks; (bt = *btp) != 0; btp = &bt->next)
2324 if (bt->num == num)
2325 break;
2326
2327 if (!bt && !cmdv)
2328 return;
2329
2330 if (bt) {
2331 for (v = bt->cmdv; *v; v++)
2332 free(*v);
2333 free(bt->cmdv);
2334 if (bt->buf)
2335 free(bt->buf);
2336 if (bt->ev.fd >= 0)
2337 close(bt->ev.fd);
2338 evdeq(&bt->ev);
2339 }
2340
2341 if (bt && !cmdv) {
2342 *btp = bt->next;
2343 free(bt);
2344 return;
2345 }
2346
2347 if (!bt){
2348 bt = (struct backtick *)malloc(sizeof *bt);
2349 if (!bt){
2350 Msg(0, "%s", strnomem);
2351 return;
2352 }
2353 bzero(bt, sizeof(*bt));
2354 bt->next = 0;
2355 *btp = bt;
2356 }
2357
2358 bt->num = num;
2359 bt->tick = tick;
2360 bt->lifespan = lifespan;
2361 bt->bestbefore = 0;
2362 bt->result[0] = 0;
2363 bt->buf = 0;
2364 bt->bufi = 0;
2365 bt->cmdv = cmdv;
2366 bt->ev.fd = -1;
2367
2368 if (bt->tick == 0 && bt->lifespan == 0) {
2369 debug("setbacktick: continuous mode\n");
2370 bt->buf = (char *)malloc(MAXSTR);
2371 if (bt->buf == 0) {
2372 Msg(0, "%s", strnomem);
2373 setbacktick(num, 0, 0, (char **)0);
2374 return;
2375 }
2376
2377 bt->ev.type = EV_READ;
2378 bt->ev.fd = readpipe(bt->cmdv);
2379 bt->ev.handler = backtick_fn;
2380 bt->ev.data = (char *)bt;
2381 if (bt->ev.fd >= 0)
2382 evenq(&bt->ev);
2383 }
2384 }
2385
runbacktick(struct backtick * bt,int * tickp,time_t now)2386 static char * runbacktick(struct backtick *bt, int *tickp, time_t now)
2387 {
2388 int f, i, l, j;
2389 time_t now2;
2390
2391 debug1("runbacktick called for backtick #%d\n", bt->num);
2392 if (bt->tick && (!*tickp || bt->tick < *tickp))
2393 *tickp = bt->tick;
2394 if ((bt->lifespan == 0 && bt->tick == 0) || now < bt->bestbefore) {
2395 debug1("returning old result (%d)\n", bt->lifespan);
2396 return bt->result;
2397 }
2398
2399 f = readpipe(bt->cmdv);
2400 if (f == -1)
2401 return bt->result;
2402 i = 0;
2403 while ((l = read(f, bt->result + i, sizeof(bt->result) - i)) > 0) {
2404 debug1("runbacktick: read %d bytes\n", l);
2405 i += l;
2406 for (j = 1; j < l; j++)
2407 if (bt->result[i - j - 1] == '\n')
2408 break;
2409
2410 if (j == l && i == sizeof(bt->result)) {
2411 j = sizeof(bt->result) / 2;
2412 l = j + 1;
2413 }
2414
2415 if (j < l) {
2416 bcopy(bt->result + i - j, bt->result, j);
2417 i = j;
2418 }
2419 }
2420
2421 close(f);
2422 bt->result[sizeof(bt->result) - 1] = '\n';
2423 if (i && bt->result[i - 1] == '\n')
2424 i--;
2425 debug1("runbacktick: finished, %d bytes\n", i);
2426 bt->result[i] = 0;
2427 backtick_filter(bt);
2428 (void)time(&now2);
2429 bt->bestbefore = now2 + bt->lifespan;
2430 return bt->result;
2431 }
2432
AddWinMsgRend(const char * str,int r)2433 int AddWinMsgRend(const char *str, int r)
2434 {
2435 if (winmsg_numrend >= MAX_WINMSG_REND || str < winmsg_buf || str >= winmsg_buf + MAXSTR)
2436 return -1;
2437
2438 winmsg_rend[winmsg_numrend] = r;
2439 winmsg_rendpos[winmsg_numrend] = str - winmsg_buf;
2440 winmsg_numrend++;
2441 return 0;
2442 }
2443
MakeWinMsgEv(char * str,struct win * win,int esc,int padlen,struct event * ev,int rec)2444 char *MakeWinMsgEv(char *str, struct win *win, int esc, int padlen, struct event *ev, int rec)
2445 {
2446 static int tick;
2447 char *s = str;
2448 register char *p = winmsg_buf;
2449 register int ctrl;
2450 struct timeval now;
2451 struct tm *tm;
2452 int l, i, r;
2453 int num;
2454 int zeroflg;
2455 int longflg;
2456 int minusflg;
2457 int plusflg;
2458 int qmflag = 0, omflag = 0, qmnumrend = 0;
2459 char *qmpos = 0;
2460 int numpad = 0;
2461 int lastpad = 0;
2462 int truncpos = -1;
2463 int truncper = 0;
2464 int trunclong = 0;
2465 struct backtick *bt = NULL;
2466
2467 if (winmsg_numrend >= 0)
2468 winmsg_numrend = 0;
2469 else
2470 winmsg_numrend = -winmsg_numrend;
2471
2472 tick = 0;
2473 tm = 0;
2474 ctrl = 0;
2475 gettimeofday(&now, NULL);
2476 for (; *s && (l = winmsg_buf + MAXSTR - 1 - p) > 0; s++, p++) {
2477 *p = *s;
2478 if (ctrl) {
2479 ctrl = 0;
2480 if (*s != '^' && *s >= 64)
2481 *p &= 0x1f;
2482 continue;
2483 }
2484
2485 if (*s != esc) {
2486 if (esc == '%') {
2487 switch (*s) {
2488 #if 0
2489 case '~':
2490 *p = BELL;
2491 break;
2492 #endif
2493 case '^':
2494 ctrl = 1;
2495 *p-- = '^';
2496 break;
2497
2498 default:
2499 break;
2500 }
2501 }
2502 continue;
2503 }
2504
2505 if (*++s == esc) /* double escape ? */
2506 continue;
2507
2508 if ((plusflg = *s == '+') != 0)
2509 s++;
2510
2511 if ((minusflg = *s == '-') != 0)
2512 s++;
2513
2514 if ((zeroflg = *s == '0') != 0)
2515 s++;
2516
2517 num = 0;
2518 while(*s >= '0' && *s <= '9')
2519 num = num * 10 + (*s++ - '0');
2520
2521 if ((longflg = *s == 'L') != 0)
2522 s++;
2523
2524 switch (*s) {
2525 case '?':
2526 p--;
2527 if (qmpos) {
2528 if ((!qmflag && !omflag) || omflag == 1){
2529 p = qmpos;
2530 if (qmnumrend < winmsg_numrend)
2531 winmsg_numrend = qmnumrend;
2532 }
2533 qmpos = 0;
2534 break;
2535 }
2536 qmpos = p;
2537 qmnumrend = winmsg_numrend;
2538 qmflag = omflag = 0;
2539 break;
2540
2541 case ':':
2542 p--;
2543 if (!qmpos)
2544 break;
2545 if (qmflag && omflag != 1) {
2546 omflag = 1;
2547 qmpos = p;
2548 qmnumrend = winmsg_numrend;
2549 }
2550 else {
2551 p = qmpos;
2552 if (qmnumrend < winmsg_numrend)
2553 winmsg_numrend = qmnumrend;
2554 omflag = -1;
2555 }
2556 break;
2557
2558 case 'd': case 'D': case 'm': case 'M': case 'y': case 'Y':
2559 case 'a': case 'A': case 's': case 'c': case 'C':
2560
2561 if (l < 4)
2562 break;
2563 if (tm == 0) {
2564 time_t nowsec = now.tv_sec;
2565 tm = localtime(&nowsec);
2566 }
2567
2568 qmflag = 1;
2569 if (!tick || tick > 3600)
2570 tick = 3600;
2571
2572 switch (*s) {
2573 case 'd':
2574 sprintf(p, "%02d", tm->tm_mday % 100);
2575 break;
2576
2577 case 'D':
2578 #ifdef USE_LOCALE
2579 strftime(p, l, (longflg ? "%A" : "%a"), tm);
2580 #else
2581 sprintf(p, "%3.3s", days + 3 * tm->tm_wday);
2582 #endif
2583 break;
2584
2585 case 'm':
2586 sprintf(p, "%02d", tm->tm_mon + 1);
2587 break;
2588
2589 case 'M':
2590 #ifdef USE_LOCALE
2591 strftime(p, l, (longflg ? "%B" : "%b"), tm);
2592 #else
2593 sprintf(p, "%3.3s", months + 3 * tm->tm_mon);
2594 #endif
2595 break;
2596
2597 case 'y':
2598 sprintf(p, "%02d", tm->tm_year % 100);
2599 break;
2600
2601 case 'Y':
2602 sprintf(p, "%04d", tm->tm_year + 1900);
2603 break;
2604
2605 case 'a':
2606 sprintf(p, tm->tm_hour >= 12 ? "pm" : "am");
2607 break;
2608
2609 case 'A':
2610 sprintf(p, tm->tm_hour >= 12 ? "PM" : "AM");
2611 break;
2612
2613 case 's':
2614 sprintf(p, "%02d", tm->tm_sec);
2615 tick = 1;
2616 break;
2617
2618 case 'c':
2619 sprintf(p, zeroflg ? "%02d:%02d" : "%2d:%02d", tm->tm_hour, tm->tm_min);
2620 if (!tick || tick > 60)
2621 tick = 60;
2622 break;
2623
2624 case 'C':
2625 sprintf(p, zeroflg ? "%02d:%02d" : "%2d:%02d", (tm->tm_hour + 11) % 12 + 1, tm->tm_min);
2626 if (!tick || tick > 60)
2627 tick = 60;
2628 break;
2629
2630 default:
2631 break;
2632 }
2633
2634 p += strlen(p) - 1;
2635 break;
2636
2637 case 'X': case 'x':
2638 *p = 0;
2639 for (i = 0; win && win->w_cmdargs[i]; i++) {
2640 if (l < strlen(win->w_cmdargs[i]) + 1)
2641 break;
2642 sprintf(p, i ? " %s" : "%s", win->w_cmdargs[i]);
2643 l -= strlen(p);
2644 p += strlen(p);
2645 if (i == 0 && *s == 'X')
2646 break;
2647 }
2648 p--;
2649 break;
2650
2651 case 'l':
2652 #ifdef LOADAV
2653 *p = 0;
2654 if (l > 20)
2655 AddLoadav(p);
2656 if (*p) {
2657 qmflag = 1;
2658 p += strlen(p) - 1;
2659 }
2660 else
2661 *p = '?';
2662
2663 if (!tick || tick > 60)
2664 tick = 60;
2665 #else
2666 *p = '?';
2667 #endif
2668 p += strlen(p) - 1;
2669 break;
2670
2671 case '`':
2672 case 'h':
2673 if (rec >= 10 || (*s == 'h' && (win == 0 || win->w_hstatus == 0 || *win->w_hstatus == 0))) {
2674 p--;
2675 break;
2676 }
2677 if (*s == '`') {
2678 for (bt = backticks; bt; bt = bt->next)
2679 if (bt->num == num)
2680 break;
2681 if (bt == 0) {
2682 p--;
2683 break;
2684 }
2685 }
2686 {
2687 char savebuf[sizeof(winmsg_buf)];
2688 int oldtick = tick;
2689 int oldnumrend = winmsg_numrend;
2690
2691 *p = 0;
2692 strcpy(savebuf, winmsg_buf);
2693 winmsg_numrend = -winmsg_numrend;
2694 MakeWinMsgEv(*s == 'h' ?
2695 win->w_hstatus : runbacktick(bt, &oldtick, now.tv_sec), win, '\005', 0, (struct event *)0, rec + 1);
2696 debug2("oldtick=%d tick=%d\n", oldtick, tick);
2697 if (!tick || oldtick < tick)
2698 tick = oldtick;
2699 if ((int)strlen(winmsg_buf) < l)
2700 strcat(savebuf, winmsg_buf);
2701 strcpy(winmsg_buf, savebuf);
2702 while (oldnumrend < winmsg_numrend)
2703 winmsg_rendpos[oldnumrend++] += p - winmsg_buf;
2704 if (*p)
2705 qmflag = 1;
2706 p += strlen(p) - 1;
2707 }
2708 break;
2709
2710 case 'w':
2711 case 'W':
2712 {
2713 struct win *oldfore = 0;
2714 char *ss;
2715 if (display) {
2716 oldfore = D_fore;
2717 D_fore = win;
2718 }
2719 ss = AddWindows(p, l - 1, (*s == 'w' ? 0 : 1) |
2720 (longflg ? 0 : 2) | (plusflg ? 4 : 0) |
2721 (minusflg ? 8 : 0), win ? win->w_number : -1);
2722 if (display)
2723 D_fore = oldfore;
2724 }
2725 if (*p)
2726 qmflag = 1;
2727 p += strlen(p) - 1;
2728 break;
2729
2730 case 'u':
2731 *p = 0;
2732 if (win)
2733 AddOtherUsers(p, l - 1, win);
2734 if (*p)
2735 qmflag = 1;
2736 p += strlen(p) - 1;
2737 break;
2738
2739 case 'f':
2740 *p = 0;
2741 if (win)
2742 AddWindowFlags(p, l - 1, win);
2743 if (*p)
2744 qmflag = 1;
2745 p += strlen(p) - 1;
2746 break;
2747
2748 case 't':
2749 *p = 0;
2750 if (win && (int)strlen(win->w_title) < l) {
2751 strcpy(p, win->w_title);
2752 if (*p)
2753 qmflag = 1;
2754 }
2755 p += strlen(p) - 1;
2756 break;
2757
2758 case '{':
2759 {
2760 char rbuf[128];
2761 s++;
2762
2763 for (i = 0; i < 127; i++)
2764 if (s[i] && s[i] != '}')
2765 rbuf[i] = s[i];
2766 else
2767 break;
2768
2769 if (s[i] == '}' && winmsg_numrend < MAX_WINMSG_REND) {
2770 r = -1;
2771 rbuf[i] = 0;
2772 debug1("MakeWinMsg attrcolor %s\n", rbuf);
2773
2774 if (i != 1 || rbuf[0] != '-')
2775 r = ParseAttrColor(rbuf, (char *)0, 0);
2776 if (r != -1 || (i == 1 && rbuf[0] == '-')) {
2777 winmsg_rend[winmsg_numrend] = r;
2778 winmsg_rendpos[winmsg_numrend] = p - winmsg_buf;
2779 winmsg_numrend++;
2780 }
2781 }
2782 s += i;
2783 p--;
2784 }
2785 break;
2786
2787 case 'H':
2788 *p = 0;
2789 if ((int)strlen(HostName) < l) {
2790 strcpy(p, HostName);
2791 if (*p)
2792 qmflag = 1;
2793 }
2794 p += strlen(p) - 1;
2795 break;
2796
2797 case 'S':
2798 {
2799 char *session_name;
2800 *p = 0;
2801 session_name = strchr(SockName, '.') + 1;
2802 if ((int)strlen(session_name) < l) {
2803 strcpy(p, session_name);
2804 if (*p)
2805 qmflag = 1;
2806 }
2807 p += strlen(p) - 1;
2808 }
2809 break;
2810
2811 case 'p':
2812 {
2813 sprintf(p, "%d", (plusflg && display) ? D_userpid : getpid());
2814 p += strlen(p) - 1;
2815 }
2816 break;
2817
2818 case 'F':
2819 p--;
2820 /* small hack */
2821 if (display && ((ev && ev == &D_forecv->c_captev) || (!ev && win && win == D_fore)))
2822 minusflg = !minusflg;
2823 if (minusflg)
2824 qmflag = 1;
2825 break;
2826
2827 case 'P':
2828 p--;
2829 #ifdef COPY_PASTE
2830 if (display && ev && ev != &D_hstatusev) { /* Hack */
2831 /* Is the layer in the current canvas in copy mode? */
2832 struct canvas *cv = (struct canvas *)ev->data;
2833 if (ev == &cv->c_captev && cv->c_layer->l_layfn == &MarkLf)
2834 qmflag = 1;
2835 }
2836 #endif
2837 break;
2838
2839
2840 case 'E':
2841 p--;
2842 if (display && D_ESCseen)
2843 qmflag = 1;
2844 break;
2845
2846 case '>':
2847 truncpos = p - winmsg_buf;
2848 truncper = num > 100 ? 100 : num;
2849 trunclong = longflg;
2850 p--;
2851 break;
2852
2853 case '=':
2854 case '<':
2855 *p = ' ';
2856 if (num || zeroflg || plusflg || longflg || (*s != '=')) {
2857 /* expand all pads */
2858 if (minusflg) {
2859 num = (plusflg ? lastpad : padlen) - num;
2860 if (!plusflg && padlen == 0)
2861 num = p - winmsg_buf;
2862 plusflg = 0;
2863 }
2864 else if (!zeroflg) {
2865 if (*s != '=' && num == 0 && !plusflg)
2866 num = 100;
2867 if (num > 100)
2868 num = 100;
2869 if (padlen == 0)
2870 num = p - winmsg_buf;
2871 else
2872 num = (padlen - (plusflg ? lastpad : 0)) * num / 100;
2873 }
2874
2875 if (num < 0)
2876 num = 0;
2877
2878 if (plusflg)
2879 num += lastpad;
2880
2881 if (num > MAXSTR - 1)
2882 num = MAXSTR - 1;
2883
2884 if (numpad)
2885 p = pad_expand(winmsg_buf, p, numpad, num);
2886
2887 numpad = 0;
2888 if (p - winmsg_buf > num && !longflg) {
2889 int left, trunc;
2890 if (truncpos == -1) {
2891 truncpos = lastpad;
2892 truncper = 0;
2893 }
2894
2895 trunc = lastpad + truncper * (num - lastpad) / 100;
2896 if (trunc > num)
2897 trunc = num;
2898 if (trunc < lastpad)
2899 trunc = lastpad;
2900 left = truncpos - trunc;
2901 if (left > p - winmsg_buf - num)
2902 left = p - winmsg_buf - num;
2903 debug1("lastpad = %d, ", lastpad);
2904 debug3("truncpos = %d, trunc = %d, left = %d\n", truncpos, trunc, left);
2905
2906 if (left > 0) {
2907 if (left + lastpad > p - winmsg_buf)
2908 left = p - winmsg_buf - lastpad;
2909 if (p - winmsg_buf - lastpad - left > 0)
2910 bcopy(winmsg_buf + lastpad + left, winmsg_buf + lastpad, p - winmsg_buf - lastpad - left);
2911 p -= left;
2912 r = winmsg_numrend;
2913 while (r && winmsg_rendpos[r - 1] > lastpad) {
2914 r--;
2915 winmsg_rendpos[r] -= left;
2916 if (winmsg_rendpos[r] < lastpad)
2917 winmsg_rendpos[r] = lastpad;
2918 }
2919
2920 if (trunclong) {
2921 if (p - winmsg_buf > lastpad)
2922 winmsg_buf[lastpad] = '.';
2923 if (p - winmsg_buf > lastpad + 1)
2924 winmsg_buf[lastpad + 1] = '.';
2925 if (p - winmsg_buf > lastpad + 2)
2926 winmsg_buf[lastpad + 2] = '.';
2927 }
2928 }
2929
2930 if (p - winmsg_buf > num) {
2931 p = winmsg_buf + num;
2932 if (trunclong) {
2933 if (num - 1 >= lastpad)
2934 p[-1] = '.';
2935 if (num - 2 >= lastpad)
2936 p[-2] = '.';
2937 if (num - 3 >= lastpad)
2938 p[-3] = '.';
2939 }
2940 r = winmsg_numrend;
2941 while (r && winmsg_rendpos[r - 1] > num)
2942 winmsg_rendpos[--r] = num;
2943 }
2944 truncpos = -1;
2945 trunclong = 0;
2946 if (lastpad > p - winmsg_buf)
2947 lastpad = p - winmsg_buf;
2948 debug1("lastpad now %d\n", lastpad);
2949 }
2950
2951 if (*s == '=') {
2952 while (p - winmsg_buf < num)
2953 *p++ = ' ';
2954 lastpad = p - winmsg_buf;
2955 truncpos = -1;
2956 trunclong = 0;
2957 debug1("lastpad2 now %d\n", lastpad);
2958 }
2959 p--;
2960 }
2961 else if (padlen) {
2962 *p = 127; /* internal pad representation */
2963 numpad++;
2964 }
2965 break;
2966
2967 case 'n':
2968 s++;
2969 /* FALLTHROUGH */
2970
2971 default:
2972 s--;
2973 if (l > 10 + num) {
2974 if (num == 0)
2975 num = 1;
2976 if (!win)
2977 sprintf(p, "%*s", num, num > 1 ? "--" : "-");
2978 else
2979 sprintf(p, "%*d", num, win->w_number);
2980 qmflag = 1;
2981 p += strlen(p) - 1;
2982 }
2983 break;
2984 }
2985 }
2986
2987 if (qmpos && !qmflag)
2988 p = qmpos + 1;
2989 *p = '\0';
2990 if (numpad) {
2991 if (padlen > MAXSTR - 1)
2992 padlen = MAXSTR - 1;
2993 p = pad_expand(winmsg_buf, p, numpad, padlen);
2994 }
2995 if (ev) {
2996 evdeq(ev); /* just in case */
2997 ev->timeout.tv_sec = 0;
2998 ev->timeout.tv_usec = 0;
2999 }
3000 if (ev && tick) {
3001 now.tv_usec = 100000;
3002 if (tick == 1)
3003 now.tv_sec++;
3004 else
3005 now.tv_sec += tick - (now.tv_sec % tick);
3006 ev->timeout = now;
3007 debug2("NEW timeout %d %d\n", ev->timeout.tv_sec, tick);
3008 }
3009 return winmsg_buf;
3010 }
3011
3012
MakeWinMsg(char * s,struct win * win,int esc)3013 char *MakeWinMsg(char *s, struct win *win, int esc)
3014 {
3015 return MakeWinMsgEv(s, win, esc, 0, (struct event *)0, 0);
3016 }
3017
PutWinMsg(char * s,int start,int max)3018 void PutWinMsg(char *s, int start, int max)
3019 {
3020 int i, p, l, r, n;
3021 struct mchar rend;
3022 struct mchar rendstack[MAX_WINMSG_REND];
3023 int rendstackn = 0;
3024
3025 if (s != winmsg_buf) {
3026 /* sorry, no fancy coloring available */
3027 debug1("PutWinMsg %s plain\n", s);
3028 l = strlen(s);
3029 if (l > max)
3030 l = max;
3031 l -= start;
3032 s += start;
3033 while (l-- > 0)
3034 PUTCHARLP(*s++);
3035 return;
3036 }
3037 rend = D_rend;
3038 p = 0;
3039 l = strlen(s);
3040 debug2("PutWinMsg %s start attr %x\n", s, rend.attr);
3041 for (i = 0; i < winmsg_numrend && max > 0; i++) {
3042 if (p > winmsg_rendpos[i] || winmsg_rendpos[i] > l)
3043 break;
3044 if (p < winmsg_rendpos[i]) {
3045 n = winmsg_rendpos[i] - p;
3046 if (n > max)
3047 n = max;
3048 max -= n;
3049 p += n;
3050 while(n-- > 0) {
3051 if (start-- > 0)
3052 s++;
3053 else
3054 PUTCHARLP(*s++);
3055 }
3056 }
3057
3058 r = winmsg_rend[i];
3059 if (r == -1) {
3060 if (rendstackn > 0)
3061 rend = rendstack[--rendstackn];
3062 }
3063 else {
3064 rendstack[rendstackn++] = rend;
3065 ApplyAttrColor(r, &rend);
3066 }
3067
3068 SetRendition(&rend);
3069 }
3070 if (p < l){
3071 n = l - p;
3072 if (n > max)
3073 n = max;
3074 while(n-- > 0) {
3075 if (start-- > 0)
3076 s++;
3077 else
3078 PUTCHARLP(*s++);
3079 }
3080 }
3081 }
3082
3083 #ifdef DEBUG
fds1(int i,int j)3084 static void fds1(int i, int j)
3085 {
3086 while (i < j) {
3087 debug1("%d ", i);
3088 i++;
3089 }
3090 if ((j = open("/dev/null", 0)) >= 0) {
3091 fds1(i + 1, j);
3092 close(j);
3093 }
3094 else {
3095 while (dup(++i) < 0 && errno != EBADF)
3096 debug1("%d ", i);
3097 debug1(" [%d]\n", i);
3098 }
3099 }
3100
fds()3101 static void fds()
3102 {
3103 debug("fds: ");
3104 fds1(-1, -1);
3105 }
3106 #endif
3107
serv_read_fn(struct event * ev,char * data)3108 static void serv_read_fn(struct event *ev, char *data)
3109 {
3110 debug("Knock - knock!\n");
3111 ReceiveMsg();
3112 }
3113
serv_select_fn(struct event * ev,char * data)3114 static void serv_select_fn(struct event *ev, char *data)
3115 {
3116 struct win *p;
3117 debug("serv_select_fn called\n");
3118 /* XXX: messages?? */
3119 if (GotSigChld)
3120 SigChldHandler();
3121
3122 if (InterruptPlease) {
3123 debug("Backend received interrupt\n");
3124 /* This approach is rather questionable in a multi-display
3125 * environment */
3126 if (fore && displays) {
3127 #if defined(TERMIO) || defined(POSIX)
3128 char ibuf = displays->d_OldMode.tio.c_cc[VINTR];
3129 #else
3130 char ibuf = displays->d_OldMode.m_tchars.t_intrc;
3131 #endif
3132
3133
3134 #ifdef PSEUDOS
3135 write(W_UWP(fore) ? fore->w_pwin->p_ptyfd : fore->w_ptyfd, &ibuf, 1);
3136 debug1("Backend wrote interrupt to %d", fore->w_number);
3137 debug1("%s\n", W_UWP(fore) ? " (pseudowin)" : "");
3138 #else
3139 write(fore->w_ptyfd, &ibuf, 1);
3140 debug1("Backend wrote interrupt to %d\n", fore->w_number);
3141 #endif
3142 }
3143 InterruptPlease = 0;
3144 }
3145
3146 for (p = windows; p; p = p->w_next){
3147 if (p->w_bell == BELL_FOUND || p->w_bell == BELL_VISUAL) {
3148 struct canvas *cv;
3149 int visual = p->w_bell == BELL_VISUAL || visual_bell;
3150 p->w_bell = BELL_ON;
3151 for (display = displays; display; display = display->d_next) {
3152 for (cv = D_cvlist; cv; cv = cv->c_next)
3153 if (cv->c_layer->l_bottom == &p->w_layer)
3154 break;
3155
3156 if (cv == 0) {
3157 p->w_bell = BELL_DONE;
3158 Msg(0, "%s", MakeWinMsg(BellString, p, '%'));
3159 }
3160 else if (visual && !D_VB && (!D_status || !D_status_bell)) {
3161 Msg(0, "%s", VisualBellString);
3162
3163 if (D_status) {
3164 D_status_bell = 1;
3165 debug1("using vbell timeout %d\n", VBellWait);
3166 SetTimeout(&D_statusev, VBellWait );
3167 }
3168 }
3169 }
3170
3171 /* don't annoy the user with two messages */
3172 if (p->w_monitor == MON_FOUND)
3173 p->w_monitor = MON_DONE;
3174 WindowChanged(p, 'f');
3175 }
3176
3177 if (p->w_monitor == MON_FOUND) {
3178 struct canvas *cv;
3179 p->w_monitor = MON_ON;
3180 for (display = displays; display; display = display->d_next) {
3181 for (cv = D_cvlist; cv; cv = cv->c_next)
3182 if (cv->c_layer->l_bottom == &p->w_layer)
3183 break;
3184 if (cv)
3185 continue; /* user already sees window */
3186
3187 #ifdef MULTIUSER
3188 if (!(ACLBYTE(p->w_mon_notify, D_user->u_id) & ACLBIT(D_user->u_id)))
3189 continue; /* user doesn't care */
3190 #endif
3191
3192 Msg(0, "%s", MakeWinMsg(ActivityString, p, '%'));
3193 p->w_monitor = MON_DONE;
3194 }
3195 WindowChanged(p, 'f');
3196 }
3197
3198 if (p->w_silence == SILENCE_FOUND) {
3199 /* Unset the flag if the user switched to this window. */
3200 if (p->w_layer.l_cvlist) {
3201 p->w_silence = SILENCE_ON;
3202 WindowChanged(p, 'f');
3203 }
3204 }
3205 }
3206
3207 for (display = displays; display; display = display->d_next) {
3208 struct canvas *cv;
3209 if (D_status == STATUS_ON_WIN)
3210 continue;
3211 /* XXX: should use display functions! */
3212 for (cv = D_cvlist; cv; cv = cv->c_next) {
3213 int lx, ly;
3214
3215 /* normalize window, see resize.c */
3216 lx = cv->c_layer->l_x;
3217 ly = cv->c_layer->l_y;
3218 if (lx == cv->c_layer->l_width)
3219 lx--;
3220 if (ly + cv->c_yoff < cv->c_ys) {
3221 int i, n = cv->c_ys - (ly + cv->c_yoff);
3222 cv->c_yoff = cv->c_ys - ly;
3223 RethinkViewportOffsets(cv);
3224 if (n > cv->c_layer->l_height)
3225 n = cv->c_layer->l_height;
3226 CV_CALL(cv,
3227 LScrollV(flayer, -n, 0, flayer->l_height - 1, 0);
3228 LayRedisplayLine(-1, -1, -1, 1);
3229 for (i = 0; i < n; i++)
3230 LayRedisplayLine(i, 0, flayer->l_width - 1, 1);
3231 if (cv == cv->c_display->d_forecv)
3232 LaySetCursor();
3233 );
3234 }
3235 else if (ly + cv->c_yoff > cv->c_ye) {
3236 int i, n = ly + cv->c_yoff - cv->c_ye;
3237 cv->c_yoff = cv->c_ye - ly;
3238 RethinkViewportOffsets(cv);
3239 if (n > cv->c_layer->l_height)
3240 n = cv->c_layer->l_height;
3241 CV_CALL(cv,
3242 LScrollV(flayer, n, 0, cv->c_layer->l_height - 1, 0);
3243 LayRedisplayLine(-1, -1, -1, 1);
3244 for (i = 0; i < n; i++)
3245 LayRedisplayLine(i + flayer->l_height - n, 0, flayer->l_width - 1, 1);
3246 if (cv == cv->c_display->d_forecv)
3247 LaySetCursor();
3248 );
3249 }
3250 if (lx + cv->c_xoff < cv->c_xs) {
3251 int i, n = cv->c_xs - (lx + cv->c_xoff);
3252 if (n < (cv->c_xe - cv->c_xs + 1) / 2)
3253 n = (cv->c_xe - cv->c_xs + 1) / 2;
3254 if (cv->c_xoff + n > cv->c_xs)
3255 n = cv->c_xs - cv->c_xoff;
3256 cv->c_xoff += n;
3257 RethinkViewportOffsets(cv);
3258 if (n > cv->c_layer->l_width)
3259 n = cv->c_layer->l_width;
3260 CV_CALL(cv,
3261 LayRedisplayLine(-1, -1, -1, 1);
3262 for (i = 0; i < flayer->l_height; i++) {
3263 LScrollH(flayer, -n, i, 0, flayer->l_width - 1, 0, 0);
3264 LayRedisplayLine(i, 0, n - 1, 1);
3265 }
3266 if (cv == cv->c_display->d_forecv)
3267 LaySetCursor();
3268 );
3269 }
3270 else if (lx + cv->c_xoff > cv->c_xe) {
3271 int i, n = lx + cv->c_xoff - cv->c_xe;
3272 if (n < (cv->c_xe - cv->c_xs + 1) / 2)
3273 n = (cv->c_xe - cv->c_xs + 1) / 2;
3274 if (cv->c_xoff - n + cv->c_layer->l_width - 1 < cv->c_xe)
3275 n = cv->c_xoff + cv->c_layer->l_width - 1 - cv->c_xe;
3276 cv->c_xoff -= n;
3277 RethinkViewportOffsets(cv);
3278 if (n > cv->c_layer->l_width)
3279 n = cv->c_layer->l_width;
3280 CV_CALL(cv,
3281 LayRedisplayLine(-1, -1, -1, 1);
3282 for (i = 0; i < flayer->l_height; i++) {
3283 LScrollH(flayer, n, i, 0, flayer->l_width - 1, 0, 0);
3284 LayRedisplayLine(i, flayer->l_width - n, flayer->l_width - 1, 1);
3285 }
3286 if (cv == cv->c_display->d_forecv)
3287 LaySetCursor();
3288 );
3289 }
3290 }
3291 }
3292 for (display = displays; display; display = display->d_next) {
3293 if (D_status == STATUS_ON_WIN || D_cvlist == 0 || D_cvlist->c_next == 0)
3294 continue;
3295 debug1("serv_select_fn: Restore on cv %#x\n", (int)D_forecv);
3296 CV_CALL(D_forecv, LayRestore();LaySetCursor());
3297 }
3298 }
3299
logflush_fn(struct event * ev,char * data)3300 static void logflush_fn(struct event *ev, char *data)
3301 {
3302 struct win *p;
3303 char *buf;
3304 int n;
3305
3306 if (!islogfile(NULL))
3307 return; /* no more logfiles */
3308 logfflush(NULL);
3309 n = log_flush ? log_flush : (logtstamp_after + 4) / 5;
3310
3311 if (n) {
3312 SetTimeout(ev, n * 1000);
3313 evenq(ev); /* re-enqueue ourself */
3314 }
3315
3316 if (!logtstamp_on)
3317 return;
3318
3319 /* write fancy time-stamp */
3320 for (p = windows; p; p = p->w_next) {
3321 if (!p->w_log)
3322 continue;
3323 p->w_logsilence += n;
3324 if (p->w_logsilence < logtstamp_after)
3325 continue;
3326 if (p->w_logsilence - n >= logtstamp_after)
3327 continue;
3328 buf = MakeWinMsg(logtstamp_string, p, '%');
3329 logfwrite(p->w_log, buf, strlen(buf));
3330 }
3331 }
3332
3333 /*
3334 * Interprets ^?, ^@ and other ^-control-char notation.
3335 * Interprets \ddd octal notation
3336 *
3337 * The result is placed in *cp, p is advanced behind the parsed expression and
3338 * returned.
3339 */
ParseChar(char * p,char * cp)3340 static char *ParseChar(char *p, char *cp)
3341 {
3342 if (*p == 0)
3343 return 0;
3344
3345 if (*p == '^' && p[1]) {
3346 if (*++p == '?')
3347 *cp = '\177';
3348 else if (*p >= '@')
3349 *cp = Ctrl(*p);
3350 else
3351 return 0;
3352 ++p;
3353 }
3354 else if (*p == '\\' && *++p <= '7' && *p >= '0') {
3355 *cp = 0;
3356 do
3357 *cp = *cp * 8 + *p - '0';
3358 while (*++p <= '7' && *p >= '0');
3359 }
3360 else
3361 *cp = *p++;
3362 return p;
3363 }
3364
ParseEscape(char * p)3365 static int ParseEscape(char *p)
3366 {
3367 unsigned char buf[2];
3368
3369 if (*p == 0)
3370 SetEscape((struct acluser *)0, -1, -1);
3371 else {
3372 if ((p = ParseChar(p, (char *)buf)) == NULL ||
3373 (p = ParseChar(p, (char *)buf+1)) == NULL || *p)
3374 return -1;
3375 SetEscape((struct acluser *)0, buf[0], buf[1]);
3376 }
3377 return 0;
3378 }
3379
SetTtyname(bool fatal,struct stat * st)3380 void SetTtyname(bool fatal, struct stat *st)
3381 {
3382 int ret;
3383 int saved_errno = 0;
3384
3385 attach_tty_is_in_new_ns = false;
3386 memset(&attach_tty_name_in_ns, 0, sizeof(attach_tty_name_in_ns));
3387
3388 errno = 0;
3389 attach_tty = ttyname(0);
3390 if (!attach_tty) {
3391 if (errno == ENODEV) {
3392 saved_errno = errno;
3393 attach_tty = "/proc/self/fd/0";
3394 attach_tty_is_in_new_ns = true;
3395 ret = readlink(attach_tty, attach_tty_name_in_ns, sizeof(attach_tty_name_in_ns));
3396 if (ret < 0 || (size_t)ret >= sizeof(attach_tty_name_in_ns))
3397 Panic(0, "Bad tty '%s'", attach_tty);
3398 } else if (fatal) {
3399 Panic(0, "Must be connected to a terminal.");
3400 } else {
3401 attach_tty = "";
3402 }
3403 }
3404
3405 if (attach_tty && strcmp(attach_tty, "")) {
3406 if (stat(attach_tty, st))
3407 Panic(errno, "Cannot access '%s'", attach_tty);
3408
3409 if (strlen(attach_tty) >= MAXPATHLEN)
3410 Panic(0, "TtyName too long - sorry.");
3411
3412 /* Only call CheckTtyname() if the device does not exist in
3413 * another namespace.
3414 */
3415 if (saved_errno != ENODEV && CheckTtyname(attach_tty))
3416 Panic(0, "Bad tty '%s'", attach_tty);
3417 }
3418 }
3419