main.c revision 894e0ac8
1/* $XTermId: main.c,v 1.758 2014/05/26 00:01:25 tom Exp $ */ 2 3/* 4 * Copyright 2002-2013,2014 by Thomas E. Dickey 5 * 6 * All Rights Reserved 7 * 8 * Permission is hereby granted, free of charge, to any person obtaining a 9 * copy of this software and associated documentation files (the 10 * "Software"), to deal in the Software without restriction, including 11 * without limitation the rights to use, copy, modify, merge, publish, 12 * distribute, sublicense, and/or sell copies of the Software, and to 13 * permit persons to whom the Software is furnished to do so, subject to 14 * the following conditions: 15 * 16 * The above copyright notice and this permission notice shall be included 17 * in all copies or substantial portions of the Software. 18 * 19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY 23 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 * 27 * Except as contained in this notice, the name(s) of the above copyright 28 * holders shall not be used in advertising or otherwise to promote the 29 * sale, use or other dealings in this Software without prior written 30 * authorization. 31 * 32 * Copyright 1987, 1988 The Open Group 33 * 34 * Permission to use, copy, modify, distribute, and sell this software and its 35 * documentation for any purpose is hereby granted without fee, provided that 36 * the above copyright notice appear in all copies and that both that 37 * copyright notice and this permission notice appear in supporting 38 * documentation. 39 * 40 * The above copyright notice and this permission notice shall be included in 41 * all copies or substantial portions of the Software. 42 * 43 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 44 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 45 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 46 * OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 47 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 48 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 49 * 50 * Except as contained in this notice, the name of The Open Group shall not be 51 * used in advertising or otherwise to promote the sale, use or other dealings 52 * in this Software without prior written authorization from The Open Group. 53 * 54 * Copyright 1987, 1988 by Digital Equipment Corporation, Maynard. 55 * 56 * All Rights Reserved 57 * 58 * Permission to use, copy, modify, and distribute this software and its 59 * documentation for any purpose and without fee is hereby granted, 60 * provided that the above copyright notice appear in all copies and that 61 * both that copyright notice and this permission notice appear in 62 * supporting documentation, and that the name of Digital not be used in 63 * advertising or publicity pertaining to distribution of the software 64 * without specific, written prior permission. 65 * 66 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 67 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 68 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 69 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 70 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 71 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 72 * SOFTWARE. 73 */ 74 75/* 76 * W A R N I N G 77 * 78 * If you think you know what all of this code is doing, you are 79 * probably very mistaken. There be serious and nasty dragons here. 80 * 81 * This client is *not* to be taken as an example of how to write X 82 * Toolkit applications. It is in need of a substantial rewrite, 83 * ideally to create a generic tty widget with several different parsing 84 * widgets so that you can plug 'em together any way you want. Don't 85 * hold your breath, though.... 86 */ 87 88/* main.c */ 89 90#define RES_OFFSET(field) XtOffsetOf(XTERM_RESOURCE, field) 91 92#include <xterm.h> 93#include <version.h> 94#include <graphics.h> 95 96#include <X11/cursorfont.h> 97#include <X11/Xlocale.h> 98 99#if OPT_TOOLBAR 100 101#if defined(HAVE_LIB_XAW) 102#include <X11/Xaw/Form.h> 103#elif defined(HAVE_LIB_XAW3D) 104#include <X11/Xaw3d/Form.h> 105#elif defined(HAVE_LIB_NEXTAW) 106#include <X11/neXtaw/Form.h> 107#elif defined(HAVE_LIB_XAWPLUS) 108#include <X11/XawPlus/Form.h> 109#endif 110 111#endif /* OPT_TOOLBAR */ 112 113#include <pwd.h> 114#include <ctype.h> 115 116#include <data.h> 117#include <error.h> 118#include <menu.h> 119#include <main.h> 120#include <xstrings.h> 121#include <xtermcap.h> 122#include <xterm_io.h> 123 124#if OPT_WIDE_CHARS 125#include <charclass.h> 126#endif 127 128#ifdef __osf__ 129#define USE_SYSV_SIGNALS 130#define WTMP 131#include <pty.h> /* openpty() */ 132#endif 133 134#ifdef __sgi 135#include <grp.h> /* initgroups() */ 136#endif 137 138static void Syntax(char *) GCC_NORETURN; 139static void HsSysError(int) GCC_NORETURN; 140 141#if defined(__SCO__) || defined(SVR4) || defined(_POSIX_SOURCE) 142#define USE_POSIX_SIGNALS 143#endif 144 145#if defined(SYSV) && !defined(SVR4) && !defined(ISC22) && !defined(ISC30) 146/* older SYSV systems cannot ignore SIGHUP. 147 Shell hangs, or you get extra shells, or something like that */ 148#define USE_SYSV_SIGHUP 149#endif 150 151#if defined(sony) && defined(bsd43) && !defined(KANJI) 152#define KANJI 153#endif 154 155#ifdef linux 156#define USE_SYSV_PGRP 157#define USE_SYSV_SIGNALS 158#define WTMP 159#ifdef __GLIBC__ 160#if (__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 1)) 161#include <pty.h> 162#endif 163#endif 164#endif 165 166#ifdef __MVS__ 167#define USE_SYSV_PGRP 168#define USE_SYSV_SIGNALS 169#endif 170 171#ifdef __CYGWIN__ 172#define WTMP 173#endif 174 175#ifdef __SCO__ 176#ifndef _SVID3 177#define _SVID3 178#endif 179#endif 180 181#if defined(__GLIBC__) && !defined(linux) 182#define USE_SYSV_PGRP 183#define WTMP 184#endif 185 186#if defined(USE_TTY_GROUP) || defined(USE_UTMP_SETGID) 187#include <grp.h> 188#endif 189 190#ifndef TTY_GROUP_NAME 191#define TTY_GROUP_NAME "tty" 192#endif 193 194#include <sys/stat.h> 195 196#ifdef Lynx 197#ifndef BSDLY 198#define BSDLY 0 199#endif 200#ifndef VTDLY 201#define VTDLY 0 202#endif 203#ifndef FFDLY 204#define FFDLY 0 205#endif 206#endif 207 208#ifdef SYSV /* { */ 209 210#ifdef USE_USG_PTYS /* AT&T SYSV has no ptyio.h */ 211#include <sys/stropts.h> /* for I_PUSH */ 212#include <poll.h> /* for POLLIN */ 213#endif /* USE_USG_PTYS */ 214 215#define USE_SYSV_SIGNALS 216#define USE_SYSV_PGRP 217 218#if !defined(TIOCSWINSZ) || defined(__SCO__) || defined(__UNIXWARE__) 219#define USE_SYSV_ENVVARS /* COLUMNS/LINES vs. TERMCAP */ 220#endif 221 222/* 223 * now get system-specific includes 224 */ 225#ifdef macII 226#include <sys/ttychars.h> 227#undef USE_SYSV_ENVVARS 228#undef FIOCLEX 229#undef FIONCLEX 230#define setpgrp2 setpgrp 231#include <sgtty.h> 232#include <sys/resource.h> 233#endif 234 235#ifdef __hpux 236#include <sys/ptyio.h> 237#endif /* __hpux */ 238 239#ifdef __osf__ 240#undef USE_SYSV_PGRP 241#define setpgrp setpgid 242#endif 243 244#ifdef __sgi 245#include <sys/sysmacros.h> 246#endif /* __sgi */ 247 248#ifdef sun 249#include <sys/strredir.h> 250#endif 251 252#else /* } !SYSV { */ /* BSD systems */ 253 254#ifdef __QNX__ 255 256#ifndef __QNXNTO__ 257#define ttyslot() 1 258#else 259#define USE_SYSV_PGRP 260extern __inline__ 261int 262ttyslot(void) 263{ 264 return 1; /* yuk */ 265} 266#endif 267 268#else 269 270#if defined(__INTERIX) || defined(__APPLE__) 271#define setpgrp setpgid 272#endif 273 274#ifndef linux 275#ifndef VMS 276#ifndef USE_POSIX_TERMIOS 277#ifndef USE_ANY_SYSV_TERMIO 278#include <sgtty.h> 279#endif 280#endif /* USE_POSIX_TERMIOS */ 281#ifdef Lynx 282#include <resource.h> 283#else 284#include <sys/resource.h> 285#endif 286#endif /* !VMS */ 287#endif /* !linux */ 288 289#endif /* __QNX__ */ 290 291#endif /* } !SYSV */ 292 293/* Xpoll.h and <sys/param.h> on glibc 2.1 systems have colliding NBBY's */ 294#if defined(__GLIBC__) && ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 1))) 295#ifndef NOFILE 296#define NOFILE OPEN_MAX 297#endif 298#elif !(defined(VMS) || defined(WIN32) || defined(Lynx) || defined(__GNU__) || defined(__MVS__)) 299#include <sys/param.h> /* for NOFILE */ 300#endif 301 302#if defined(BSD) && (BSD >= 199103) 303#define WTMP 304#endif 305 306#include <stdio.h> 307 308#ifdef __hpux 309#include <sys/utsname.h> 310#endif /* __hpux */ 311 312#if defined(apollo) && (OSMAJORVERSION == 10) && (OSMINORVERSION < 4) 313#define ttyslot() 1 314#endif /* apollo */ 315 316#if defined(UTMPX_FOR_UTMP) 317#define UTMP_STR utmpx 318#else 319#define UTMP_STR utmp 320#endif 321 322#if defined(USE_UTEMPTER) 323#include <utempter.h> 324#endif 325 326#if defined(UTMPX_FOR_UTMP) 327 328#include <utmpx.h> 329 330#define call_endutent endutxent 331#define call_getutid getutxid 332#define call_pututline pututxline 333#define call_setutent setutxent 334#define call_updwtmp updwtmpx 335 336#elif defined(HAVE_UTMP) 337 338#include <utmp.h> 339 340#if defined(_CRAY) && (OSMAJORVERSION < 8) 341extern struct utmp *getutid __((struct utmp * _Id)); 342#endif 343 344#define call_endutent endutent 345#define call_getutid getutid 346#define call_pututline pututline 347#define call_setutent setutent 348#define call_updwtmp updwtmp 349 350#endif 351 352#if defined(USE_LASTLOG) && defined(HAVE_LASTLOG_H) 353#include <lastlog.h> /* caution: glibc includes utmp.h here */ 354#endif 355 356#ifndef USE_LASTLOGX 357#if defined(_NETBSD_SOURCE) && defined(_PATH_LASTLOGX) 358#define USE_LASTLOGX 1 359#endif 360#endif 361 362#ifdef PUCC_PTYD 363#include <local/openpty.h> 364#endif /* PUCC_PTYD */ 365 366#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) 367#include <util.h> /* openpty() */ 368#endif 369 370#if defined(__FreeBSD__) || defined(__DragonFly__) 371#include <libutil.h> /* openpty() */ 372#endif 373 374#if !defined(UTMP_FILENAME) 375#if defined(UTMP_FILE) 376#define UTMP_FILENAME UTMP_FILE 377#elif defined(_PATH_UTMP) 378#define UTMP_FILENAME _PATH_UTMP 379#else 380#define UTMP_FILENAME "/etc/utmp" 381#endif 382#endif 383 384#ifndef LASTLOG_FILENAME 385#ifdef _PATH_LASTLOG 386#define LASTLOG_FILENAME _PATH_LASTLOG 387#else 388#define LASTLOG_FILENAME "/usr/adm/lastlog" /* only on BSD systems */ 389#endif 390#endif 391 392#if !defined(WTMP_FILENAME) 393#if defined(WTMP_FILE) 394#define WTMP_FILENAME WTMP_FILE 395#elif defined(_PATH_WTMP) 396#define WTMP_FILENAME _PATH_WTMP 397#elif defined(SYSV) 398#define WTMP_FILENAME "/etc/wtmp" 399#else 400#define WTMP_FILENAME "/usr/adm/wtmp" 401#endif 402#endif 403 404#include <signal.h> 405 406#if defined(__SCO__) || (defined(ISC) && !defined(_POSIX_SOURCE)) 407#undef SIGTSTP /* defined, but not the BSD way */ 408#endif 409 410#ifdef SIGTSTP 411#include <sys/wait.h> 412#endif 413 414#if defined(__SCO__) || defined(__UNIXWARE__) 415#undef ECHOKE 416#undef ECHOCTL 417#endif 418 419#if defined(HAVE_SYS_TTYDEFAULTS_H) && !defined(CEOF) 420#include <sys/ttydefaults.h> 421#endif 422 423#ifdef X_NOT_POSIX 424extern long lseek(); 425#if defined(USG) || defined(SVR4) 426extern unsigned sleep(); 427#else 428extern void sleep(); 429#endif 430extern char *ttyname(); 431#endif 432 433#if defined(SYSV) && defined(DECL_PTSNAME) 434extern char *ptsname(int); 435#endif 436 437#ifndef VMS 438static void reapchild(int /* n */ ); 439static int spawnXTerm(XtermWidget /* xw */ ); 440static void remove_termcap_entry(char *, const char *); 441#ifdef USE_PTY_SEARCH 442static int pty_search(int * /* pty */ ); 443#endif 444#endif /* ! VMS */ 445 446static int get_pty(int *pty, char *from); 447static void resize_termcap(XtermWidget xw); 448static void set_owner(char *device, unsigned uid, unsigned gid, unsigned mode); 449 450static Bool added_utmp_entry = False; 451 452#ifdef HAVE_POSIX_SAVED_IDS 453static uid_t save_euid; 454static gid_t save_egid; 455#endif 456 457static uid_t save_ruid; 458static gid_t save_rgid; 459 460#if defined(USE_UTMP_SETGID) 461static int really_get_pty(int *pty, char *from); 462#endif 463 464#if defined(USE_SYSV_UTMP) && !defined(USE_UTEMPTER) 465static Bool xterm_exiting = False; 466#endif 467 468static char *explicit_shname = NULL; 469 470/* 471** Ordinarily it should be okay to omit the assignment in the following 472** statement. Apparently the c89 compiler on AIX 4.1.3 has a bug, or does 473** it? Without the assignment though the compiler will init command_to_exec 474** to 0xffffffff instead of NULL; and subsequent usage, e.g. in spawnXTerm() to 475** SEGV. 476*/ 477static char **command_to_exec = NULL; 478 479#if OPT_LUIT_PROG 480static char **command_to_exec_with_luit = NULL; 481static unsigned command_length_with_luit = 0; 482#endif 483 484#define TERMCAP_ERASE "kb" 485#define VAL_INITIAL_ERASE A2E(8) 486 487/* choose a nice default value for speed - if we make it too low, users who 488 * mistakenly use $TERM set to vt100 will get padding delays. Setting it to a 489 * higher value is not useful since legacy applications (termcap) that care 490 * about padding generally store the code in a short, which does not have 491 * enough bits for the extended values. 492 */ 493#ifdef B38400 /* everyone should define this */ 494#define VAL_LINE_SPEED B38400 495#else /* ...but xterm's used this for a long time */ 496#define VAL_LINE_SPEED B9600 497#endif 498 499/* 500 * Allow use of system default characters if defined and reasonable. 501 * These are based on the BSD ttydefaults.h 502 */ 503#ifndef CBRK 504#define CBRK 0xff /* was 0 */ 505#endif 506#ifndef CDISCARD 507#define CDISCARD CONTROL('O') 508#endif 509#ifndef CDSUSP 510#define CDSUSP CONTROL('Y') 511#endif 512#ifndef CEOF 513#define CEOF CONTROL('D') 514#endif 515#ifndef CEOL 516#define CEOL 0xff /* was 0 */ 517#endif 518#ifndef CERASE 519#define CERASE 0177 520#endif 521#ifndef CERASE2 522#define CERASE2 CONTROL('H') 523#endif 524#ifndef CFLUSH 525#define CFLUSH CONTROL('O') 526#endif 527#ifndef CINTR 528#define CINTR CONTROL('C') 529#endif 530#ifndef CKILL 531#define CKILL CONTROL('U') /* was '@' */ 532#endif 533#ifndef CLNEXT 534#define CLNEXT CONTROL('V') 535#endif 536#ifndef CNUL 537#define CNUL 0 538#endif 539#ifndef CQUIT 540#define CQUIT CONTROL('\\') 541#endif 542#ifndef CRPRNT 543#define CRPRNT CONTROL('R') 544#endif 545#ifndef CREPRINT 546#define CREPRINT CRPRNT 547#endif 548#ifndef CSTART 549#define CSTART CONTROL('Q') 550#endif 551#ifndef CSTATUS 552#define CSTATUS CONTROL('T') 553#endif 554#ifndef CSTOP 555#define CSTOP CONTROL('S') 556#endif 557#ifndef CSUSP 558#define CSUSP CONTROL('Z') 559#endif 560#ifndef CSWTCH 561#define CSWTCH 0 562#endif 563#ifndef CWERASE 564#define CWERASE CONTROL('W') 565#endif 566 567#ifdef USE_ANY_SYSV_TERMIO 568#define TERMIO_STRUCT struct termio 569#define ttySetAttr(fd, datap) ioctl(fd, TCSETA, datap) 570#define ttyGetAttr(fd, datap) ioctl(fd, TCGETA, datap) 571#define ttyFlush(fd) ioctl(fd, TCFLSH, 1) 572#elif defined(USE_POSIX_TERMIOS) 573#define TERMIO_STRUCT struct termios 574#define ttySetAttr(fd, datap) tcsetattr(fd, TCSANOW, datap) 575#define ttyGetAttr(fd, datap) tcgetattr(fd, datap) 576#define ttyFlush(fd) tcflush(fd, TCOFLUSH) 577#endif /* USE_ANY_SYSV_TERMIO */ 578 579#ifndef VMS 580#ifdef TERMIO_STRUCT 581/* The following structures are initialized in main() in order 582** to eliminate any assumptions about the internal order of their 583** contents. 584*/ 585static TERMIO_STRUCT d_tio; 586 587#ifdef HAS_LTCHARS 588static struct ltchars d_ltc; 589#endif /* HAS_LTCHARS */ 590 591#ifdef TIOCLSET 592static unsigned int d_lmode; 593#endif /* TIOCLSET */ 594 595#else /* !TERMIO_STRUCT */ 596static struct sgttyb d_sg = 597{ 598 0, 0, 0177, CKILL, (EVENP | ODDP | ECHO | XTABS | CRMOD) 599}; 600static struct tchars d_tc = 601{ 602 CINTR, CQUIT, CSTART, 603 CSTOP, CEOF, CBRK 604}; 605static struct ltchars d_ltc = 606{ 607 CSUSP, CDSUSP, CRPRNT, 608 CFLUSH, CWERASE, CLNEXT 609}; 610static int d_disipline = NTTYDISC; 611static long int d_lmode = LCRTBS | LCRTERA | LCRTKIL | LCTLECH; 612#ifdef sony 613static long int d_jmode = KM_SYSSJIS | KM_ASCII; 614static struct jtchars d_jtc = 615{ 616 'J', 'B' 617}; 618#endif /* sony */ 619#endif /* TERMIO_STRUCT */ 620#endif /* ! VMS */ 621 622/* 623 * SYSV has the termio.c_cc[V] and ltchars; BSD has tchars and ltchars; 624 * SVR4 has only termio.c_cc, but it includes everything from ltchars. 625 * POSIX termios has termios.c_cc, which is similar to SVR4. 626 */ 627#define TTYMODE(name) { name, sizeof(name)-1, 0, 0 } 628static Boolean override_tty_modes = False; 629/* *INDENT-OFF* */ 630static struct _xttymodes { 631 const char *name; 632 size_t len; 633 int set; 634 int value; 635} ttymodelist[] = { 636 TTYMODE("intr"), /* tchars.t_intrc ; VINTR */ 637#define XTTYMODE_intr 0 638 TTYMODE("quit"), /* tchars.t_quitc ; VQUIT */ 639#define XTTYMODE_quit 1 640 TTYMODE("erase"), /* sgttyb.sg_erase ; VERASE */ 641#define XTTYMODE_erase 2 642 TTYMODE("kill"), /* sgttyb.sg_kill ; VKILL */ 643#define XTTYMODE_kill 3 644 TTYMODE("eof"), /* tchars.t_eofc ; VEOF */ 645#define XTTYMODE_eof 4 646 TTYMODE("eol"), /* VEOL */ 647#define XTTYMODE_eol 5 648 TTYMODE("swtch"), /* VSWTCH */ 649#define XTTYMODE_swtch 6 650 TTYMODE("start"), /* tchars.t_startc ; VSTART */ 651#define XTTYMODE_start 7 652 TTYMODE("stop"), /* tchars.t_stopc ; VSTOP */ 653#define XTTYMODE_stop 8 654 TTYMODE("brk"), /* tchars.t_brkc */ 655#define XTTYMODE_brk 9 656 TTYMODE("susp"), /* ltchars.t_suspc ; VSUSP */ 657#define XTTYMODE_susp 10 658 TTYMODE("dsusp"), /* ltchars.t_dsuspc ; VDSUSP */ 659#define XTTYMODE_dsusp 11 660 TTYMODE("rprnt"), /* ltchars.t_rprntc ; VREPRINT */ 661#define XTTYMODE_rprnt 12 662 TTYMODE("flush"), /* ltchars.t_flushc ; VDISCARD */ 663#define XTTYMODE_flush 13 664 TTYMODE("weras"), /* ltchars.t_werasc ; VWERASE */ 665#define XTTYMODE_weras 14 666 TTYMODE("lnext"), /* ltchars.t_lnextc ; VLNEXT */ 667#define XTTYMODE_lnext 15 668 TTYMODE("status"), /* VSTATUS */ 669#define XTTYMODE_status 16 670 TTYMODE("erase2"), /* VERASE2 */ 671#define XTTYMODE_erase2 17 672 TTYMODE("eol2"), /* VEOL2 */ 673#define XTTYMODE_eol2 18 674 { NULL, 0, 0, '\0' }, /* end of data */ 675}; 676 677#define validTtyChar(data, n) \ 678 (known_ttyChars[n].sysMode >= 0 && \ 679 known_ttyChars[n].sysMode < (int) XtNumber(data.c_cc)) 680 681static const struct { 682 int sysMode; 683 int myMode; 684 int myDefault; 685} known_ttyChars[] = { 686#ifdef VINTR 687 { VINTR, XTTYMODE_intr, CINTR }, 688#endif 689#ifdef VQUIT 690 { VQUIT, XTTYMODE_quit, CQUIT }, 691#endif 692#ifdef VERASE 693 { VERASE, XTTYMODE_erase, CERASE }, 694#endif 695#ifdef VKILL 696 { VKILL, XTTYMODE_kill, CKILL }, 697#endif 698#ifdef VEOF 699 { VEOF, XTTYMODE_eof, CEOF }, 700#endif 701#ifdef VEOL 702 { VEOL, XTTYMODE_eol, CEOL }, 703#endif 704#ifdef VSWTCH 705 { VSWTCH, XTTYMODE_swtch, CNUL }, 706#endif 707#ifdef VSTART 708 { VSTART, XTTYMODE_start, CSTART }, 709#endif 710#ifdef VSTOP 711 { VSTOP, XTTYMODE_stop, CSTOP }, 712#endif 713#ifdef VSUSP 714 { VSUSP, XTTYMODE_susp, CSUSP }, 715#endif 716#ifdef VDSUSP 717 { VDSUSP, XTTYMODE_dsusp, CDSUSP }, 718#endif 719#ifdef VREPRINT 720 { VREPRINT, XTTYMODE_rprnt, CREPRINT }, 721#endif 722#ifdef VDISCARD 723 { VDISCARD, XTTYMODE_flush, CDISCARD }, 724#endif 725#ifdef VWERASE 726 { VWERASE, XTTYMODE_weras, CWERASE }, 727#endif 728#ifdef VLNEXT 729 { VLNEXT, XTTYMODE_lnext, CLNEXT }, 730#endif 731#ifdef VSTATUS 732 { VSTATUS, XTTYMODE_status, CSTATUS }, 733#endif 734#ifdef VERASE2 735 { VERASE2, XTTYMODE_erase2, CERASE2 }, 736#endif 737#ifdef VEOL2 738 { VEOL2, XTTYMODE_eol2, CNUL }, 739#endif 740}; 741/* *INDENT-ON* */ 742 743#define TMODE(ind,var) if (ttymodelist[ind].set) var = (cc_t) ttymodelist[ind].value 744 745static int parse_tty_modes(char *s, struct _xttymodes *modelist); 746 747#ifndef USE_UTEMPTER 748#ifdef USE_SYSV_UTMP 749#if (defined(AIXV3) && (OSMAJORVERSION < 4)) && !(defined(getutid)) 750extern struct utmp *getutid(); 751#endif /* AIXV3 */ 752 753#else /* not USE_SYSV_UTMP */ 754static char etc_utmp[] = UTMP_FILENAME; 755#endif /* USE_SYSV_UTMP */ 756 757#if defined(USE_LASTLOG) && defined(USE_STRUCT_LASTLOG) 758static char etc_lastlog[] = LASTLOG_FILENAME; 759#else 760#undef USE_LASTLOG 761#endif 762 763#ifdef WTMP 764static char etc_wtmp[] = WTMP_FILENAME; 765#endif 766#endif /* !USE_UTEMPTER */ 767 768/* 769 * Some people with 4.3bsd /bin/login seem to like to use login -p -f user 770 * to implement xterm -ls. They can turn on USE_LOGIN_DASH_P and turn off 771 * WTMP and USE_LASTLOG. 772 */ 773#ifdef USE_LOGIN_DASH_P 774#ifndef LOGIN_FILENAME 775#define LOGIN_FILENAME "/bin/login" 776#endif 777static char bin_login[] = LOGIN_FILENAME; 778#endif 779 780static char passedPty[PTYCHARLEN + 1]; /* name if pty if slave */ 781 782#if defined(TIOCCONS) || defined(SRIOCSREDIR) 783static int Console; 784#include <X11/Xmu/SysUtil.h> /* XmuGetHostname */ 785#define MIT_CONSOLE_LEN 12 786#define MIT_CONSOLE "MIT_CONSOLE_" 787static char mit_console_name[255 + MIT_CONSOLE_LEN + 1] = MIT_CONSOLE; 788static Atom mit_console; 789#endif /* TIOCCONS */ 790 791#ifndef USE_SYSV_UTMP 792static int tslot; 793#endif /* USE_SYSV_UTMP */ 794static sigjmp_buf env; 795 796#define SetUtmpHost(dst, screen) \ 797 { \ 798 char host[sizeof(dst) + 1]; \ 799 strncpy(host, DisplayString(screen->display), sizeof(host)); \ 800 TRACE(("DisplayString(%s)\n", host)); \ 801 if (!resource.utmpDisplayId) { \ 802 char *endptr = strrchr(host, ':'); \ 803 if (endptr) { \ 804 TRACE(("trimming display-id '%s'\n", host)); \ 805 *endptr = '\0'; \ 806 } \ 807 } \ 808 copy_filled(dst, host, sizeof(dst)); \ 809 } 810 811#ifdef HAVE_UTMP_UT_SYSLEN 812# define SetUtmpSysLen(utmp) \ 813 { \ 814 utmp.ut_host[sizeof(utmp.ut_host)-1] = '\0'; \ 815 utmp.ut_syslen = strlen(utmp.ut_host) + 1; \ 816 } 817#endif 818 819/* used by VT (charproc.c) */ 820 821static XtResource application_resources[] = 822{ 823 Sres("iconGeometry", "IconGeometry", icon_geometry, NULL), 824 Sres(XtNtitle, XtCTitle, title, NULL), 825 Sres(XtNiconHint, XtCIconHint, icon_hint, NULL), 826 Sres(XtNiconName, XtCIconName, icon_name, NULL), 827 Sres("termName", "TermName", term_name, NULL), 828 Sres("ttyModes", "TtyModes", tty_modes, NULL), 829 Bres("hold", "Hold", hold_screen, False), 830 Bres("utmpInhibit", "UtmpInhibit", utmpInhibit, False), 831 Bres("utmpDisplayId", "UtmpDisplayId", utmpDisplayId, True), 832 Bres("messages", "Messages", messages, True), 833 Ires("minBufSize", "MinBufSize", minBufSize, 4096), 834 Ires("maxBufSize", "MaxBufSize", maxBufSize, 32768), 835 Sres("menuLocale", "MenuLocale", menuLocale, DEF_MENU_LOCALE), 836 Sres("omitTranslation", "OmitTranslation", omitTranslation, NULL), 837 Sres("keyboardType", "KeyboardType", keyboardType, "unknown"), 838#if OPT_PRINT_ON_EXIT 839 Ires("printModeImmediate", "PrintModeImmediate", printModeNow, 0), 840 Ires("printOptsImmediate", "PrintOptsImmediate", printOptsNow, 9), 841 Sres("printFileImmediate", "PrintFileImmediate", printFileNow, NULL), 842 Ires("printModeOnXError", "PrintModeOnXError", printModeOnXError, 0), 843 Ires("printOptsOnXError", "PrintOptsOnXError", printOptsOnXError, 9), 844 Sres("printFileOnXError", "PrintFileOnXError", printFileOnXError, NULL), 845#endif 846#if OPT_SUNPC_KBD 847 Bres("sunKeyboard", "SunKeyboard", sunKeyboard, False), 848#endif 849#if OPT_HP_FUNC_KEYS 850 Bres("hpFunctionKeys", "HpFunctionKeys", hpFunctionKeys, False), 851#endif 852#if OPT_SCO_FUNC_KEYS 853 Bres("scoFunctionKeys", "ScoFunctionKeys", scoFunctionKeys, False), 854#endif 855#if OPT_SUN_FUNC_KEYS 856 Bres("sunFunctionKeys", "SunFunctionKeys", sunFunctionKeys, False), 857#endif 858#if OPT_TCAP_FKEYS 859 Bres("tcapFunctionKeys", "TcapFunctionKeys", termcapKeys, False), 860#endif 861#if OPT_INITIAL_ERASE 862 Bres("ptyInitialErase", "PtyInitialErase", ptyInitialErase, DEF_INITIAL_ERASE), 863 Bres("backarrowKeyIsErase", "BackarrowKeyIsErase", backarrow_is_erase, DEF_BACKARO_ERASE), 864#endif 865 Bres("useInsertMode", "UseInsertMode", useInsertMode, False), 866#if OPT_ZICONBEEP 867 Ires("zIconBeep", "ZIconBeep", zIconBeep, 0), 868 Sres("zIconTitleFormat", "ZIconTitleFormat", zIconFormat, "*** %s"), 869#endif 870#if OPT_PTY_HANDSHAKE 871 Bres("waitForMap", "WaitForMap", wait_for_map, False), 872 Bres("ptyHandshake", "PtyHandshake", ptyHandshake, True), 873 Bres("ptySttySize", "PtySttySize", ptySttySize, DEF_PTY_STTY_SIZE), 874#endif 875#if OPT_REPORT_COLORS 876 Bres("reportColors", "ReportColors", reportColors, False), 877#endif 878#if OPT_REPORT_FONTS 879 Bres("reportFonts", "ReportFonts", reportFonts, False), 880#endif 881#if OPT_SAME_NAME 882 Bres("sameName", "SameName", sameName, True), 883#endif 884#if OPT_SESSION_MGT 885 Bres("sessionMgt", "SessionMgt", sessionMgt, True), 886#endif 887#if OPT_TOOLBAR 888 Bres(XtNtoolBar, XtCToolBar, toolBar, True), 889#endif 890#if OPT_MAXIMIZE 891 Bres(XtNmaximized, XtCMaximized, maximized, False), 892 Sres(XtNfullscreen, XtCFullscreen, fullscreen_s, "off"), 893#endif 894}; 895 896static String fallback_resources[] = 897{ 898#if OPT_TOOLBAR 899 "*toolBar: false", 900#endif 901 "*SimpleMenu*menuLabel.vertSpace: 100", 902 "*SimpleMenu*HorizontalMargins: 16", 903 "*SimpleMenu*Sme.height: 16", 904 "*SimpleMenu*Cursor: left_ptr", 905 "*mainMenu.Label: Main Options (no app-defaults)", 906 "*vtMenu.Label: VT Options (no app-defaults)", 907 "*fontMenu.Label: VT Fonts (no app-defaults)", 908#if OPT_TEK4014 909 "*tekMenu.Label: Tek Options (no app-defaults)", 910#endif 911 NULL 912}; 913 914/* Command line options table. Only resources are entered here...there is a 915 pass over the remaining options after XrmParseCommand is let loose. */ 916/* *INDENT-OFF* */ 917static XrmOptionDescRec optionDescList[] = { 918{"-geometry", "*vt100.geometry",XrmoptionSepArg, (XPointer) NULL}, 919{"-132", "*c132", XrmoptionNoArg, (XPointer) "on"}, 920{"+132", "*c132", XrmoptionNoArg, (XPointer) "off"}, 921{"-ah", "*alwaysHighlight", XrmoptionNoArg, (XPointer) "on"}, 922{"+ah", "*alwaysHighlight", XrmoptionNoArg, (XPointer) "off"}, 923{"-aw", "*autoWrap", XrmoptionNoArg, (XPointer) "on"}, 924{"+aw", "*autoWrap", XrmoptionNoArg, (XPointer) "off"}, 925#ifndef NO_ACTIVE_ICON 926{"-ai", "*activeIcon", XrmoptionNoArg, (XPointer) "off"}, 927{"+ai", "*activeIcon", XrmoptionNoArg, (XPointer) "on"}, 928#endif /* NO_ACTIVE_ICON */ 929{"-b", "*internalBorder",XrmoptionSepArg, (XPointer) NULL}, 930{"-bc", "*cursorBlink", XrmoptionNoArg, (XPointer) "on"}, 931{"+bc", "*cursorBlink", XrmoptionNoArg, (XPointer) "off"}, 932{"-bcf", "*cursorOffTime",XrmoptionSepArg, (XPointer) NULL}, 933{"-bcn", "*cursorOnTime",XrmoptionSepArg, (XPointer) NULL}, 934{"-bdc", "*colorBDMode", XrmoptionNoArg, (XPointer) "off"}, 935{"+bdc", "*colorBDMode", XrmoptionNoArg, (XPointer) "on"}, 936{"-cb", "*cutToBeginningOfLine", XrmoptionNoArg, (XPointer) "off"}, 937{"+cb", "*cutToBeginningOfLine", XrmoptionNoArg, (XPointer) "on"}, 938{"-cc", "*charClass", XrmoptionSepArg, (XPointer) NULL}, 939{"-cm", "*colorMode", XrmoptionNoArg, (XPointer) "off"}, 940{"+cm", "*colorMode", XrmoptionNoArg, (XPointer) "on"}, 941{"-cn", "*cutNewline", XrmoptionNoArg, (XPointer) "off"}, 942{"+cn", "*cutNewline", XrmoptionNoArg, (XPointer) "on"}, 943{"-cr", "*cursorColor", XrmoptionSepArg, (XPointer) NULL}, 944{"-cu", "*curses", XrmoptionNoArg, (XPointer) "on"}, 945{"+cu", "*curses", XrmoptionNoArg, (XPointer) "off"}, 946{"-dc", "*dynamicColors",XrmoptionNoArg, (XPointer) "off"}, 947{"+dc", "*dynamicColors",XrmoptionNoArg, (XPointer) "on"}, 948{"-fb", "*boldFont", XrmoptionSepArg, (XPointer) NULL}, 949{"-fbb", "*freeBoldBox", XrmoptionNoArg, (XPointer)"off"}, 950{"+fbb", "*freeBoldBox", XrmoptionNoArg, (XPointer)"on"}, 951{"-fbx", "*forceBoxChars", XrmoptionNoArg, (XPointer)"off"}, 952{"+fbx", "*forceBoxChars", XrmoptionNoArg, (XPointer)"on"}, 953#ifndef NO_ACTIVE_ICON 954{"-fi", "*iconFont", XrmoptionSepArg, (XPointer) NULL}, 955#endif /* NO_ACTIVE_ICON */ 956#if OPT_RENDERFONT 957{"-fa", "*faceName", XrmoptionSepArg, (XPointer) NULL}, 958{"-fd", "*faceNameDoublesize", XrmoptionSepArg, (XPointer) NULL}, 959{"-fs", "*faceSize", XrmoptionSepArg, (XPointer) NULL}, 960#endif 961#if OPT_WIDE_CHARS 962{"-fw", "*wideFont", XrmoptionSepArg, (XPointer) NULL}, 963{"-fwb", "*wideBoldFont", XrmoptionSepArg, (XPointer) NULL}, 964#endif 965#if OPT_INPUT_METHOD 966{"-fx", "*ximFont", XrmoptionSepArg, (XPointer) NULL}, 967#endif 968#if OPT_HIGHLIGHT_COLOR 969{"-hc", "*highlightColor", XrmoptionSepArg, (XPointer) NULL}, 970{"-hm", "*highlightColorMode", XrmoptionNoArg, (XPointer) "on"}, 971{"+hm", "*highlightColorMode", XrmoptionNoArg, (XPointer) "off"}, 972{"-selfg", "*highlightTextColor", XrmoptionSepArg, (XPointer) NULL}, 973{"-selbg", "*highlightColor", XrmoptionSepArg, (XPointer) NULL}, 974#endif 975#if OPT_HP_FUNC_KEYS 976{"-hf", "*hpFunctionKeys",XrmoptionNoArg, (XPointer) "on"}, 977{"+hf", "*hpFunctionKeys",XrmoptionNoArg, (XPointer) "off"}, 978#endif 979{"-hold", "*hold", XrmoptionNoArg, (XPointer) "on"}, 980{"+hold", "*hold", XrmoptionNoArg, (XPointer) "off"}, 981#if OPT_INITIAL_ERASE 982{"-ie", "*ptyInitialErase", XrmoptionNoArg, (XPointer) "on"}, 983{"+ie", "*ptyInitialErase", XrmoptionNoArg, (XPointer) "off"}, 984#endif 985{"-j", "*jumpScroll", XrmoptionNoArg, (XPointer) "on"}, 986{"+j", "*jumpScroll", XrmoptionNoArg, (XPointer) "off"}, 987#if OPT_C1_PRINT 988{"-k8", "*allowC1Printable", XrmoptionNoArg, (XPointer) "on"}, 989{"+k8", "*allowC1Printable", XrmoptionNoArg, (XPointer) "off"}, 990#endif 991{"-kt", "*keyboardType", XrmoptionSepArg, (XPointer) NULL}, 992/* parse logging options anyway for compatibility */ 993{"-l", "*logging", XrmoptionNoArg, (XPointer) "on"}, 994{"+l", "*logging", XrmoptionNoArg, (XPointer) "off"}, 995{"-lf", "*logFile", XrmoptionSepArg, (XPointer) NULL}, 996{"-ls", "*loginShell", XrmoptionNoArg, (XPointer) "on"}, 997{"+ls", "*loginShell", XrmoptionNoArg, (XPointer) "off"}, 998{"-mb", "*marginBell", XrmoptionNoArg, (XPointer) "on"}, 999{"+mb", "*marginBell", XrmoptionNoArg, (XPointer) "off"}, 1000{"-mc", "*multiClickTime", XrmoptionSepArg, (XPointer) NULL}, 1001{"-mesg", "*messages", XrmoptionNoArg, (XPointer) "off"}, 1002{"+mesg", "*messages", XrmoptionNoArg, (XPointer) "on"}, 1003{"-ms", "*pointerColor",XrmoptionSepArg, (XPointer) NULL}, 1004{"-nb", "*nMarginBell", XrmoptionSepArg, (XPointer) NULL}, 1005{"-nul", "*underLine", XrmoptionNoArg, (XPointer) "off"}, 1006{"+nul", "*underLine", XrmoptionNoArg, (XPointer) "on"}, 1007{"-pc", "*boldColors", XrmoptionNoArg, (XPointer) "on"}, 1008{"+pc", "*boldColors", XrmoptionNoArg, (XPointer) "off"}, 1009{"-rw", "*reverseWrap", XrmoptionNoArg, (XPointer) "on"}, 1010{"+rw", "*reverseWrap", XrmoptionNoArg, (XPointer) "off"}, 1011{"-s", "*multiScroll", XrmoptionNoArg, (XPointer) "on"}, 1012{"+s", "*multiScroll", XrmoptionNoArg, (XPointer) "off"}, 1013{"-sb", "*scrollBar", XrmoptionNoArg, (XPointer) "on"}, 1014{"+sb", "*scrollBar", XrmoptionNoArg, (XPointer) "off"}, 1015#if OPT_REPORT_COLORS 1016{"-report-colors","*reportColors", XrmoptionNoArg, (XPointer) "on"}, 1017#endif 1018#if OPT_REPORT_FONTS 1019{"-report-fonts","*reportFonts", XrmoptionNoArg, (XPointer) "on"}, 1020#endif 1021#ifdef SCROLLBAR_RIGHT 1022{"-leftbar", "*rightScrollBar", XrmoptionNoArg, (XPointer) "off"}, 1023{"-rightbar", "*rightScrollBar", XrmoptionNoArg, (XPointer) "on"}, 1024#endif 1025{"-rvc", "*colorRVMode", XrmoptionNoArg, (XPointer) "off"}, 1026{"+rvc", "*colorRVMode", XrmoptionNoArg, (XPointer) "on"}, 1027{"-sf", "*sunFunctionKeys", XrmoptionNoArg, (XPointer) "on"}, 1028{"+sf", "*sunFunctionKeys", XrmoptionNoArg, (XPointer) "off"}, 1029{"-sh", "*scaleHeight", XrmoptionSepArg, (XPointer) NULL}, 1030{"-si", "*scrollTtyOutput", XrmoptionNoArg, (XPointer) "off"}, 1031{"+si", "*scrollTtyOutput", XrmoptionNoArg, (XPointer) "on"}, 1032{"-sk", "*scrollKey", XrmoptionNoArg, (XPointer) "on"}, 1033{"+sk", "*scrollKey", XrmoptionNoArg, (XPointer) "off"}, 1034{"-sl", "*saveLines", XrmoptionSepArg, (XPointer) NULL}, 1035#if OPT_SUNPC_KBD 1036{"-sp", "*sunKeyboard", XrmoptionNoArg, (XPointer) "on"}, 1037{"+sp", "*sunKeyboard", XrmoptionNoArg, (XPointer) "off"}, 1038#endif 1039#if OPT_TEK4014 1040{"-t", "*tekStartup", XrmoptionNoArg, (XPointer) "on"}, 1041{"+t", "*tekStartup", XrmoptionNoArg, (XPointer) "off"}, 1042#endif 1043{"-ti", "*decTerminalID",XrmoptionSepArg, (XPointer) NULL}, 1044{"-tm", "*ttyModes", XrmoptionSepArg, (XPointer) NULL}, 1045{"-tn", "*termName", XrmoptionSepArg, (XPointer) NULL}, 1046#if OPT_WIDE_CHARS 1047{"-u8", "*utf8", XrmoptionNoArg, (XPointer) "2"}, 1048{"+u8", "*utf8", XrmoptionNoArg, (XPointer) "0"}, 1049#endif 1050#if OPT_LUIT_PROG 1051{"-lc", "*locale", XrmoptionNoArg, (XPointer) "on"}, 1052{"+lc", "*locale", XrmoptionNoArg, (XPointer) "off"}, 1053{"-lcc", "*localeFilter",XrmoptionSepArg, (XPointer) NULL}, 1054{"-en", "*locale", XrmoptionSepArg, (XPointer) NULL}, 1055#endif 1056{"-uc", "*cursorUnderLine", XrmoptionNoArg, (XPointer) "on"}, 1057{"+uc", "*cursorUnderLine", XrmoptionNoArg, (XPointer) "off"}, 1058{"-ulc", "*colorULMode", XrmoptionNoArg, (XPointer) "off"}, 1059{"+ulc", "*colorULMode", XrmoptionNoArg, (XPointer) "on"}, 1060{"-ulit", "*italicULMode", XrmoptionNoArg, (XPointer) "off"}, 1061{"+ulit", "*italicULMode", XrmoptionNoArg, (XPointer) "on"}, 1062{"-ut", "*utmpInhibit", XrmoptionNoArg, (XPointer) "on"}, 1063{"+ut", "*utmpInhibit", XrmoptionNoArg, (XPointer) "off"}, 1064{"-im", "*useInsertMode", XrmoptionNoArg, (XPointer) "on"}, 1065{"+im", "*useInsertMode", XrmoptionNoArg, (XPointer) "off"}, 1066{"-vb", "*visualBell", XrmoptionNoArg, (XPointer) "on"}, 1067{"+vb", "*visualBell", XrmoptionNoArg, (XPointer) "off"}, 1068{"-pob", "*popOnBell", XrmoptionNoArg, (XPointer) "on"}, 1069{"+pob", "*popOnBell", XrmoptionNoArg, (XPointer) "off"}, 1070#if OPT_WIDE_CHARS 1071{"-wc", "*wideChars", XrmoptionNoArg, (XPointer) "on"}, 1072{"+wc", "*wideChars", XrmoptionNoArg, (XPointer) "off"}, 1073{"-mk_width", "*mkWidth", XrmoptionNoArg, (XPointer) "on"}, 1074{"+mk_width", "*mkWidth", XrmoptionNoArg, (XPointer) "off"}, 1075{"-cjk_width", "*cjkWidth", XrmoptionNoArg, (XPointer) "on"}, 1076{"+cjk_width", "*cjkWidth", XrmoptionNoArg, (XPointer) "off"}, 1077#endif 1078{"-wf", "*waitForMap", XrmoptionNoArg, (XPointer) "on"}, 1079{"+wf", "*waitForMap", XrmoptionNoArg, (XPointer) "off"}, 1080#if OPT_ZICONBEEP 1081{"-ziconbeep", "*zIconBeep", XrmoptionSepArg, (XPointer) NULL}, 1082#endif 1083#if OPT_SAME_NAME 1084{"-samename", "*sameName", XrmoptionNoArg, (XPointer) "on"}, 1085{"+samename", "*sameName", XrmoptionNoArg, (XPointer) "off"}, 1086#endif 1087#if OPT_SESSION_MGT 1088{"-sm", "*sessionMgt", XrmoptionNoArg, (XPointer) "on"}, 1089{"+sm", "*sessionMgt", XrmoptionNoArg, (XPointer) "off"}, 1090#endif 1091#if OPT_TOOLBAR 1092{"-tb", "*"XtNtoolBar, XrmoptionNoArg, (XPointer) "on"}, 1093{"+tb", "*"XtNtoolBar, XrmoptionNoArg, (XPointer) "off"}, 1094#endif 1095#if OPT_MAXIMIZE 1096{"-maximized", "*maximized", XrmoptionNoArg, (XPointer) "on"}, 1097{"+maximized", "*maximized", XrmoptionNoArg, (XPointer) "off"}, 1098{"-fullscreen", "*fullscreen", XrmoptionNoArg, (XPointer) "on"}, 1099{"+fullscreen", "*fullscreen", XrmoptionNoArg, (XPointer) "off"}, 1100#endif 1101/* options that we process ourselves */ 1102{"-help", NULL, XrmoptionSkipNArgs, (XPointer) NULL}, 1103{"-version", NULL, XrmoptionSkipNArgs, (XPointer) NULL}, 1104{"-class", NULL, XrmoptionSkipArg, (XPointer) NULL}, 1105{"-e", NULL, XrmoptionSkipLine, (XPointer) NULL}, 1106{"-into", NULL, XrmoptionSkipArg, (XPointer) NULL}, 1107/* bogus old compatibility stuff for which there are 1108 standard XtOpenApplication options now */ 1109{"%", "*tekGeometry", XrmoptionStickyArg, (XPointer) NULL}, 1110{"#", ".iconGeometry",XrmoptionStickyArg, (XPointer) NULL}, 1111{"-T", ".title", XrmoptionSepArg, (XPointer) NULL}, 1112{"-n", "*iconName", XrmoptionSepArg, (XPointer) NULL}, 1113{"-r", "*reverseVideo",XrmoptionNoArg, (XPointer) "on"}, 1114{"+r", "*reverseVideo",XrmoptionNoArg, (XPointer) "off"}, 1115{"-rv", "*reverseVideo",XrmoptionNoArg, (XPointer) "on"}, 1116{"+rv", "*reverseVideo",XrmoptionNoArg, (XPointer) "off"}, 1117{"-w", ".borderWidth", XrmoptionSepArg, (XPointer) NULL}, 1118}; 1119 1120static OptionHelp xtermOptions[] = { 1121{ "-version", "print the version number" }, 1122{ "-help", "print out this message" }, 1123{ "-display displayname", "X server to contact" }, 1124{ "-geometry geom", "size (in characters) and position" }, 1125{ "-/+rv", "turn on/off reverse video" }, 1126{ "-bg color", "background color" }, 1127{ "-fg color", "foreground color" }, 1128{ "-bd color", "border color" }, 1129{ "-bw number", "border width in pixels" }, 1130{ "-fn fontname", "normal text font" }, 1131{ "-fb fontname", "bold text font" }, 1132{ "-/+fbb", "turn on/off normal/bold font comparison inhibit"}, 1133{ "-/+fbx", "turn off/on linedrawing characters"}, 1134#if OPT_RENDERFONT 1135{ "-fa pattern", "FreeType font-selection pattern" }, 1136{ "-fd pattern", "FreeType Doublesize font-selection pattern" }, 1137{ "-fs size", "FreeType font-size" }, 1138#endif 1139#if OPT_WIDE_CHARS 1140{ "-fw fontname", "doublewidth text font" }, 1141{ "-fwb fontname", "doublewidth bold text font" }, 1142#endif 1143#if OPT_INPUT_METHOD 1144{ "-fx fontname", "XIM fontset" }, 1145#endif 1146{ "-iconic", "start iconic" }, 1147{ "-name string", "client instance, icon, and title strings" }, 1148{ "-class string", "class string (XTerm)" }, 1149{ "-title string", "title string" }, 1150{ "-xrm resourcestring", "additional resource specifications" }, 1151{ "-/+132", "turn on/off 80/132 column switching" }, 1152{ "-/+ah", "turn on/off always highlight" }, 1153#ifndef NO_ACTIVE_ICON 1154{ "-/+ai", "turn off/on active icon" }, 1155{ "-fi fontname", "icon font for active icon" }, 1156#endif /* NO_ACTIVE_ICON */ 1157{ "-b number", "internal border in pixels" }, 1158{ "-/+bc", "turn on/off text cursor blinking" }, 1159{ "-bcf milliseconds", "time text cursor is off when blinking"}, 1160{ "-bcn milliseconds", "time text cursor is on when blinking"}, 1161{ "-/+bdc", "turn off/on display of bold as color"}, 1162{ "-/+cb", "turn on/off cut-to-beginning-of-line inhibit" }, 1163{ "-cc classrange", "specify additional character classes" }, 1164{ "-/+cm", "turn off/on ANSI color mode" }, 1165{ "-/+cn", "turn on/off cut newline inhibit" }, 1166{ "-cr color", "text cursor color" }, 1167{ "-/+cu", "turn on/off curses emulation" }, 1168{ "-/+dc", "turn off/on dynamic color selection" }, 1169#if OPT_HIGHLIGHT_COLOR 1170{ "-/+hm", "turn on/off selection-color override" }, 1171{ "-selbg color", "selection background color" }, 1172{ "-selfg color", "selection foreground color" }, 1173/* -hc is deprecated, not shown in help message */ 1174#endif 1175#if OPT_HP_FUNC_KEYS 1176{ "-/+hf", "turn on/off HP Function Key escape codes" }, 1177#endif 1178{ "-/+hold", "turn on/off logic that retains window after exit" }, 1179#if OPT_INITIAL_ERASE 1180{ "-/+ie", "turn on/off initialization of 'erase' from pty" }, 1181#endif 1182{ "-/+im", "use insert mode for TERMCAP" }, 1183{ "-/+j", "turn on/off jump scroll" }, 1184#if OPT_C1_PRINT 1185{ "-/+k8", "turn on/off C1-printable classification"}, 1186#endif 1187{ "-kt keyboardtype", "set keyboard type:" KEYBOARD_TYPES }, 1188#ifdef ALLOWLOGGING 1189{ "-/+l", "turn on/off logging" }, 1190{ "-lf filename", "logging filename" }, 1191#else 1192{ "-/+l", "turn on/off logging (not supported)" }, 1193{ "-lf filename", "logging filename (not supported)" }, 1194#endif 1195{ "-/+ls", "turn on/off login shell" }, 1196{ "-/+mb", "turn on/off margin bell" }, 1197{ "-mc milliseconds", "multiclick time in milliseconds" }, 1198{ "-/+mesg", "forbid/allow messages" }, 1199{ "-ms color", "pointer color" }, 1200{ "-nb number", "margin bell in characters from right end" }, 1201{ "-/+nul", "turn off/on display of underlining" }, 1202{ "-/+aw", "turn on/off auto wraparound" }, 1203{ "-/+pc", "turn on/off PC-style bold colors" }, 1204{ "-/+rw", "turn on/off reverse wraparound" }, 1205{ "-/+s", "turn on/off multiscroll" }, 1206{ "-/+sb", "turn on/off scrollbar" }, 1207#if OPT_REPORT_COLORS 1208{ "-report-colors", "report colors as they are allocated" }, 1209#endif 1210#if OPT_REPORT_FONTS 1211{ "-report-fonts", "report fonts as loaded to stdout" }, 1212#endif 1213#ifdef SCROLLBAR_RIGHT 1214{ "-rightbar", "force scrollbar right (default left)" }, 1215{ "-leftbar", "force scrollbar left" }, 1216#endif 1217{ "-/+rvc", "turn off/on display of reverse as color" }, 1218{ "-/+sf", "turn on/off Sun Function Key escape codes" }, 1219{ "-sh number", "scale line-height values by the given number" }, 1220{ "-/+si", "turn on/off scroll-on-tty-output inhibit" }, 1221{ "-/+sk", "turn on/off scroll-on-keypress" }, 1222{ "-sl number", "number of scrolled lines to save" }, 1223#if OPT_SUNPC_KBD 1224{ "-/+sp", "turn on/off Sun/PC Function/Keypad mapping" }, 1225#endif 1226#if OPT_TEK4014 1227{ "-/+t", "turn on/off Tek emulation window" }, 1228#endif 1229#if OPT_TOOLBAR 1230{ "-/+tb", "turn on/off toolbar" }, 1231#endif 1232{ "-ti termid", "terminal identifier" }, 1233{ "-tm string", "terminal mode keywords and characters" }, 1234{ "-tn name", "TERM environment variable name" }, 1235#if OPT_WIDE_CHARS 1236{ "-/+u8", "turn on/off UTF-8 mode (implies wide-characters)" }, 1237#endif 1238#if OPT_LUIT_PROG 1239{ "-/+lc", "turn on/off locale mode using luit" }, 1240{ "-lcc path", "filename of locale converter (" DEFLOCALEFILTER ")" }, 1241/* -en is deprecated, not shown in help message */ 1242#endif 1243{ "-/+uc", "turn on/off underline cursor" }, 1244{ "-/+ulc", "turn off/on display of underline as color" }, 1245{ "-/+ulit", "turn off/on display of underline as italics" }, 1246#ifdef HAVE_UTMP 1247{ "-/+ut", "turn on/off utmp support" }, 1248#else 1249{ "-/+ut", "turn on/off utmp support (not available)" }, 1250#endif 1251{ "-/+vb", "turn on/off visual bell" }, 1252{ "-/+pob", "turn on/off pop on bell" }, 1253#if OPT_WIDE_CHARS 1254{ "-/+wc", "turn on/off wide-character mode" }, 1255{ "-/+mk_width", "turn on/off simple width convention" }, 1256{ "-/+cjk_width", "turn on/off legacy CJK width convention" }, 1257#endif 1258{ "-/+wf", "turn on/off wait for map before command exec" }, 1259{ "-e command args ...", "command to execute" }, 1260#if OPT_TEK4014 1261{ "%geom", "Tek window geometry" }, 1262#endif 1263{ "#geom", "icon window geometry" }, 1264{ "-T string", "title name for window" }, 1265{ "-n string", "icon name for window" }, 1266#if defined(TIOCCONS) || defined(SRIOCSREDIR) 1267{ "-C", "intercept console messages" }, 1268#else 1269{ "-C", "intercept console messages (not supported)" }, 1270#endif 1271{ "-Sccn", "slave mode on \"ttycc\", file descriptor \"n\"" }, 1272{ "-into windowId", "use the window id given to -into as the parent window rather than the default root window" }, 1273#if OPT_ZICONBEEP 1274{ "-ziconbeep percent", "beep and flag icon of window having hidden output" }, 1275#endif 1276#if OPT_SAME_NAME 1277{ "-/+samename", "turn on/off the no-flicker option for title and icon name" }, 1278#endif 1279#if OPT_SESSION_MGT 1280{ "-/+sm", "turn on/off the session-management support" }, 1281#endif 1282#if OPT_MAXIMIZE 1283{"-/+maximized", "turn on/off maxmize on startup" }, 1284{"-/+fullscreen", "turn on/off fullscreen on startup" }, 1285#endif 1286{ NULL, NULL }}; 1287/* *INDENT-ON* */ 1288 1289static const char *message[] = 1290{ 1291 "Fonts should be fixed width and, if both normal and bold are specified, should", 1292 "have the same size. If only a normal font is specified, it will be used for", 1293 "both normal and bold text (by doing overstriking). The -e option, if given,", 1294 "must appear at the end of the command line, otherwise the user's default shell", 1295 "will be started. Options that start with a plus sign (+) restore the default.", 1296 NULL}; 1297 1298/* 1299 * Decode a key-definition. This combines the termcap and ttyModes, for 1300 * comparison. Note that octal escapes in ttyModes are done by the normal 1301 * resource translation. Also, ttyModes allows '^-' as a synonym for disabled. 1302 */ 1303static int 1304decode_keyvalue(char **ptr, int termcap) 1305{ 1306 char *string = *ptr; 1307 int value = -1; 1308 1309 TRACE(("decode_keyvalue '%s'\n", string)); 1310 if (*string == '^') { 1311 switch (*++string) { 1312 case '?': 1313 value = A2E(ANSI_DEL); 1314 break; 1315 case '-': 1316 if (!termcap) { 1317 errno = 0; 1318#if defined(_POSIX_VDISABLE) && defined(HAVE_UNISTD_H) 1319 value = _POSIX_VDISABLE; 1320#endif 1321#if defined(_PC_VDISABLE) 1322 if (value == -1) { 1323 value = (int) fpathconf(0, _PC_VDISABLE); 1324 if (value == -1) { 1325 if (errno != 0) 1326 break; /* skip this (error) */ 1327 value = 0377; 1328 } 1329 } 1330#elif defined(VDISABLE) 1331 if (value == -1) 1332 value = VDISABLE; 1333#endif 1334 break; 1335 } 1336 /* FALLTHRU */ 1337 default: 1338 value = CONTROL(*string); 1339 break; 1340 } 1341 ++string; 1342 } else if (termcap && (*string == '\\')) { 1343 char *d; 1344 int temp = (int) strtol(string + 1, &d, 8); 1345 if (temp > 0 && d != string) { 1346 value = temp; 1347 string = d; 1348 } 1349 } else { 1350 value = CharOf(*string); 1351 ++string; 1352 } 1353 *ptr = string; 1354 TRACE(("...decode_keyvalue %#x\n", value)); 1355 return value; 1356} 1357 1358static int 1359matchArg(XrmOptionDescRec * table, const char *param) 1360{ 1361 int result = -1; 1362 int n; 1363 int ch; 1364 1365 for (n = 0; (ch = table->option[n]) != '\0'; ++n) { 1366 if (param[n] == ch) { 1367 result = n; 1368 } else { 1369 if (param[n] != '\0') 1370 result = -1; 1371 break; 1372 } 1373 } 1374 1375 return result; 1376} 1377 1378/* return the number of argv[] entries which constitute arguments of option */ 1379static int 1380countArg(XrmOptionDescRec * item) 1381{ 1382 int result = 0; 1383 1384 switch (item->argKind) { 1385 case XrmoptionNoArg: 1386 /* FALLTHRU */ 1387 case XrmoptionIsArg: 1388 /* FALLTHRU */ 1389 case XrmoptionStickyArg: 1390 break; 1391 case XrmoptionSepArg: 1392 /* FALLTHRU */ 1393 case XrmoptionResArg: 1394 /* FALLTHRU */ 1395 case XrmoptionSkipArg: 1396 result = 1; 1397 break; 1398 case XrmoptionSkipLine: 1399 break; 1400 case XrmoptionSkipNArgs: 1401 result = (int) (long) (item->value); 1402 break; 1403 } 1404 return result; 1405} 1406 1407#define isOption(string) (Boolean)((string)[0] == '-' || (string)[0] == '+') 1408 1409/* 1410 * Parse the argument list, more/less as XtInitialize, etc., would do, so we 1411 * can find our own "-help" and "-version" options reliably. Improve on just 1412 * doing that, by detecting ambiguous options (things that happen to match the 1413 * abbreviated option we are examining), and making it smart enough to handle 1414 * "-d" as an abbreviation for "-display". Doing this requires checking the 1415 * standard table (something that the X libraries should do). 1416 */ 1417static XrmOptionDescRec * 1418parseArg(int *num, char **argv, char **valuep) 1419{ 1420 /* table adapted from XtInitialize, used here to improve abbreviations */ 1421 /* *INDENT-OFF* */ 1422#define DATA(option,kind) { option, NULL, kind, (XtPointer) NULL } 1423 static XrmOptionDescRec opTable[] = { 1424 DATA("+synchronous", XrmoptionNoArg), 1425 DATA("-background", XrmoptionSepArg), 1426 DATA("-bd", XrmoptionSepArg), 1427 DATA("-bg", XrmoptionSepArg), 1428 DATA("-bordercolor", XrmoptionSepArg), 1429 DATA("-borderwidth", XrmoptionSepArg), 1430 DATA("-bw", XrmoptionSepArg), 1431 DATA("-display", XrmoptionSepArg), 1432 DATA("-fg", XrmoptionSepArg), 1433 DATA("-fn", XrmoptionSepArg), 1434 DATA("-font", XrmoptionSepArg), 1435 DATA("-foreground", XrmoptionSepArg), 1436 DATA("-iconic", XrmoptionNoArg), 1437 DATA("-name", XrmoptionSepArg), 1438 DATA("-reverse", XrmoptionNoArg), 1439 DATA("-selectionTimeout", XrmoptionSepArg), 1440 DATA("-synchronous", XrmoptionNoArg), 1441 DATA("-title", XrmoptionSepArg), 1442 DATA("-xnllanguage", XrmoptionSepArg), 1443 DATA("-xrm", XrmoptionResArg), 1444 DATA("-xtsessionID", XrmoptionSepArg), 1445 /* These xterm options are processed after XtOpenApplication */ 1446#if defined(TIOCCONS) || defined(SRIOCSREDIR) 1447 DATA("-C", XrmoptionNoArg), 1448#endif /* TIOCCONS */ 1449 DATA("-S", XrmoptionStickyArg), 1450 DATA("-D", XrmoptionNoArg), 1451 }; 1452#undef DATA 1453 /* *INDENT-ON* */ 1454 1455 XrmOptionDescRec *result = 0; 1456 Cardinal inlist; 1457 Cardinal limit = XtNumber(optionDescList) + XtNumber(opTable); 1458 int atbest = -1; 1459 int best = -1; 1460 int test; 1461 Boolean exact = False; 1462 int ambiguous1 = -1; 1463 int ambiguous2 = -1; 1464 char *option; 1465 char *value; 1466 1467#define ITEM(n) ((Cardinal)(n) < XtNumber(optionDescList) \ 1468 ? &optionDescList[n] \ 1469 : &opTable[(Cardinal)(n) - XtNumber(optionDescList)]) 1470 1471 if ((option = argv[*num]) != 0) { 1472 Boolean need_value; 1473 Boolean have_value = False; 1474 1475 TRACE(("parseArg %s\n", option)); 1476 if ((value = argv[(*num) + 1]) != 0) { 1477 have_value = (Boolean) !isOption(value); 1478 } 1479 for (inlist = 0; inlist < limit; ++inlist) { 1480 XrmOptionDescRec *check = ITEM(inlist); 1481 1482 test = matchArg(check, option); 1483 if (test < 0) 1484 continue; 1485 1486 /* check for exact match */ 1487 if ((test + 1) == (int) strlen(check->option)) { 1488 if (check->argKind == XrmoptionStickyArg) { 1489 if (strlen(option) > strlen(check->option)) { 1490 exact = True; 1491 atbest = (int) inlist; 1492 break; 1493 } 1494 } else if ((test + 1) == (int) strlen(option)) { 1495 exact = True; 1496 atbest = (int) inlist; 1497 break; 1498 } 1499 } 1500 1501 need_value = (Boolean) (test > 0 && countArg(check) > 0); 1502 1503 if (need_value && value != 0) { 1504 ; 1505 } else if (need_value ^ have_value) { 1506 TRACE(("...skipping, need %d vs have %d\n", need_value, have_value)); 1507 continue; 1508 } 1509 1510 /* special-case for our own options - always allow abbreviation */ 1511 if (test > 0 1512 && ITEM(inlist)->argKind >= XrmoptionSkipArg) { 1513 atbest = (int) inlist; 1514 if (ITEM(inlist)->argKind == XrmoptionSkipNArgs) { 1515 /* in particular, silence a warning about ambiguity */ 1516 exact = 1; 1517 } 1518 break; 1519 } 1520 if (test > best) { 1521 best = test; 1522 atbest = (int) inlist; 1523 } else if (test == best) { 1524 if (atbest >= 0) { 1525 if (atbest > 0) { 1526 ambiguous1 = (int) inlist; 1527 ambiguous2 = (int) atbest; 1528 } 1529 atbest = -1; 1530 } 1531 } 1532 } 1533 } 1534 1535 *valuep = 0; 1536 if (atbest >= 0) { 1537 result = ITEM(atbest); 1538 if (!exact) { 1539 if (ambiguous1 >= 0 && ambiguous2 >= 0) { 1540 xtermWarning("ambiguous option \"%s\" vs \"%s\"\n", 1541 ITEM(ambiguous1)->option, 1542 ITEM(ambiguous2)->option); 1543 } else if (strlen(option) > strlen(result->option)) { 1544 result = 0; 1545 } 1546 } 1547 if (result != 0) { 1548 TRACE(("...result %s\n", result->option)); 1549 /* expand abbreviations */ 1550 if (result->argKind != XrmoptionStickyArg) { 1551 if (strcmp(argv[*num], result->option)) { 1552 argv[*num] = x_strdup(result->option); 1553 } 1554 } 1555 1556 /* adjust (*num) to skip option value */ 1557 (*num) += countArg(result); 1558 TRACE(("...next %s\n", NonNull(argv[*num]))); 1559 if (result->argKind == XrmoptionSkipArg) { 1560 *valuep = argv[*num]; 1561 TRACE(("...parameter %s\n", NonNull(*valuep))); 1562 } 1563 } 1564 } 1565#undef ITEM 1566 return result; 1567} 1568 1569static void 1570Syntax(char *badOption) 1571{ 1572 OptionHelp *opt; 1573 OptionHelp *list = sortedOpts(xtermOptions, optionDescList, XtNumber(optionDescList)); 1574 int col; 1575 1576 TRACE(("Syntax error at %s\n", badOption)); 1577 xtermWarning("bad command line option \"%s\"\r\n\n", badOption); 1578 1579 fprintf(stderr, "usage: %s", ProgramName); 1580 col = 8 + (int) strlen(ProgramName); 1581 for (opt = list; opt->opt; opt++) { 1582 int len = 3 + (int) strlen(opt->opt); /* space [ string ] */ 1583 if (col + len > 79) { 1584 fprintf(stderr, "\r\n "); /* 3 spaces */ 1585 col = 3; 1586 } 1587 fprintf(stderr, " [%s]", opt->opt); 1588 col += len; 1589 } 1590 1591 fprintf(stderr, "\r\n\nType %s -help for a full description.\r\n\n", 1592 ProgramName); 1593 exit(1); 1594} 1595 1596static void 1597Version(void) 1598{ 1599 printf("%s\n", xtermVersion()); 1600 fflush(stdout); 1601} 1602 1603static void 1604Help(void) 1605{ 1606 OptionHelp *opt; 1607 OptionHelp *list = sortedOpts(xtermOptions, optionDescList, XtNumber(optionDescList)); 1608 const char **cpp; 1609 1610 printf("%s usage:\n %s [-options ...] [-e command args]\n\n", 1611 xtermVersion(), ProgramName); 1612 printf("where options include:\n"); 1613 for (opt = list; opt->opt; opt++) { 1614 printf(" %-28s %s\n", opt->opt, opt->desc); 1615 } 1616 1617 putchar('\n'); 1618 for (cpp = message; *cpp; cpp++) 1619 puts(*cpp); 1620 putchar('\n'); 1621 fflush(stdout); 1622} 1623 1624#if defined(TIOCCONS) || defined(SRIOCSREDIR) 1625/* ARGSUSED */ 1626static Boolean 1627ConvertConsoleSelection(Widget w GCC_UNUSED, 1628 Atom *selection GCC_UNUSED, 1629 Atom *target GCC_UNUSED, 1630 Atom *type GCC_UNUSED, 1631 XtPointer *value GCC_UNUSED, 1632 unsigned long *length GCC_UNUSED, 1633 int *format GCC_UNUSED) 1634{ 1635 /* we don't save console output, so can't offer it */ 1636 return False; 1637} 1638#endif /* TIOCCONS */ 1639 1640/* 1641 * DeleteWindow(): Action proc to implement ICCCM delete_window. 1642 */ 1643/* ARGSUSED */ 1644static void 1645DeleteWindow(Widget w, 1646 XEvent *event GCC_UNUSED, 1647 String *params GCC_UNUSED, 1648 Cardinal *num_params GCC_UNUSED) 1649{ 1650#if OPT_TEK4014 1651 if (w == toplevel) { 1652 if (TEK4014_SHOWN(term)) 1653 hide_vt_window(); 1654 else 1655 do_hangup(w, (XtPointer) 0, (XtPointer) 0); 1656 } else if (TScreenOf(term)->Vshow) 1657 hide_tek_window(); 1658 else 1659#endif 1660 do_hangup(w, (XtPointer) 0, (XtPointer) 0); 1661} 1662 1663/* ARGSUSED */ 1664static void 1665KeyboardMapping(Widget w GCC_UNUSED, 1666 XEvent *event, 1667 String *params GCC_UNUSED, 1668 Cardinal *num_params GCC_UNUSED) 1669{ 1670 switch (event->type) { 1671 case MappingNotify: 1672 XRefreshKeyboardMapping(&event->xmapping); 1673 break; 1674 } 1675} 1676 1677static XtActionsRec actionProcs[] = 1678{ 1679 {"DeleteWindow", DeleteWindow}, 1680 {"KeyboardMapping", KeyboardMapping}, 1681}; 1682 1683/* 1684 * Some platforms use names such as /dev/tty01, others /dev/pts/1. Parse off 1685 * the "tty01" or "pts/1" portion, and return that for use as an identifier for 1686 * utmp. 1687 */ 1688static char * 1689my_pty_name(char *device) 1690{ 1691 size_t len = strlen(device); 1692 Bool name = False; 1693 1694 while (len != 0) { 1695 int ch = device[len - 1]; 1696 if (isdigit(ch)) { 1697 len--; 1698 } else if (ch == '/') { 1699 if (name) 1700 break; 1701 len--; 1702 } else if (isalpha(ch)) { 1703 name = True; 1704 len--; 1705 } else { 1706 break; 1707 } 1708 } 1709 TRACE(("my_pty_name(%s) -> '%s'\n", device, device + len)); 1710 return device + len; 1711} 1712 1713/* 1714 * If the name contains a '/', it is a "pts/1" case. Otherwise, return the 1715 * last few characters for a utmp identifier. 1716 */ 1717static char * 1718my_pty_id(char *device) 1719{ 1720 char *name = my_pty_name(device); 1721 char *leaf = x_basename(name); 1722 1723 if (name == leaf) { /* no '/' in the name */ 1724 int len = (int) strlen(leaf); 1725 if (PTYCHARLEN < len) 1726 leaf = leaf + (len - PTYCHARLEN); 1727 } 1728 TRACE(("my_pty_id (%s) -> '%s'\n", device, leaf)); 1729 return leaf; 1730} 1731 1732/* 1733 * Set the tty/pty identifier 1734 */ 1735static void 1736set_pty_id(char *device, char *id) 1737{ 1738 char *name = my_pty_name(device); 1739 char *leaf = x_basename(name); 1740 1741 if (name == leaf) { 1742 strcpy(my_pty_id(device), id); 1743 } else { 1744 strcpy(leaf, id); 1745 } 1746 TRACE(("set_pty_id(%s) -> '%s'\n", id, device)); 1747} 1748 1749/* 1750 * The original -S option accepts two characters to identify the pty, and a 1751 * file-descriptor (assumed to be nonzero). That is not general enough, so we 1752 * check first if the option contains a '/' to delimit the two fields, and if 1753 * not, fall-thru to the original logic. 1754 */ 1755static Bool 1756ParseSccn(char *option) 1757{ 1758 char *leaf = x_basename(option); 1759 Bool code = False; 1760 1761 if (leaf != option) { 1762 if (leaf - option > 0 1763 && isdigit(CharOf(*leaf)) 1764 && sscanf(leaf, "%d", &am_slave) == 1) { 1765 size_t len = (size_t) (leaf - option - 1); 1766 /* 1767 * If we have a slash, we only care about the part after the slash, 1768 * which is a file-descriptor. The part before the slash can be 1769 * the /dev/pts/XXX value, but since we do not need to reopen it, 1770 * it is useful mainly for display in a "ps -ef". 1771 */ 1772 strncpy(passedPty, option, len); 1773 passedPty[len] = 0; 1774 code = True; 1775 } 1776 } else { 1777 code = (sscanf(option, "%c%c%d", 1778 passedPty, passedPty + 1, &am_slave) == 3); 1779 } 1780 TRACE(("ParseSccn(%s) = '%s' %d (%s)\n", option, 1781 passedPty, am_slave, code ? "OK" : "ERR")); 1782 return code; 1783} 1784 1785#if defined(USE_SYSV_UTMP) && !defined(USE_UTEMPTER) 1786/* 1787 * From "man utmp": 1788 * xterm and other terminal emulators directly create a USER_PROCESS record 1789 * and generate the ut_id by using the last two letters of /dev/ttyp%c or by 1790 * using p%d for /dev/pts/%d. If they find a DEAD_PROCESS for this id, they 1791 * recycle it, otherwise they create a new entry. If they can, they will mark 1792 * it as DEAD_PROCESS on exiting and it is advised that they null ut_line, 1793 * ut_time, ut_user and ut_host as well. 1794 * 1795 * Generally ut_id allows no more than 3 characters (plus null), even if the 1796 * pty implementation allows more than 3 digits. 1797 */ 1798static char * 1799my_utmp_id(char *device) 1800{ 1801 typedef struct UTMP_STR UTMP_STRUCT; 1802#define UTIDSIZE (sizeof(((UTMP_STRUCT *)NULL)->ut_id)) 1803 static char result[UTIDSIZE + 1]; 1804 1805#if defined(__SCO__) || defined(__UNIXWARE__) 1806 /* 1807 * Legend does not support old-style pty's, has no related compatibility 1808 * issues, and can use the available space in ut_id differently from the 1809 * default convention. 1810 * 1811 * This scheme is intended to avoid conflicts both with other users of 1812 * utmpx as well as between multiple xterms. First, Legend uses all of the 1813 * characters of ut_id, and adds no terminating NUL is required (the 1814 * default scheme may add a trailing NUL). Second, all xterm entries will 1815 * start with the letter 'x' followed by three digits, which will be the 1816 * last three digits of the device name, regardless of the format of the 1817 * device name, with leading 0's added where necessary. For instance, an 1818 * xterm on /dev/pts/3 will have a ut_id of x003; an xterm on /dev/pts123 1819 * will have a ut_id of x123. Under the other convention, /dev/pts/3 would 1820 * have a ut_id of p3 and /dev/pts123 would have a ut_id of p123. 1821 */ 1822 int len, n; 1823 1824 len = strlen(device); 1825 n = UTIDSIZE; 1826 result[n] = '\0'; 1827 while ((n > 0) && (len > 0) && isdigit(device[len - 1])) 1828 result[--n] = device[--len]; 1829 while (n > 0) 1830 result[--n] = '0'; 1831 result[0] = 'x'; 1832#else 1833 char *name = my_pty_name(device); 1834 char *leaf = x_basename(name); 1835 size_t len = strlen(leaf); 1836 1837 if ((UTIDSIZE - 1) < len) 1838 leaf = leaf + (len - (UTIDSIZE - 1)); 1839 sprintf(result, "p%s", leaf); 1840#endif 1841 1842 TRACE(("my_utmp_id (%s) -> '%s'\n", device, result)); 1843 return result; 1844} 1845#endif /* USE_SYSV_UTMP */ 1846 1847#ifdef USE_POSIX_SIGNALS 1848 1849typedef void (*sigfunc) (int); 1850 1851/* make sure we sure we ignore SIGCHLD for the cases parent 1852 has just been stopped and not actually killed */ 1853 1854static sigfunc 1855posix_signal(int signo, sigfunc func) 1856{ 1857 struct sigaction act, oact; 1858 1859 act.sa_handler = func; 1860 sigemptyset(&act.sa_mask); 1861#ifdef SA_RESTART 1862 act.sa_flags = SA_NOCLDSTOP | SA_RESTART; 1863#else 1864 act.sa_flags = SA_NOCLDSTOP; 1865#endif 1866 if (sigaction(signo, &act, &oact) < 0) 1867 return (SIG_ERR); 1868 return (oact.sa_handler); 1869} 1870 1871#endif /* USE_POSIX_SIGNALS */ 1872 1873#if defined(DISABLE_SETUID) || defined(USE_UTMP_SETGID) 1874static void 1875disableSetUid(void) 1876{ 1877 TRACE(("process %d disableSetUid\n", (int) getpid())); 1878 if (setuid(save_ruid) == -1) { 1879 xtermWarning("unable to reset uid\n"); 1880 exit(1); 1881 } 1882 TRACE_IDS; 1883} 1884#else 1885#define disableSetUid() /* nothing */ 1886#endif /* DISABLE_SETUID */ 1887 1888#if defined(DISABLE_SETGID) || defined(USE_UTMP_SETGID) 1889static void 1890disableSetGid(void) 1891{ 1892 TRACE(("process %d disableSetGid\n", (int) getpid())); 1893 if (setegid(save_rgid) == -1) { 1894 xtermWarning("unable to reset effective gid\n"); 1895 exit(1); 1896 } 1897 TRACE_IDS; 1898} 1899#else 1900#define disableSetGid() /* nothing */ 1901#endif /* DISABLE_SETGID */ 1902 1903#if defined(HAVE_POSIX_SAVED_IDS) 1904#if (!defined(USE_UTEMPTER) || !defined(DISABLE_SETGID)) 1905static void 1906setEffectiveGroup(gid_t group) 1907{ 1908 TRACE(("process %d setEffectiveGroup(%d)\n", (int) getpid(), (int) group)); 1909 if (setegid(group) == -1) { 1910#ifdef __MVS__ 1911 if (!(errno == EMVSERR)) /* could happen if _BPX_SHAREAS=REUSE */ 1912#endif 1913 { 1914 xtermPerror("setegid(%d)", (int) group); 1915 } 1916 } 1917 TRACE_IDS; 1918} 1919#endif 1920 1921#if !defined(USE_UTMP_SETGID) && (!defined(USE_UTEMPTER) || !defined(DISABLE_SETUID)) 1922static void 1923setEffectiveUser(uid_t user) 1924{ 1925 TRACE(("process %d setEffectiveUser(%d)\n", (int) getpid(), (int) user)); 1926 if (seteuid(user) == -1) { 1927#ifdef __MVS__ 1928 if (!(errno == EMVSERR)) 1929#endif 1930 { 1931 xtermPerror("seteuid(%d)", (int) user); 1932 } 1933 } 1934 TRACE_IDS; 1935} 1936#endif 1937#endif /* HAVE_POSIX_SAVED_IDS */ 1938 1939int 1940main(int argc, char *argv[]ENVP_ARG) 1941{ 1942#if OPT_MAXIMIZE 1943#define DATA(name) { #name, es##name } 1944 static FlagList tblFullscreen[] = 1945 { 1946 DATA(Always), 1947 DATA(Never) 1948 }; 1949#undef DATA 1950#endif 1951 1952 Widget form_top, menu_top; 1953 Dimension menu_high; 1954 TScreen *screen; 1955 int mode; 1956 char *my_class = x_strdup(DEFCLASS); 1957 Window winToEmbedInto = None; 1958 1959 ProgramName = argv[0]; 1960 1961#ifdef HAVE_POSIX_SAVED_IDS 1962 save_euid = geteuid(); 1963 save_egid = getegid(); 1964#endif 1965 1966 save_ruid = getuid(); 1967 save_rgid = getgid(); 1968 1969#if defined(DISABLE_SETUID) || defined(DISABLE_SETGID) 1970#if defined(DISABLE_SETUID) 1971 disableSetUid(); 1972#endif 1973#if defined(DISABLE_SETGID) 1974 disableSetGid(); 1975#endif 1976 TRACE_IDS; 1977#endif 1978 1979 /* extra length in case longer tty name like /dev/ttyq255 */ 1980 ttydev = TypeMallocN(char, sizeof(TTYDEV) + 80); 1981#ifdef USE_PTY_DEVICE 1982 ptydev = TypeMallocN(char, sizeof(PTYDEV) + 80); 1983 if (!ttydev || !ptydev) 1984#else 1985 if (!ttydev) 1986#endif 1987 { 1988 xtermWarning("unable to allocate memory for ttydev or ptydev\n"); 1989 exit(1); 1990 } 1991 strcpy(ttydev, TTYDEV); 1992#ifdef USE_PTY_DEVICE 1993 strcpy(ptydev, PTYDEV); 1994#endif 1995 1996#if defined(USE_UTMP_SETGID) 1997 get_pty(NULL, NULL); 1998 disableSetUid(); 1999 disableSetGid(); 2000 TRACE_IDS; 2001#define get_pty(pty, from) really_get_pty(pty, from) 2002#endif 2003 2004 /* Do these first, since we may not be able to open the display */ 2005 TRACE_OPTS(xtermOptions, optionDescList, XtNumber(optionDescList)); 2006 TRACE_ARGV("Before XtOpenApplication", argv); 2007 if (argc > 1) { 2008 XrmOptionDescRec *option_ptr; 2009 char *option_value; 2010 int n; 2011 Bool quit = False; 2012 2013 for (n = 1; n < argc; n++) { 2014 if ((option_ptr = parseArg(&n, argv, &option_value)) == 0) { 2015 if (argv[n] == 0) { 2016 break; 2017 } else if (isOption(argv[n])) { 2018 Syntax(argv[n]); 2019 } else if (explicit_shname != 0) { 2020 xtermWarning("Explicit shell already was %s\n", explicit_shname); 2021 Syntax(argv[n]); 2022 } 2023 explicit_shname = xtermFindShell(argv[n], True); 2024 if (explicit_shname == 0) 2025 exit(0); 2026 TRACE(("...explicit shell %s\n", explicit_shname)); 2027 } else if (!strcmp(option_ptr->option, "-e")) { 2028 command_to_exec = (argv + n + 1); 2029 if (!command_to_exec[0]) 2030 Syntax(argv[n]); 2031 break; 2032 } else if (!strcmp(option_ptr->option, "-version")) { 2033 Version(); 2034 quit = True; 2035 } else if (!strcmp(option_ptr->option, "-help")) { 2036 Help(); 2037 quit = True; 2038 } else if (!strcmp(option_ptr->option, "-class")) { 2039 free(my_class); 2040 if ((my_class = x_strdup(option_value)) == 0) { 2041 Help(); 2042 quit = True; 2043 } 2044 } else if (!strcmp(option_ptr->option, "-into")) { 2045 char *endPtr; 2046 winToEmbedInto = (Window) strtol(option_value, &endPtr, 0); 2047 } 2048 } 2049 if (quit) 2050 exit(0); 2051 /* 2052 * If there is anything left unparsed, and we're not using "-e", 2053 * then give up. 2054 */ 2055 if (n < argc && !command_to_exec) { 2056 Syntax(argv[n]); 2057 } 2058 } 2059 2060 /* This dumped core on HP-UX 9.05 with X11R5 */ 2061#if OPT_I18N_SUPPORT 2062 XtSetLanguageProc(NULL, NULL, NULL); 2063#endif 2064 2065#ifdef TERMIO_STRUCT /* { */ 2066 /* Initialization is done here rather than above in order 2067 * to prevent any assumptions about the order of the contents 2068 * of the various terminal structures (which may change from 2069 * implementation to implementation). 2070 */ 2071 memset(&d_tio, 0, sizeof(d_tio)); 2072 d_tio.c_iflag = ICRNL | IXON; 2073#ifdef TAB3 2074 d_tio.c_oflag = OPOST | ONLCR | TAB3; 2075#else 2076#ifdef ONLCR 2077 d_tio.c_oflag = OPOST | ONLCR; 2078#else 2079 d_tio.c_oflag = OPOST; 2080#endif 2081#endif 2082 { 2083 Cardinal nn; 2084 2085 /* fill in default-values */ 2086 for (nn = 0; nn < XtNumber(known_ttyChars); ++nn) { 2087 if (validTtyChar(d_tio, nn)) { 2088 d_tio.c_cc[known_ttyChars[nn].sysMode] = 2089 (cc_t) known_ttyChars[nn].myDefault; 2090 } 2091 } 2092 } 2093#if defined(macII) || defined(ATT) || defined(CRAY) /* { */ 2094 d_tio.c_cflag = VAL_LINE_SPEED | CS8 | CREAD | PARENB | HUPCL; 2095 d_tio.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK; 2096#ifdef ECHOKE 2097 d_tio.c_lflag |= ECHOKE | IEXTEN; 2098#endif 2099#ifdef ECHOCTL 2100 d_tio.c_lflag |= ECHOCTL | IEXTEN; 2101#endif 2102#ifndef USE_TERMIOS /* { */ 2103 d_tio.c_line = 0; 2104#endif /* } */ 2105#ifdef HAS_LTCHARS /* { */ 2106 d_ltc.t_suspc = CSUSP; /* t_suspc */ 2107 d_ltc.t_dsuspc = CDSUSP; /* t_dsuspc */ 2108 d_ltc.t_rprntc = CRPRNT; 2109 d_ltc.t_flushc = CFLUSH; 2110 d_ltc.t_werasc = CWERASE; 2111 d_ltc.t_lnextc = CLNEXT; 2112#endif /* } HAS_LTCHARS */ 2113#ifdef TIOCLSET /* { */ 2114 d_lmode = 0; 2115#endif /* } TIOCLSET */ 2116#else /* }{ else !macII, ATT, CRAY */ 2117#ifndef USE_POSIX_TERMIOS 2118#ifdef BAUD_0 /* { */ 2119 d_tio.c_cflag = CS8 | CREAD | PARENB | HUPCL; 2120#else /* }{ !BAUD_0 */ 2121 d_tio.c_cflag = VAL_LINE_SPEED | CS8 | CREAD | PARENB | HUPCL; 2122#endif /* } !BAUD_0 */ 2123#else /* USE_POSIX_TERMIOS */ 2124 d_tio.c_cflag = CS8 | CREAD | PARENB | HUPCL; 2125 cfsetispeed(&d_tio, VAL_LINE_SPEED); 2126 cfsetospeed(&d_tio, VAL_LINE_SPEED); 2127#endif 2128 d_tio.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK; 2129#ifdef ECHOKE 2130 d_tio.c_lflag |= ECHOKE | IEXTEN; 2131#endif 2132#ifdef ECHOCTL 2133 d_tio.c_lflag |= ECHOCTL | IEXTEN; 2134#endif 2135#ifndef USE_POSIX_TERMIOS 2136#ifdef NTTYDISC 2137 d_tio.c_line = NTTYDISC; 2138#else 2139 d_tio.c_line = 0; 2140#endif 2141#endif /* USE_POSIX_TERMIOS */ 2142#ifdef __sgi 2143 d_tio.c_cflag &= ~(HUPCL | PARENB); 2144 d_tio.c_iflag |= BRKINT | ISTRIP | IGNPAR; 2145#endif 2146#ifdef __MVS__ 2147 d_tio.c_cflag &= ~(HUPCL | PARENB); 2148#endif 2149 { 2150 Cardinal nn; 2151 int i; 2152 2153 /* try to inherit tty settings */ 2154 for (i = 0; i <= 2; i++) { 2155 TERMIO_STRUCT deftio; 2156 if (ttyGetAttr(i, &deftio) == 0) { 2157 for (nn = 0; nn < XtNumber(known_ttyChars); ++nn) { 2158 if (validTtyChar(d_tio, nn)) { 2159 d_tio.c_cc[known_ttyChars[nn].sysMode] = 2160 deftio.c_cc[known_ttyChars[nn].sysMode]; 2161 } 2162 } 2163 break; 2164 } 2165 } 2166 } 2167#if defined(USE_TERMIOS) || defined(USE_POSIX_TERMIOS) /* { */ 2168 d_tio.c_cc[VMIN] = 1; 2169 d_tio.c_cc[VTIME] = 0; 2170#endif /* } */ 2171#ifdef HAS_LTCHARS /* { */ 2172 d_ltc.t_suspc = CharOf('\000'); /* t_suspc */ 2173 d_ltc.t_dsuspc = CharOf('\000'); /* t_dsuspc */ 2174 d_ltc.t_rprntc = CharOf('\377'); /* reserved... */ 2175 d_ltc.t_flushc = CharOf('\377'); 2176 d_ltc.t_werasc = CharOf('\377'); 2177 d_ltc.t_lnextc = CharOf('\377'); 2178#endif /* } HAS_LTCHARS */ 2179 2180#ifdef TIOCLSET /* { */ 2181 d_lmode = 0; 2182#endif /* } TIOCLSET */ 2183#endif /* } macII, ATT, CRAY */ 2184#endif /* } TERMIO_STRUCT */ 2185 2186 /* Init the Toolkit. */ 2187 { 2188#if defined(HAVE_POSIX_SAVED_IDS) && !defined(USE_UTMP_SETGID) && !defined(USE_UTEMPTER) 2189 setEffectiveGroup(save_rgid); 2190 setEffectiveUser(save_ruid); 2191 TRACE_IDS; 2192#endif 2193 init_colored_cursor(); 2194 2195 toplevel = xtermOpenApplication(&app_con, 2196 my_class, 2197 optionDescList, 2198 XtNumber(optionDescList), 2199 &argc, (String *) argv, 2200 fallback_resources, 2201 sessionShellWidgetClass, 2202 NULL, 0); 2203 2204 XtGetApplicationResources(toplevel, (XtPointer) &resource, 2205 application_resources, 2206 XtNumber(application_resources), NULL, 0); 2207 TRACE_XRES(); 2208#if OPT_MAXIMIZE 2209 resource.fullscreen = extendedBoolean(resource.fullscreen_s, 2210 tblFullscreen, 2211 XtNumber(tblFullscreen)); 2212#endif 2213 VTInitTranslations(); 2214#if OPT_PTY_HANDSHAKE 2215 resource.wait_for_map0 = resource.wait_for_map; 2216#endif 2217 2218#if defined(HAVE_POSIX_SAVED_IDS) && !defined(USE_UTMP_SETGID) 2219#if !defined(DISABLE_SETUID) || !defined(DISABLE_SETGID) 2220#if !defined(DISABLE_SETUID) 2221 setEffectiveUser(save_euid); 2222#endif 2223#if !defined(DISABLE_SETGID) 2224 setEffectiveGroup(save_egid); 2225#endif 2226 TRACE_IDS; 2227#endif 2228#endif 2229 } 2230 2231 /* 2232 * ICCCM delete_window. 2233 */ 2234 XtAppAddActions(app_con, actionProcs, XtNumber(actionProcs)); 2235 2236 /* 2237 * fill in terminal modes 2238 */ 2239 if (resource.tty_modes) { 2240 int n = parse_tty_modes(resource.tty_modes, ttymodelist); 2241 if (n < 0) { 2242 xtermWarning("bad tty modes \"%s\"\n", resource.tty_modes); 2243 } else if (n > 0) { 2244 override_tty_modes = True; 2245 } 2246 } 2247 initZIconBeep(); 2248 hold_screen = resource.hold_screen ? 1 : 0; 2249 if (resource.icon_geometry != NULL) { 2250 int scr, junk; 2251 int ix, iy; 2252 Arg args[2]; 2253 2254 for (scr = 0; /* yyuucchh */ 2255 XtScreen(toplevel) != ScreenOfDisplay(XtDisplay(toplevel), scr); 2256 scr++) ; 2257 2258 args[0].name = XtNiconX; 2259 args[1].name = XtNiconY; 2260 XGeometry(XtDisplay(toplevel), scr, resource.icon_geometry, "", 2261 0, 0, 0, 0, 0, &ix, &iy, &junk, &junk); 2262 args[0].value = (XtArgVal) ix; 2263 args[1].value = (XtArgVal) iy; 2264 XtSetValues(toplevel, args, 2); 2265 } 2266 2267 XtSetValues(toplevel, ourTopLevelShellArgs, 2268 number_ourTopLevelShellArgs); 2269 2270#if OPT_WIDE_CHARS 2271 /* seems as good a place as any */ 2272 init_classtab(); 2273#endif 2274 2275 /* Parse the rest of the command line */ 2276 TRACE_ARGV("After XtOpenApplication", argv); 2277 for (argc--, argv++; argc > 0; argc--, argv++) { 2278 if (!isOption(*argv)) { 2279#ifdef VMS 2280 Syntax(*argv); 2281#else 2282 if (argc > 1) 2283 Syntax(*argv); 2284 continue; 2285#endif 2286 } 2287 2288 TRACE(("parsing %s\n", argv[0])); 2289 switch (argv[0][1]) { 2290 case 'C': 2291#if defined(TIOCCONS) || defined(SRIOCSREDIR) 2292#ifndef __sgi 2293 { 2294 struct stat sbuf; 2295 2296 /* Must be owner and have read/write permission. 2297 xdm cooperates to give the console the right user. */ 2298 if (!stat("/dev/console", &sbuf) && 2299 (sbuf.st_uid == save_ruid) && 2300 !access("/dev/console", R_OK | W_OK)) { 2301 Console = True; 2302 } else 2303 Console = False; 2304 } 2305#else /* __sgi */ 2306 Console = True; 2307#endif /* __sgi */ 2308#endif /* TIOCCONS */ 2309 continue; 2310 case 'S': 2311 if (!ParseSccn(*argv + 2)) 2312 Syntax(*argv); 2313 continue; 2314#ifdef DEBUG 2315 case 'D': 2316 debug = True; 2317 continue; 2318#endif /* DEBUG */ 2319 case 'c': 2320 if (strcmp(argv[0], "-class")) 2321 Syntax(*argv); 2322 argc--, argv++; 2323 continue; 2324 case 'e': 2325 if (strcmp(argv[0], "-e")) 2326 Syntax(*argv); 2327 command_to_exec = (argv + 1); 2328 break; 2329 case 'i': 2330 if (strcmp(argv[0], "-into")) 2331 Syntax(*argv); 2332 argc--, argv++; 2333 continue; 2334 2335 default: 2336 Syntax(*argv); 2337 } 2338 break; 2339 } 2340 2341 SetupMenus(toplevel, &form_top, &menu_top, &menu_high); 2342 2343 term = (XtermWidget) XtVaCreateManagedWidget("vt100", xtermWidgetClass, 2344 form_top, 2345#if OPT_TOOLBAR 2346 XtNmenuBar, menu_top, 2347 XtNresizable, True, 2348 XtNfromVert, menu_top, 2349 XtNleft, XawChainLeft, 2350 XtNright, XawChainRight, 2351 XtNtop, XawChainTop, 2352 XtNbottom, XawChainBottom, 2353 XtNmenuHeight, menu_high, 2354#endif 2355 (XtPointer) 0); 2356 decode_keyboard_type(term, &resource); 2357 2358 screen = TScreenOf(term); 2359 screen->inhibit = 0; 2360 2361#ifdef ALLOWLOGGING 2362 if (term->misc.logInhibit) 2363 screen->inhibit |= I_LOG; 2364#endif 2365 if (term->misc.signalInhibit) 2366 screen->inhibit |= I_SIGNAL; 2367#if OPT_TEK4014 2368 if (term->misc.tekInhibit) 2369 screen->inhibit |= I_TEK; 2370#endif 2371 2372 /* 2373 * We might start by showing the tek4014 window. 2374 */ 2375#if OPT_TEK4014 2376 if (screen->inhibit & I_TEK) 2377 TEK4014_ACTIVE(term) = False; 2378 2379 if (TEK4014_ACTIVE(term) && !TekInit()) 2380 SysError(ERROR_INIT); 2381#endif 2382 2383 /* 2384 * Start the toolbar at this point, after the first window has been setup. 2385 */ 2386#if OPT_TOOLBAR 2387 ShowToolbar(resource.toolBar); 2388#endif 2389 2390 xtermOpenSession(); 2391 2392 /* 2393 * Set title and icon name if not specified 2394 */ 2395 if (command_to_exec) { 2396 Arg args[2]; 2397 2398 if (!resource.title) { 2399 if (command_to_exec) { 2400 resource.title = x_basename(command_to_exec[0]); 2401 } /* else not reached */ 2402 } 2403 2404 if (!resource.icon_name) 2405 resource.icon_name = resource.title; 2406 XtSetArg(args[0], XtNtitle, resource.title); 2407 XtSetArg(args[1], XtNiconName, resource.icon_name); 2408 2409 TRACE(("setting:\n\ttitle \"%s\"\n\ticon \"%s\"\n\thint \"%s\"\n\tbased on command \"%s\"\n", 2410 resource.title, 2411 resource.icon_name, 2412 NonNull(resource.icon_hint), 2413 *command_to_exec)); 2414 2415 XtSetValues(toplevel, args, 2); 2416 } 2417#if OPT_LUIT_PROG 2418 if (term->misc.callfilter) { 2419 char **split_filter = x_splitargs(term->misc.localefilter); 2420 unsigned count_split = x_countargv(split_filter); 2421 unsigned count_exec = x_countargv(command_to_exec); 2422 unsigned count_using = (unsigned) (term->misc.use_encoding ? 2 : 0); 2423 2424 command_to_exec_with_luit = TypeCallocN(char *, 2425 (count_split 2426 + count_exec 2427 + count_using 2428 + 8)); 2429 if (command_to_exec_with_luit == NULL) 2430 SysError(ERROR_LUMALLOC); 2431 2432 x_appendargv(command_to_exec_with_luit, split_filter); 2433 if (count_using) { 2434 char *encoding_opt[4]; 2435 encoding_opt[0] = x_strdup("-encoding"); 2436 encoding_opt[1] = term->misc.locale_str; 2437 encoding_opt[2] = 0; 2438 x_appendargv(command_to_exec_with_luit, encoding_opt); 2439 } 2440 command_length_with_luit = x_countargv(command_to_exec_with_luit); 2441 if (count_exec) { 2442 char *delimiter[2]; 2443 delimiter[0] = x_strdup("--"); 2444 delimiter[1] = 0; 2445 x_appendargv(command_to_exec_with_luit, delimiter); 2446 x_appendargv(command_to_exec_with_luit, command_to_exec); 2447 } 2448 TRACE_ARGV("luit command", command_to_exec_with_luit); 2449 xtermSetenv("XTERM_FILTER", *command_to_exec_with_luit); 2450 } 2451#endif 2452 2453 if_DEBUG({ 2454 /* Set up stderr properly. Opening this log file cannot be 2455 done securely by a privileged xterm process (although we try), 2456 so the debug feature is disabled by default. */ 2457 char dbglogfile[TIMESTAMP_LEN + 20]; 2458 int i = -1; 2459 timestamp_filename(dbglogfile, "xterm.debug.log."); 2460 if (creat_as(save_ruid, save_rgid, False, dbglogfile, 0600) > 0) { 2461 i = open(dbglogfile, O_WRONLY | O_TRUNC); 2462 } 2463 if (i >= 0) { 2464 dup2(i, 2); 2465 2466 /* mark this file as close on exec */ 2467 (void) fcntl(i, F_SETFD, 1); 2468 } 2469 }); 2470 2471 spawnXTerm(term); 2472 2473#ifndef VMS 2474 /* Child process is out there, let's catch its termination */ 2475 2476#ifdef USE_POSIX_SIGNALS 2477 (void) posix_signal(SIGCHLD, reapchild); 2478#else 2479 (void) signal(SIGCHLD, reapchild); 2480#endif 2481 /* Realize procs have now been executed */ 2482 2483 if (am_slave >= 0) { /* Write window id so master end can read and use */ 2484 char buf[80]; 2485 2486 buf[0] = '\0'; 2487 sprintf(buf, "%lx\n", XtWindow(SHELL_OF(CURRENT_EMU()))); 2488 IGNORE_RC(write(screen->respond, buf, strlen(buf))); 2489 } 2490#ifdef AIXV3 2491#if (OSMAJORVERSION < 4) 2492 /* In AIXV3, xterms started from /dev/console have CLOCAL set. 2493 * This means we need to clear CLOCAL so that SIGHUP gets sent 2494 * to the slave-pty process when xterm exits. 2495 */ 2496 2497 { 2498 TERMIO_STRUCT tio; 2499 2500 if (ttyGetAttr(screen->respond, &tio) == -1) 2501 SysError(ERROR_TIOCGETP); 2502 2503 tio.c_cflag &= ~(CLOCAL); 2504 2505 if (ttySetAttr(screen->respond, &tio) == -1) 2506 SysError(ERROR_TIOCSETP); 2507 } 2508#endif 2509#endif 2510#if defined(USE_ANY_SYSV_TERMIO) || defined(__MVS__) 2511 if (0 > (mode = fcntl(screen->respond, F_GETFL, 0))) 2512 SysError(ERROR_F_GETFL); 2513#ifdef O_NDELAY 2514 mode |= O_NDELAY; 2515#else 2516 mode |= O_NONBLOCK; 2517#endif /* O_NDELAY */ 2518 if (fcntl(screen->respond, F_SETFL, mode)) 2519 SysError(ERROR_F_SETFL); 2520#else /* !USE_ANY_SYSV_TERMIO */ 2521 mode = 1; 2522 if (ioctl(screen->respond, FIONBIO, (char *) &mode) == -1) 2523 SysError(ERROR_FIONBIO); 2524#endif /* USE_ANY_SYSV_TERMIO, etc */ 2525 2526 /* The erase character is used to delete the current completion */ 2527#if OPT_DABBREV 2528#ifdef TERMIO_STRUCT 2529 screen->dabbrev_erase_char = d_tio.c_cc[VERASE]; 2530#else 2531 screen->dabbrev_erase_char = d_sg.sg_erase; 2532#endif 2533 TRACE(("set dabbrev erase_char %#x\n", screen->dabbrev_erase_char)); 2534#endif 2535 2536 FD_ZERO(&pty_mask); 2537 FD_ZERO(&X_mask); 2538 FD_ZERO(&Select_mask); 2539 FD_SET(screen->respond, &pty_mask); 2540 FD_SET(ConnectionNumber(screen->display), &X_mask); 2541 FD_SET(screen->respond, &Select_mask); 2542 FD_SET(ConnectionNumber(screen->display), &Select_mask); 2543 max_plus1 = ((screen->respond < ConnectionNumber(screen->display)) 2544 ? (1 + ConnectionNumber(screen->display)) 2545 : (1 + screen->respond)); 2546 2547#endif /* !VMS */ 2548 if_DEBUG({ 2549 TRACE(("debugging on pid %d\n", (int) getpid())); 2550 }); 2551 XSetErrorHandler(xerror); 2552 XSetIOErrorHandler(xioerror); 2553 IceSetIOErrorHandler(ice_error); 2554 2555 initPtyData(&VTbuffer); 2556#ifdef ALLOWLOGGING 2557 if (term->misc.log_on) { 2558 StartLog(term); 2559 } 2560#endif 2561 2562 xtermEmbedWindow(winToEmbedInto); 2563#if OPT_COLOR_RES 2564 TRACE(("checking reverseVideo before rv %s fg %s, bg %s\n", 2565 term->misc.re_verse0 ? "reverse" : "normal", 2566 NonNull(TScreenOf(term)->Tcolors[TEXT_FG].resource), 2567 NonNull(TScreenOf(term)->Tcolors[TEXT_BG].resource))); 2568 2569 if (term->misc.re_verse0) { 2570 if (isDefaultForeground(TScreenOf(term)->Tcolors[TEXT_FG].resource) 2571 && isDefaultBackground(TScreenOf(term)->Tcolors[TEXT_BG].resource)) { 2572 TScreenOf(term)->Tcolors[TEXT_FG].resource = x_strdup(XtDefaultBackground); 2573 TScreenOf(term)->Tcolors[TEXT_BG].resource = x_strdup(XtDefaultForeground); 2574 } else { 2575 ReverseVideo(term); 2576 } 2577 term->misc.re_verse = True; 2578 update_reversevideo(); 2579 TRACE(("updated reverseVideo after rv %s fg %s, bg %s\n", 2580 term->misc.re_verse ? "reverse" : "normal", 2581 NonNull(TScreenOf(term)->Tcolors[TEXT_FG].resource), 2582 NonNull(TScreenOf(term)->Tcolors[TEXT_BG].resource))); 2583 } 2584#endif /* OPT_COLOR_RES */ 2585 2586#if OPT_MAXIMIZE 2587 if (resource.maximized) 2588 RequestMaximize(term, True); 2589#endif 2590 for (;;) { 2591#if OPT_TEK4014 2592 if (TEK4014_ACTIVE(term)) 2593 TekRun(); 2594 else 2595#endif 2596 VTRun(term); 2597 } 2598} 2599 2600#if defined(__osf__) || (defined(__GLIBC__) && !defined(USE_USG_PTYS)) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) 2601#define USE_OPENPTY 1 2602static int opened_tty = -1; 2603#endif 2604 2605/* 2606 * This function opens up a pty master and stuffs its value into pty. 2607 * 2608 * If it finds one, it returns a value of 0. If it does not find one, 2609 * it returns a value of !0. This routine is designed to be re-entrant, 2610 * so that if a pty master is found and later, we find that the slave 2611 * has problems, we can re-enter this function and get another one. 2612 */ 2613static int 2614get_pty(int *pty, char *from GCC_UNUSED) 2615{ 2616 int result = 1; 2617 2618#if defined(USE_OPENPTY) 2619 result = openpty(pty, &opened_tty, ttydev, NULL, NULL); 2620#elif defined(HAVE_POSIX_OPENPT) && defined(HAVE_PTSNAME) && defined(HAVE_GRANTPT_PTY_ISATTY) 2621 if ((*pty = posix_openpt(O_RDWR)) >= 0) { 2622 char *name = ptsname(*pty); 2623 if (name != 0) { 2624 strcpy(ttydev, name); 2625 result = 0; 2626 } 2627 } 2628#ifdef USE_PTY_SEARCH 2629 if (result) { 2630 result = pty_search(pty); 2631 } 2632#endif 2633#elif defined(PUCC_PTYD) 2634 result = ((*pty = openrpty(ttydev, ptydev, 2635 (resource.utmpInhibit ? OPTY_NOP : OPTY_LOGIN), 2636 save_ruid, from)) < 0); 2637#elif defined(__QNXNTO__) 2638 result = pty_search(pty); 2639#else 2640#if defined(USE_USG_PTYS) || defined(__CYGWIN__) 2641#ifdef __GLIBC__ /* if __GLIBC__ and USE_USG_PTYS, we know glibc >= 2.1 */ 2642 /* GNU libc 2 allows us to abstract away from having to know the 2643 master pty device name. */ 2644 if ((*pty = getpt()) >= 0) { 2645 char *name = ptsname(*pty); 2646 if (name != 0) { /* if filesystem is trashed, this may be null */ 2647 strcpy(ttydev, name); 2648 result = 0; 2649 } 2650 } 2651#elif defined(__MVS__) 2652 result = pty_search(pty); 2653#else 2654 result = ((*pty = open("/dev/ptmx", O_RDWR)) < 0); 2655#endif 2656#if defined(SVR4) || defined(__SCO__) 2657 if (!result) 2658 strcpy(ttydev, ptsname(*pty)); 2659#endif 2660 2661#elif defined(AIXV3) 2662 2663 if ((*pty = open("/dev/ptc", O_RDWR)) >= 0) { 2664 strcpy(ttydev, ttyname(*pty)); 2665 result = 0; 2666 } 2667#elif defined(__convex__) 2668 2669 char *pty_name; 2670 extern char *getpty(void); 2671 2672 while ((pty_name = getpty()) != NULL) { 2673 if ((*pty = open(pty_name, O_RDWR)) >= 0) { 2674 strcpy(ptydev, pty_name); 2675 strcpy(ttydev, pty_name); 2676 *x_basename(ttydev) = 't'; 2677 result = 0; 2678 break; 2679 } 2680 } 2681 2682#elif defined(sequent) 2683 2684 result = ((*pty = getpseudotty(&ttydev, &ptydev)) < 0); 2685 2686#elif defined(__sgi) && (OSMAJORVERSION >= 4) 2687 2688 char *tty_name; 2689 2690 tty_name = _getpty(pty, O_RDWR, 0622, 0); 2691 if (tty_name != 0) { 2692 strcpy(ttydev, tty_name); 2693 result = 0; 2694 } 2695#elif (defined(__sgi) && (OSMAJORVERSION < 4)) || (defined(umips) && defined (SYSTYPE_SYSV)) 2696 2697 struct stat fstat_buf; 2698 2699 *pty = open("/dev/ptc", O_RDWR); 2700 if (*pty >= 0 && (fstat(*pty, &fstat_buf)) >= 0) { 2701 result = 0; 2702 sprintf(ttydev, "/dev/ttyq%d", minor(fstat_buf.st_rdev)); 2703 } 2704#elif defined(__hpux) 2705 2706 /* 2707 * Use the clone device if it works, otherwise use pty_search logic. 2708 */ 2709 if ((*pty = open("/dev/ptym/clone", O_RDWR)) >= 0) { 2710 char *name = ptsname(*pty); 2711 if (name != 0) { 2712 strcpy(ttydev, name); 2713 result = 0; 2714 } else { /* permissions, or other unexpected problem */ 2715 close(*pty); 2716 *pty = -1; 2717 result = pty_search(pty); 2718 } 2719 } else { 2720 result = pty_search(pty); 2721 } 2722 2723#else 2724 2725 result = pty_search(pty); 2726 2727#endif 2728#endif 2729 2730 TRACE(("get_pty(ttydev=%s, ptydev=%s) %s fd=%d\n", 2731 ttydev != 0 ? ttydev : "?", 2732 ptydev != 0 ? ptydev : "?", 2733 result ? "FAIL" : "OK", 2734 pty != 0 ? *pty : -1)); 2735 return result; 2736} 2737 2738static void 2739set_pty_permissions(uid_t uid, unsigned gid, unsigned mode) 2740{ 2741#ifdef USE_TTY_GROUP 2742 struct group *ttygrp; 2743 2744 if ((ttygrp = getgrnam(TTY_GROUP_NAME)) != 0) { 2745 gid = ttygrp->gr_gid; 2746 mode &= 0660U; 2747 } 2748 endgrent(); 2749#endif /* USE_TTY_GROUP */ 2750 2751 TRACE_IDS; 2752 set_owner(ttydev, uid, gid, mode); 2753} 2754 2755#ifdef get_pty /* USE_UTMP_SETGID */ 2756#undef get_pty 2757/* 2758 * Call the real get_pty() before relinquishing root-setuid, caching the 2759 * result. 2760 */ 2761static int 2762get_pty(int *pty, char *from) 2763{ 2764 static int m_pty = -1; 2765 int result = -1; 2766 2767 if (pty == NULL) { 2768 result = really_get_pty(&m_pty, from); 2769 2770 seteuid(0); 2771 set_pty_permissions(save_ruid, save_rgid, 0600U); 2772 seteuid(save_ruid); 2773 TRACE_IDS; 2774 2775#ifdef USE_OPENPTY 2776 if (opened_tty >= 0) { 2777 close(opened_tty); 2778 opened_tty = -1; 2779 } 2780#endif 2781 } else if (m_pty != -1) { 2782 *pty = m_pty; 2783 result = 0; 2784 } else { 2785 result = -1; 2786 } 2787 TRACE(("get_pty(ttydev=%s, ptydev=%s) %s fd=%d (utmp setgid)\n", 2788 ttydev != 0 ? ttydev : "?", 2789 ptydev != 0 ? ptydev : "?", 2790 result ? "FAIL" : "OK", 2791 pty != 0 ? *pty : -1)); 2792 return result; 2793} 2794#endif 2795 2796/* 2797 * Called from get_pty to iterate over likely pseudo terminals 2798 * we might allocate. Used on those systems that do not have 2799 * a functional interface for allocating a pty. 2800 * Returns 0 if found a pty, 1 if fails. 2801 */ 2802#ifdef USE_PTY_SEARCH 2803static int 2804pty_search(int *pty) 2805{ 2806 static int devindex = 0, letter = 0; 2807 2808#if defined(CRAY) || defined(__MVS__) 2809 while (devindex < MAXPTTYS) { 2810 sprintf(ttydev, TTYFORMAT, devindex); 2811 sprintf(ptydev, PTYFORMAT, devindex); 2812 devindex++; 2813 2814 TRACE(("pty_search(ttydev=%s, ptydev=%s)\n", ttydev, ptydev)); 2815 if ((*pty = open(ptydev, O_RDWR)) >= 0) { 2816 return 0; 2817 } 2818 } 2819#else /* CRAY || __MVS__ */ 2820 while (PTYCHAR1[letter]) { 2821 ttydev[strlen(ttydev) - 2] = 2822 ptydev[strlen(ptydev) - 2] = PTYCHAR1[letter]; 2823 2824 while (PTYCHAR2[devindex]) { 2825 ttydev[strlen(ttydev) - 1] = 2826 ptydev[strlen(ptydev) - 1] = PTYCHAR2[devindex]; 2827 devindex++; 2828 2829 TRACE(("pty_search(ttydev=%s, ptydev=%s)\n", ttydev, ptydev)); 2830 if ((*pty = open(ptydev, O_RDWR)) >= 0) { 2831#ifdef sun 2832 /* Need to check the process group of the pty. 2833 * If it exists, then the slave pty is in use, 2834 * and we need to get another one. 2835 */ 2836 int pgrp_rtn; 2837 if (ioctl(*pty, TIOCGPGRP, &pgrp_rtn) == 0 || errno != EIO) { 2838 close(*pty); 2839 continue; 2840 } 2841#endif /* sun */ 2842 return 0; 2843 } 2844 } 2845 devindex = 0; 2846 letter++; 2847 } 2848#endif /* CRAY else */ 2849 /* 2850 * We were unable to allocate a pty master! Return an error 2851 * condition and let our caller terminate cleanly. 2852 */ 2853 return 1; 2854} 2855#endif /* USE_PTY_SEARCH */ 2856 2857/* 2858 * The only difference in /etc/termcap between 4014 and 4015 is that 2859 * the latter has support for switching character sets. We support the 2860 * 4015 protocol, but ignore the character switches. Therefore, we 2861 * choose 4014 over 4015. 2862 * 2863 * Features of the 4014 over the 4012: larger (19") screen, 12-bit 2864 * graphics addressing (compatible with 4012 10-bit addressing), 2865 * special point plot mode, incremental plot mode (not implemented in 2866 * later Tektronix terminals), and 4 character sizes. 2867 * All of these are supported by xterm. 2868 */ 2869 2870#if OPT_TEK4014 2871static const char *tekterm[] = 2872{ 2873 "tek4014", 2874 "tek4015", /* 4014 with APL character set support */ 2875 "tek4012", /* 4010 with lower case */ 2876 "tek4013", /* 4012 with APL character set support */ 2877 "tek4010", /* small screen, upper-case only */ 2878 "dumb", 2879 0 2880}; 2881#endif 2882 2883/* The VT102 is a VT100 with the Advanced Video Option included standard. 2884 * It also adds Escape sequences for insert/delete character/line. 2885 * The VT220 adds 8-bit character sets, selective erase. 2886 * The VT320 adds a 25th status line, terminal state interrogation. 2887 * The VT420 has up to 48 lines on the screen. 2888 */ 2889 2890static const char *vtterm[] = 2891{ 2892#ifdef USE_X11TERM 2893 "x11term", /* for people who want special term name */ 2894#endif 2895 DFT_TERMTYPE, /* for people who want special term name */ 2896 "xterm", /* the prefered name, should be fastest */ 2897 "vt102", 2898 "vt100", 2899 "ansi", 2900 "dumb", 2901 0 2902}; 2903 2904/* ARGSUSED */ 2905static void 2906hungtty(int i GCC_UNUSED) 2907{ 2908 DEBUG_MSG("handle:hungtty\n"); 2909 siglongjmp(env, 1); 2910} 2911 2912#if OPT_PTY_HANDSHAKE 2913#define NO_FDS {-1, -1} 2914 2915static int cp_pipe[2] = NO_FDS; /* this pipe is used for child to parent transfer */ 2916static int pc_pipe[2] = NO_FDS; /* this pipe is used for parent to child transfer */ 2917 2918typedef enum { /* c == child, p == parent */ 2919 PTY_BAD, /* c->p: can't open pty slave for some reason */ 2920 PTY_FATALERROR, /* c->p: we had a fatal error with the pty */ 2921 PTY_GOOD, /* c->p: we have a good pty, let's go on */ 2922 PTY_NEW, /* p->c: here is a new pty slave, try this */ 2923 PTY_NOMORE, /* p->c; no more pty's, terminate */ 2924 UTMP_ADDED, /* c->p: utmp entry has been added */ 2925 UTMP_TTYSLOT, /* c->p: here is my ttyslot */ 2926 PTY_EXEC /* p->c: window has been mapped the first time */ 2927} status_t; 2928 2929typedef struct { 2930 status_t status; 2931 int error; 2932 int fatal_error; 2933 int tty_slot; 2934 int rows; 2935 int cols; 2936 char buffer[1024]; 2937} handshake_t; 2938 2939#if OPT_TRACE 2940static void 2941trace_handshake(const char *tag, handshake_t * data) 2942{ 2943 const char *status = "?"; 2944 switch (data->status) { 2945 case PTY_BAD: 2946 status = "PTY_BAD"; 2947 break; 2948 case PTY_FATALERROR: 2949 status = "PTY_FATALERROR"; 2950 break; 2951 case PTY_GOOD: 2952 status = "PTY_GOOD"; 2953 break; 2954 case PTY_NEW: 2955 status = "PTY_NEW"; 2956 break; 2957 case PTY_NOMORE: 2958 status = "PTY_NOMORE"; 2959 break; 2960 case UTMP_ADDED: 2961 status = "UTMP_ADDED"; 2962 break; 2963 case UTMP_TTYSLOT: 2964 status = "UTMP_TTYSLOT"; 2965 break; 2966 case PTY_EXEC: 2967 status = "PTY_EXEC"; 2968 break; 2969 } 2970 TRACE(("handshake %s %s errno=%d, error=%d device \"%s\"\n", 2971 tag, 2972 status, 2973 data->error, 2974 data->fatal_error, 2975 data->buffer)); 2976} 2977#define TRACE_HANDSHAKE(tag, data) trace_handshake(tag, data) 2978#else 2979#define TRACE_HANDSHAKE(tag, data) /* nothing */ 2980#endif 2981 2982/* HsSysError() 2983 * 2984 * This routine does the equivalent of a SysError but it handshakes 2985 * over the errno and error exit to the master process so that it can 2986 * display our error message and exit with our exit code so that the 2987 * user can see it. 2988 */ 2989 2990static void 2991HsSysError(int error) 2992{ 2993 handshake_t handshake; 2994 2995 memset(&handshake, 0, sizeof(handshake)); 2996 handshake.status = PTY_FATALERROR; 2997 handshake.error = errno; 2998 handshake.fatal_error = error; 2999 strncpy(handshake.buffer, ttydev, sizeof(handshake.buffer)); 3000 3001 if (resource.ptyHandshake && (cp_pipe[1] >= 0)) { 3002 TRACE(("HsSysError errno=%d, error=%d device \"%s\"\n", 3003 handshake.error, 3004 handshake.fatal_error, 3005 handshake.buffer)); 3006 TRACE_HANDSHAKE("writing", &handshake); 3007 IGNORE_RC(write(cp_pipe[1], 3008 (const char *) &handshake, 3009 sizeof(handshake))); 3010 } else { 3011 xtermWarning("fatal pty error errno=%d, error=%d device \"%s\"\n", 3012 handshake.error, 3013 handshake.fatal_error, 3014 handshake.buffer); 3015 fprintf(stderr, "%s\n", SysErrorMsg(handshake.error)); 3016 fprintf(stderr, "Reason: %s\n", SysReasonMsg(handshake.fatal_error)); 3017 } 3018 exit(error); 3019} 3020 3021void 3022first_map_occurred(void) 3023{ 3024 if (resource.wait_for_map) { 3025 handshake_t handshake; 3026 TScreen *screen = TScreenOf(term); 3027 3028 memset(&handshake, 0, sizeof(handshake)); 3029 handshake.status = PTY_EXEC; 3030 handshake.rows = screen->max_row; 3031 handshake.cols = screen->max_col; 3032 3033 if (pc_pipe[1] >= 0) { 3034 TRACE(("first_map_occurred: %dx%d\n", handshake.rows, handshake.cols)); 3035 TRACE_HANDSHAKE("writing", &handshake); 3036 IGNORE_RC(write(pc_pipe[1], 3037 (const char *) &handshake, 3038 sizeof(handshake))); 3039 close(cp_pipe[0]); 3040 close(pc_pipe[1]); 3041 } 3042 resource.wait_for_map = False; 3043 } 3044} 3045#else 3046/* 3047 * temporary hack to get xterm working on att ptys 3048 */ 3049static void 3050HsSysError(int error) 3051{ 3052 xtermWarning("fatal pty error %d (errno=%d) on tty %s\n", 3053 error, errno, ttydev); 3054 exit(error); 3055} 3056#endif /* OPT_PTY_HANDSHAKE else !OPT_PTY_HANDSHAKE */ 3057 3058#ifndef VMS 3059static void 3060set_owner(char *device, unsigned uid, unsigned gid, unsigned mode) 3061{ 3062 int why; 3063 3064 TRACE_IDS; 3065 TRACE(("set_owner(%s, uid=%d, gid=%d, mode=%#o\n", 3066 device, (int) uid, (int) gid, (unsigned) mode)); 3067 3068 if (chown(device, uid, gid) < 0) { 3069 why = errno; 3070 if (why != ENOENT 3071 && save_ruid == 0) { 3072 xtermPerror("Cannot chown %s to %ld,%ld", 3073 device, (long) uid, (long) gid); 3074 } 3075 TRACE(("...chown failed: %s\n", strerror(why))); 3076 } else if (chmod(device, mode) < 0) { 3077 why = errno; 3078 if (why != ENOENT) { 3079 struct stat sb; 3080 if (stat(device, &sb) < 0) { 3081 xtermPerror("Cannot chmod %s to %03o", 3082 device, (unsigned) mode); 3083 } else if (mode != (sb.st_mode & 0777U)) { 3084 xtermPerror("Cannot chmod %s to %03lo currently %03lo", 3085 device, 3086 (unsigned long) mode, 3087 (unsigned long) (sb.st_mode & 0777U)); 3088 TRACE(("...stat uid=%d, gid=%d, mode=%#o\n", 3089 (int) sb.st_uid, (int) sb.st_gid, (unsigned) sb.st_mode)); 3090 } 3091 } 3092 TRACE(("...chmod failed: %s\n", strerror(why))); 3093 } 3094} 3095 3096/* 3097 * utmp data may not be null-terminated; even if it is, there may be garbage 3098 * after the null. This fills the unused part of the result with nulls. 3099 */ 3100static void 3101copy_filled(char *target, const char *source, size_t len) 3102{ 3103 size_t used = 0; 3104 while (used < len) { 3105 if ((target[used] = source[used]) == 0) 3106 break; 3107 ++used; 3108 } 3109 while (used < len) { 3110 target[used++] = '\0'; 3111 } 3112} 3113 3114#if defined(HAVE_UTMP) && defined(USE_SYSV_UTMP) && !defined(USE_UTEMPTER) 3115/* 3116 * getutid() only looks at ut_type and ut_id. 3117 * But we'll also check ut_line in find_utmp(). 3118 */ 3119static void 3120init_utmp(int type, struct UTMP_STR *tofind) 3121{ 3122 memset(tofind, 0, sizeof(*tofind)); 3123 tofind->ut_type = type; 3124 copy_filled(tofind->ut_id, my_utmp_id(ttydev), sizeof(tofind->ut_id)); 3125 copy_filled(tofind->ut_line, my_pty_name(ttydev), sizeof(tofind->ut_line)); 3126} 3127 3128/* 3129 * We could use getutline() if we didn't support old systems. 3130 */ 3131static struct UTMP_STR * 3132find_utmp(struct UTMP_STR *tofind) 3133{ 3134 struct UTMP_STR *result; 3135 struct UTMP_STR limited; 3136 struct UTMP_STR working; 3137 3138 for (;;) { 3139 memset(&working, 0, sizeof(working)); 3140 working.ut_type = tofind->ut_type; 3141 copy_filled(working.ut_id, tofind->ut_id, sizeof(tofind->ut_id)); 3142#if defined(__digital__) && defined(__unix__) && (defined(OSMAJORVERSION) && OSMAJORVERSION < 5) 3143 working.ut_type = 0; 3144#endif 3145 if ((result = call_getutid(&working)) == 0) 3146 break; 3147 copy_filled(limited.ut_line, result->ut_line, sizeof(result->ut_line)); 3148 if (!memcmp(limited.ut_line, tofind->ut_line, sizeof(limited.ut_line))) 3149 break; 3150 /* 3151 * Solaris, IRIX64 and HPUX manpages say to fill the static area 3152 * pointed to by the return-value to zeros if searching for multiple 3153 * occurrences. Otherwise it will continue to return the same value. 3154 */ 3155 memset(result, 0, sizeof(*result)); 3156 } 3157 return result; 3158} 3159#endif /* HAVE_UTMP... */ 3160 3161#define close_fd(fd) close(fd), fd = -1 3162 3163#if defined(TIOCNOTTY) && (!defined(__GLIBC__) || (__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1))) 3164#define USE_NO_DEV_TTY 1 3165#else 3166#define USE_NO_DEV_TTY 0 3167#endif 3168 3169static int 3170same_leaf(char *a, char *b) 3171{ 3172 char *p = x_basename(a); 3173 char *q = x_basename(b); 3174 return !strcmp(p, q); 3175} 3176 3177/* 3178 * "good enough" (inode wouldn't port to Cygwin) 3179 */ 3180static int 3181same_file(const char *a, const char *b) 3182{ 3183 struct stat asb; 3184 struct stat bsb; 3185 int result = 0; 3186 3187 if ((stat(a, &asb) == 0) 3188 && (stat(b, &bsb) == 0) 3189 && ((asb.st_mode & S_IFMT) == S_IFREG) 3190 && ((bsb.st_mode & S_IFMT) == S_IFREG) 3191 && (asb.st_mtime == bsb.st_mtime) 3192 && (asb.st_size == bsb.st_size)) { 3193 result = 1; 3194 } 3195 return result; 3196} 3197 3198/* 3199 * Only set $SHELL for paths found in the standard location. 3200 */ 3201static Boolean 3202validShell(const char *pathname) 3203{ 3204 Boolean result = False; 3205 const char *ok_shells = "/etc/shells"; 3206 char *blob; 3207 struct stat sb; 3208 size_t rc; 3209 FILE *fp; 3210 3211 if (validProgram(pathname) 3212 && stat(ok_shells, &sb) == 0 3213 && (sb.st_mode & S_IFMT) == S_IFREG 3214 && (sb.st_size != 0) 3215 && (blob = calloc((size_t) sb.st_size + 2, sizeof(char))) != 0) { 3216 if ((fp = fopen(ok_shells, "r")) != 0) { 3217 rc = fread(blob, sizeof(char), (size_t) sb.st_size, fp); 3218 if (rc == (size_t) sb.st_size) { 3219 char *p = blob; 3220 char *q, *r; 3221 blob[rc] = '\0'; 3222 while (!result && (q = strtok(p, "\n")) != 0) { 3223 if ((r = x_strtrim(q)) != 0) { 3224 TRACE(("...test \"%s\"\n", q)); 3225 if (!strcmp(q, pathname)) { 3226 result = True; 3227 } else if (same_leaf(q, (char *) pathname) && 3228 same_file(q, pathname)) { 3229 result = True; 3230 } 3231 free(r); 3232 } 3233 p = 0; 3234 } 3235 } 3236 fclose(fp); 3237 } 3238 free(blob); 3239 } 3240 TRACE(("validShell %s ->%d\n", NonNull(pathname), result)); 3241 return result; 3242} 3243 3244static char * 3245resetShell(char *oldPath) 3246{ 3247 char *newPath = x_strdup("/bin/sh"); 3248 char *envPath = getenv("SHELL"); 3249 if (oldPath != 0) 3250 free(oldPath); 3251 if (!IsEmpty(envPath)) 3252 xtermSetenv("SHELL", newPath); 3253 return newPath; 3254} 3255 3256/* 3257 * Inits pty and tty and forks a login process. 3258 * Does not close fd Xsocket. 3259 * If slave, the pty named in passedPty is already open for use 3260 */ 3261static int 3262spawnXTerm(XtermWidget xw) 3263{ 3264 TScreen *screen = TScreenOf(xw); 3265 Cardinal nn; 3266#if OPT_PTY_HANDSHAKE 3267 Bool got_handshake_size = False; 3268 handshake_t handshake; 3269 int done; 3270#endif 3271#if OPT_INITIAL_ERASE 3272 int initial_erase = VAL_INITIAL_ERASE; 3273 Bool setInitialErase; 3274#endif 3275 int rc = 0; 3276 int ttyfd = -1; 3277 Bool ok_termcap; 3278 char *newtc; 3279 3280#ifdef TERMIO_STRUCT 3281 TERMIO_STRUCT tio; 3282#ifdef __MVS__ 3283 TERMIO_STRUCT gio; 3284#endif /* __MVS__ */ 3285#ifdef TIOCLSET 3286 unsigned lmode; 3287#endif /* TIOCLSET */ 3288#ifdef HAS_LTCHARS 3289 struct ltchars ltc; 3290#endif /* HAS_LTCHARS */ 3291#else /* !TERMIO_STRUCT */ 3292 int ldisc = 0; 3293 int discipline; 3294 unsigned lmode; 3295 struct tchars tc; 3296 struct ltchars ltc; 3297 struct sgttyb sg; 3298#ifdef sony 3299 int jmode; 3300 struct jtchars jtc; 3301#endif /* sony */ 3302#endif /* TERMIO_STRUCT */ 3303 3304 char *shell_path = 0; 3305 char *shname, *shname_minus; 3306 int i; 3307#if USE_NO_DEV_TTY 3308 int no_dev_tty = False; 3309#endif 3310 const char **envnew; /* new environment */ 3311 char buf[64]; 3312 char *TermName = NULL; 3313#ifdef TTYSIZE_STRUCT 3314 TTYSIZE_STRUCT ts; 3315#endif 3316 struct passwd pw; 3317 char *login_name = NULL; 3318#ifndef USE_UTEMPTER 3319#ifdef HAVE_UTMP 3320 struct UTMP_STR utmp; 3321#ifdef USE_SYSV_UTMP 3322 struct UTMP_STR *utret = NULL; 3323#endif 3324#ifdef USE_LASTLOG 3325 struct lastlog lastlog; 3326#endif 3327#ifdef USE_LASTLOGX 3328 struct lastlogx lastlogx; 3329#endif /* USE_LASTLOG */ 3330#endif /* HAVE_UTMP */ 3331#endif /* !USE_UTEMPTER */ 3332 3333#if OPT_TRACE 3334 unsigned long xterm_parent = (unsigned long) getpid(); 3335#endif 3336 3337 /* Noisy compilers (suppress some unused-variable warnings) */ 3338 (void) rc; 3339#if defined(HAVE_UTMP) && defined(USE_SYSV_UTMP) && !defined(USE_UTEMPTER) 3340 (void) utret; 3341#endif 3342 3343 screen->uid = save_ruid; 3344 screen->gid = save_rgid; 3345 3346#ifdef SIGTTOU 3347 /* so that TIOCSWINSZ || TIOCSIZE doesn't block */ 3348 signal(SIGTTOU, SIG_IGN); 3349#endif 3350 3351#if OPT_PTY_HANDSHAKE 3352 memset(&handshake, 0, sizeof(handshake)); 3353#endif 3354 3355 if (am_slave >= 0) { 3356 screen->respond = am_slave; 3357 set_pty_id(ttydev, passedPty); 3358#ifdef USE_PTY_DEVICE 3359 set_pty_id(ptydev, passedPty); 3360#endif 3361 if (xtermResetIds(screen) < 0) 3362 exit(1); 3363 } else { 3364 Bool tty_got_hung; 3365 3366 /* 3367 * Sometimes /dev/tty hangs on open (as in the case of a pty 3368 * that has gone away). Simply make up some reasonable 3369 * defaults. 3370 */ 3371 3372 signal(SIGALRM, hungtty); 3373 alarm(2); /* alarm(1) might return too soon */ 3374 if (!sigsetjmp(env, 1)) { 3375 ttyfd = open("/dev/tty", O_RDWR); 3376 alarm(0); 3377 tty_got_hung = False; 3378 } else { 3379 tty_got_hung = True; 3380 ttyfd = -1; 3381 errno = ENXIO; 3382 } 3383 memset(&pw, 0, sizeof(pw)); 3384#if OPT_PTY_HANDSHAKE 3385 got_handshake_size = False; 3386#endif /* OPT_PTY_HANDSHAKE */ 3387#if OPT_INITIAL_ERASE 3388 initial_erase = VAL_INITIAL_ERASE; 3389#endif 3390 signal(SIGALRM, SIG_DFL); 3391 3392 /* 3393 * Check results and ignore current control terminal if 3394 * necessary. ENXIO is what is normally returned if there is 3395 * no controlling terminal, but some systems (e.g. SunOS 4.0) 3396 * seem to return EIO. Solaris 2.3 is said to return EINVAL. 3397 * Cygwin returns ENOENT. FreeBSD can return ENOENT, especially 3398 * if xterm is run within a jail. 3399 */ 3400#if USE_NO_DEV_TTY 3401 no_dev_tty = False; 3402#endif 3403 if (ttyfd < 0) { 3404 if (tty_got_hung || errno == ENXIO || errno == EIO || 3405 errno == ENOENT || 3406#ifdef ENODEV 3407 errno == ENODEV || 3408#endif 3409 errno == EINVAL || errno == ENOTTY || errno == EACCES) { 3410#if USE_NO_DEV_TTY 3411 no_dev_tty = True; 3412#endif 3413#ifdef HAS_LTCHARS 3414 ltc = d_ltc; 3415#endif /* HAS_LTCHARS */ 3416#ifdef TIOCLSET 3417 lmode = d_lmode; 3418#endif /* TIOCLSET */ 3419#ifdef TERMIO_STRUCT 3420 tio = d_tio; 3421#else /* !TERMIO_STRUCT */ 3422 sg = d_sg; 3423 tc = d_tc; 3424 discipline = d_disipline; 3425#ifdef sony 3426 jmode = d_jmode; 3427 jtc = d_jtc; 3428#endif /* sony */ 3429#endif /* TERMIO_STRUCT */ 3430 } else { 3431 SysError(ERROR_OPDEVTTY); 3432 } 3433 } else { 3434 3435 /* Get a copy of the current terminal's state, 3436 * if we can. Some systems (e.g., SVR4 and MacII) 3437 * may not have a controlling terminal at this point 3438 * if started directly from xdm or xinit, 3439 * in which case we just use the defaults as above. 3440 */ 3441#ifdef HAS_LTCHARS 3442 if (ioctl(ttyfd, TIOCGLTC, <c) == -1) 3443 ltc = d_ltc; 3444#endif /* HAS_LTCHARS */ 3445#ifdef TIOCLSET 3446 if (ioctl(ttyfd, TIOCLGET, &lmode) == -1) 3447 lmode = d_lmode; 3448#endif /* TIOCLSET */ 3449#ifdef TERMIO_STRUCT 3450 rc = ttyGetAttr(ttyfd, &tio); 3451 if (rc == -1) 3452 tio = d_tio; 3453#else /* !TERMIO_STRUCT */ 3454 rc = ioctl(ttyfd, TIOCGETP, (char *) &sg); 3455 if (rc == -1) 3456 sg = d_sg; 3457 if (ioctl(ttyfd, TIOCGETC, (char *) &tc) == -1) 3458 tc = d_tc; 3459 if (ioctl(ttyfd, TIOCGETD, (char *) &discipline) == -1) 3460 discipline = d_disipline; 3461#ifdef sony 3462 if (ioctl(ttyfd, TIOCKGET, (char *) &jmode) == -1) 3463 jmode = d_jmode; 3464 if (ioctl(ttyfd, TIOCKGETC, (char *) &jtc) == -1) 3465 jtc = d_jtc; 3466#endif /* sony */ 3467#endif /* TERMIO_STRUCT */ 3468 3469 /* 3470 * If ptyInitialErase is set, we want to get the pty's 3471 * erase value. Just in case that will fail, first get 3472 * the value from /dev/tty, so we will have something 3473 * at least. 3474 */ 3475#if OPT_INITIAL_ERASE 3476 if (resource.ptyInitialErase) { 3477#ifdef TERMIO_STRUCT 3478 initial_erase = tio.c_cc[VERASE]; 3479#else /* !TERMIO_STRUCT */ 3480 initial_erase = sg.sg_erase; 3481#endif /* TERMIO_STRUCT */ 3482 TRACE(("%s initial_erase:%d (from /dev/tty)\n", 3483 rc == 0 ? "OK" : "FAIL", 3484 initial_erase)); 3485 } 3486#endif 3487#ifdef __MVS__ 3488 if (ttyGetAttr(ttyfd, &gio) == 0) { 3489 gio.c_cflag &= ~(HUPCL | PARENB); 3490 ttySetAttr(ttyfd, &gio); 3491 } 3492#endif /* __MVS__ */ 3493 3494 close_fd(ttyfd); 3495 } 3496 3497 if (get_pty(&screen->respond, XDisplayString(screen->display))) { 3498 SysError(ERROR_PTYS); 3499 } 3500 TRACE_TTYSIZE(screen->respond, "after get_pty"); 3501#if OPT_INITIAL_ERASE 3502 if (resource.ptyInitialErase) { 3503#ifdef TERMIO_STRUCT 3504 TERMIO_STRUCT my_tio; 3505 rc = ttyGetAttr(screen->respond, &my_tio); 3506 if (rc == 0) 3507 initial_erase = my_tio.c_cc[VERASE]; 3508#else /* !TERMIO_STRUCT */ 3509 struct sgttyb my_sg; 3510 rc = ioctl(screen->respond, TIOCGETP, (char *) &my_sg); 3511 if (rc == 0) 3512 initial_erase = my_sg.sg_erase; 3513#endif /* TERMIO_STRUCT */ 3514 TRACE(("%s initial_erase:%d (from pty)\n", 3515 (rc == 0) ? "OK" : "FAIL", 3516 initial_erase)); 3517 } 3518#endif /* OPT_INITIAL_ERASE */ 3519 } 3520 3521 /* avoid double MapWindow requests */ 3522 XtSetMappedWhenManaged(SHELL_OF(CURRENT_EMU()), False); 3523 3524 wm_delete_window = XInternAtom(XtDisplay(toplevel), "WM_DELETE_WINDOW", 3525 False); 3526 3527 if (!TEK4014_ACTIVE(xw)) 3528 VTInit(xw); /* realize now so know window size for tty driver */ 3529#if defined(TIOCCONS) || defined(SRIOCSREDIR) 3530 if (Console) { 3531 /* 3532 * Inform any running xconsole program 3533 * that we are going to steal the console. 3534 */ 3535 XmuGetHostname(mit_console_name + MIT_CONSOLE_LEN, 255); 3536 mit_console = XInternAtom(screen->display, mit_console_name, False); 3537 /* the user told us to be the console, so we can use CurrentTime */ 3538 XtOwnSelection(SHELL_OF(CURRENT_EMU()), 3539 mit_console, CurrentTime, 3540 ConvertConsoleSelection, NULL, NULL); 3541 } 3542#endif 3543#if OPT_TEK4014 3544 if (TEK4014_ACTIVE(xw)) { 3545 envnew = tekterm; 3546 } else 3547#endif 3548 { 3549 envnew = vtterm; 3550 } 3551 3552 /* 3553 * This used to exit if no termcap entry was found for the specified 3554 * terminal name. That's a little unfriendly, so instead we'll allow 3555 * the program to proceed (but not to set $TERMCAP) if the termcap 3556 * entry is not found. 3557 */ 3558 ok_termcap = True; 3559 if (!get_termcap(xw, TermName = resource.term_name)) { 3560 const char *last = NULL; 3561 char *next; 3562 3563 TermName = x_strdup(*envnew); 3564 ok_termcap = False; 3565 while (*envnew != NULL) { 3566 if (last == NULL || strcmp(last, *envnew)) { 3567 next = x_strdup(*envnew); 3568 if (get_termcap(xw, next)) { 3569 free(TermName); 3570 TermName = next; 3571 ok_termcap = True + 1; 3572 break; 3573 } else { 3574 free(next); 3575 } 3576 } 3577 last = *envnew; 3578 envnew++; 3579 } 3580 } 3581 if (ok_termcap) { 3582 resource.term_name = TermName; 3583 resize_termcap(xw); 3584 } 3585 3586 /* 3587 * Check if ptyInitialErase is not set. If so, we rely on the termcap 3588 * (or terminfo) to tell us what the erase mode should be set to. 3589 */ 3590#if OPT_INITIAL_ERASE 3591 TRACE(("resource ptyInitialErase is %sset\n", 3592 resource.ptyInitialErase ? "" : "not ")); 3593 setInitialErase = False; 3594 if (override_tty_modes && ttymodelist[XTTYMODE_erase].set) { 3595 initial_erase = ttymodelist[XTTYMODE_erase].value; 3596 setInitialErase = True; 3597 } else if (resource.ptyInitialErase) { 3598 /* EMPTY */ ; 3599 } else if (ok_termcap) { 3600 char *s = get_tcap_erase(xw); 3601 TRACE(("...extracting initial_erase value from termcap\n")); 3602 if (s != 0) { 3603 char *save = s; 3604 initial_erase = decode_keyvalue(&s, True); 3605 setInitialErase = True; 3606 free(save); 3607 } 3608 } 3609 TRACE(("...initial_erase:%d\n", initial_erase)); 3610 3611 TRACE(("resource backarrowKeyIsErase is %sset\n", 3612 resource.backarrow_is_erase ? "" : "not ")); 3613 if (resource.backarrow_is_erase) { /* see input.c */ 3614 if (initial_erase == ANSI_DEL) { 3615 UIntClr(xw->keyboard.flags, MODE_DECBKM); 3616 } else { 3617 xw->keyboard.flags |= MODE_DECBKM; 3618 xw->keyboard.reset_DECBKM = 1; 3619 } 3620 TRACE(("...sets DECBKM %s\n", 3621 (xw->keyboard.flags & MODE_DECBKM) ? "on" : "off")); 3622 } else { 3623 xw->keyboard.reset_DECBKM = 2; 3624 } 3625#endif /* OPT_INITIAL_ERASE */ 3626 3627#ifdef TTYSIZE_STRUCT 3628 /* tell tty how big window is */ 3629#if OPT_TEK4014 3630 if (TEK4014_ACTIVE(xw)) { 3631 TTYSIZE_ROWS(ts) = 38; 3632 TTYSIZE_COLS(ts) = 81; 3633#if defined(USE_STRUCT_WINSIZE) 3634 ts.ws_xpixel = TFullWidth(TekScreenOf(tekWidget)); 3635 ts.ws_ypixel = TFullHeight(TekScreenOf(tekWidget)); 3636#endif 3637 } else 3638#endif 3639 { 3640 TTYSIZE_ROWS(ts) = (ttySize_t) MaxRows(screen); 3641 TTYSIZE_COLS(ts) = (ttySize_t) MaxCols(screen); 3642#if defined(USE_STRUCT_WINSIZE) 3643 ts.ws_xpixel = (ttySize_t) FullWidth(screen); 3644 ts.ws_ypixel = (ttySize_t) FullHeight(screen); 3645#endif 3646 } 3647 TRACE_RC(i, SET_TTYSIZE(screen->respond, ts)); 3648 TRACE(("spawn SET_TTYSIZE %dx%d return %d\n", 3649 TTYSIZE_ROWS(ts), 3650 TTYSIZE_COLS(ts), i)); 3651#endif /* TTYSIZE_STRUCT */ 3652 3653#if !defined(USE_OPENPTY) 3654#if defined(USE_USG_PTYS) || defined(HAVE_POSIX_OPENPT) 3655 /* 3656 * utempter checks the ownership of the device; some implementations 3657 * set ownership in grantpt - do this first. 3658 */ 3659 grantpt(screen->respond); 3660#endif 3661#if !defined(USE_USG_PTYS) && defined(HAVE_POSIX_OPENPT) 3662 unlockpt(screen->respond); 3663 TRACE_TTYSIZE(screen->respond, "after unlockpt"); 3664#endif 3665#endif /* !USE_OPENPTY */ 3666 3667 added_utmp_entry = False; 3668#if defined(USE_UTEMPTER) 3669#undef UTMP 3670 if (!resource.utmpInhibit) { 3671 struct UTMP_STR dummy; 3672 3673 /* Note: utempter may trim it anyway */ 3674 SetUtmpHost(dummy.ut_host, screen); 3675 TRACE(("...calling addToUtmp(pty=%s, hostname=%s, master_fd=%d)\n", 3676 ttydev, dummy.ut_host, screen->respond)); 3677 addToUtmp(ttydev, dummy.ut_host, screen->respond); 3678 added_utmp_entry = True; 3679 } 3680#endif 3681 3682 if (am_slave < 0) { 3683#if OPT_PTY_HANDSHAKE 3684 if (resource.ptyHandshake && (pipe(pc_pipe) || pipe(cp_pipe))) 3685 SysError(ERROR_FORK); 3686#endif 3687 TRACE(("Forking...\n")); 3688 if ((screen->pid = fork()) == -1) 3689 SysError(ERROR_FORK); 3690 3691 if (screen->pid == 0) { 3692#ifdef USE_USG_PTYS 3693 int ptyfd = -1; 3694 char *pty_name; 3695#endif 3696 /* 3697 * now in child process 3698 */ 3699#if defined(_POSIX_SOURCE) || defined(SVR4) || defined(__convex__) || defined(__SCO__) || defined(__QNX__) 3700 int pgrp = setsid(); /* variable may not be used... */ 3701#else 3702 int pgrp = getpid(); 3703#endif 3704 TRACE_CHILD 3705 3706#ifdef USE_USG_PTYS 3707#ifdef HAVE_SETPGID 3708 setpgid(0, 0); 3709#else 3710 setpgrp(); 3711#endif 3712 unlockpt(screen->respond); 3713 TRACE_TTYSIZE(screen->respond, "after unlockpt"); 3714 if ((pty_name = ptsname(screen->respond)) == 0) { 3715 SysError(ERROR_PTSNAME); 3716 } else if ((ptyfd = open(pty_name, O_RDWR)) < 0) { 3717 SysError(ERROR_OPPTSNAME); 3718 } 3719#ifdef I_PUSH 3720 else if (ioctl(ptyfd, I_PUSH, "ptem") < 0) { 3721 SysError(ERROR_PTEM); 3722 } 3723#if !defined(SVR4) && !(defined(SYSV) && defined(i386)) 3724 else if (!x_getenv("CONSEM") 3725 && ioctl(ptyfd, I_PUSH, "consem") < 0) { 3726 SysError(ERROR_CONSEM); 3727 } 3728#endif /* !SVR4 */ 3729 else if (ioctl(ptyfd, I_PUSH, "ldterm") < 0) { 3730 SysError(ERROR_LDTERM); 3731 } 3732#ifdef SVR4 /* from Sony */ 3733 else if (ioctl(ptyfd, I_PUSH, "ttcompat") < 0) { 3734 SysError(ERROR_TTCOMPAT); 3735 } 3736#endif /* SVR4 */ 3737#endif /* I_PUSH */ 3738 ttyfd = ptyfd; 3739#ifndef __MVS__ 3740 close_fd(screen->respond); 3741#endif /* __MVS__ */ 3742 3743#ifdef TTYSIZE_STRUCT 3744 /* tell tty how big window is */ 3745#if OPT_TEK4014 3746 if (TEK4014_ACTIVE(xw)) { 3747 TTYSIZE_ROWS(ts) = 24; 3748 TTYSIZE_COLS(ts) = 80; 3749#ifdef USE_STRUCT_WINSIZE 3750 ts.ws_xpixel = TFullWidth(TekScreenOf(tekWidget)); 3751 ts.ws_ypixel = TFullHeight(TekScreenOf(tekWidget)); 3752#endif 3753 } else 3754#endif /* OPT_TEK4014 */ 3755 { 3756 TTYSIZE_ROWS(ts) = (ttySize_t) MaxRows(screen); 3757 TTYSIZE_COLS(ts) = (ttySize_t) MaxCols(screen); 3758#ifdef USE_STRUCT_WINSIZE 3759 ts.ws_xpixel = (ttySize_t) FullWidth(screen); 3760 ts.ws_ypixel = (ttySize_t) FullHeight(screen); 3761#endif 3762 } 3763#endif /* TTYSIZE_STRUCT */ 3764 3765#endif /* USE_USG_PTYS */ 3766 3767 (void) pgrp; /* not all branches use this variable */ 3768 3769#if OPT_PTY_HANDSHAKE /* warning, goes for a long ways */ 3770 if (resource.ptyHandshake) { 3771 char *ptr; 3772 3773 /* close parent's sides of the pipes */ 3774 close(cp_pipe[0]); 3775 close(pc_pipe[1]); 3776 3777 /* Make sure that our sides of the pipes are not in the 3778 * 0, 1, 2 range so that we don't fight with stdin, out 3779 * or err. 3780 */ 3781 if (cp_pipe[1] <= 2) { 3782 if ((i = fcntl(cp_pipe[1], F_DUPFD, 3)) >= 0) { 3783 IGNORE_RC(close(cp_pipe[1])); 3784 cp_pipe[1] = i; 3785 } 3786 } 3787 if (pc_pipe[0] <= 2) { 3788 if ((i = fcntl(pc_pipe[0], F_DUPFD, 3)) >= 0) { 3789 IGNORE_RC(close(pc_pipe[0])); 3790 pc_pipe[0] = i; 3791 } 3792 } 3793 3794 /* we don't need the socket, or the pty master anymore */ 3795 close(ConnectionNumber(screen->display)); 3796#ifndef __MVS__ 3797 if (screen->respond >= 0) 3798 close(screen->respond); 3799#endif /* __MVS__ */ 3800 3801 /* Now is the time to set up our process group and 3802 * open up the pty slave. 3803 */ 3804#ifdef USE_SYSV_PGRP 3805#if defined(CRAY) && (OSMAJORVERSION > 5) 3806 IGNORE_RC(setsid()); 3807#else 3808 IGNORE_RC(setpgrp()); 3809#endif 3810#endif /* USE_SYSV_PGRP */ 3811 3812#if defined(__QNX__) && !defined(__QNXNTO__) 3813 qsetlogin(getlogin(), ttydev); 3814#endif 3815 if (ttyfd >= 0) { 3816#ifdef __MVS__ 3817 if (ttyGetAttr(ttyfd, &gio) == 0) { 3818 gio.c_cflag &= ~(HUPCL | PARENB); 3819 ttySetAttr(ttyfd, &gio); 3820 } 3821#else /* !__MVS__ */ 3822 close_fd(ttyfd); 3823#endif /* __MVS__ */ 3824 } 3825 3826 for (;;) { 3827#if USE_NO_DEV_TTY 3828 if (!no_dev_tty 3829 && (ttyfd = open("/dev/tty", O_RDWR)) >= 0) { 3830 ioctl(ttyfd, TIOCNOTTY, (char *) NULL); 3831 close_fd(ttyfd); 3832 } 3833#endif /* USE_NO_DEV_TTY */ 3834#ifdef CSRG_BASED 3835 IGNORE_RC(revoke(ttydev)); 3836#endif 3837 if ((ttyfd = open(ttydev, O_RDWR)) >= 0) { 3838 TRACE_TTYSIZE(ttyfd, "after open"); 3839 TRACE_RC(i, SET_TTYSIZE(ttyfd, ts)); 3840 TRACE_TTYSIZE(ttyfd, "after fixup"); 3841#if defined(CRAY) && defined(TCSETCTTY) 3842 /* make /dev/tty work */ 3843 ioctl(ttyfd, TCSETCTTY, 0); 3844#endif 3845#if ((defined(__GLIBC__) && defined(__FreeBSD_kernel__)) || defined(__GNU__)) && defined(TIOCSCTTY) 3846 /* make /dev/tty work */ 3847 ioctl(ttyfd, TIOCSCTTY, 0); 3848#endif 3849#ifdef USE_SYSV_PGRP 3850 /* We need to make sure that we are actually 3851 * the process group leader for the pty. If 3852 * we are, then we should now be able to open 3853 * /dev/tty. 3854 */ 3855 if ((i = open("/dev/tty", O_RDWR)) >= 0) { 3856 /* success! */ 3857 close(i); 3858 break; 3859 } 3860#else /* USE_SYSV_PGRP */ 3861 break; 3862#endif /* USE_SYSV_PGRP */ 3863 } 3864 perror("open ttydev"); 3865#ifdef TIOCSCTTY 3866 ioctl(ttyfd, TIOCSCTTY, 0); 3867#endif 3868 /* let our master know that the open failed */ 3869 handshake.status = PTY_BAD; 3870 handshake.error = errno; 3871 strncpy(handshake.buffer, ttydev, sizeof(handshake.buffer)); 3872 TRACE_HANDSHAKE("writing", &handshake); 3873 IGNORE_RC(write(cp_pipe[1], 3874 (const char *) &handshake, 3875 sizeof(handshake))); 3876 3877 /* get reply from parent */ 3878 i = (int) read(pc_pipe[0], (char *) &handshake, 3879 sizeof(handshake)); 3880 if (i <= 0) { 3881 /* parent terminated */ 3882 exit(1); 3883 } 3884 3885 if (handshake.status == PTY_NOMORE) { 3886 /* No more ptys, let's shutdown. */ 3887 exit(1); 3888 } 3889 3890 /* We have a new pty to try */ 3891 if (ttyfd >= 0) 3892 close(ttyfd); 3893 free(ttydev); 3894 ttydev = x_strdup(handshake.buffer); 3895 } 3896 3897 /* use the same tty name that everyone else will use 3898 * (from ttyname) 3899 */ 3900 if ((ptr = ttyname(ttyfd)) != 0) { 3901 free(ttydev); 3902 ttydev = x_strdup(ptr); 3903 } 3904 } 3905#endif /* OPT_PTY_HANDSHAKE -- from near fork */ 3906 3907 set_pty_permissions(screen->uid, 3908 screen->gid, 3909 (resource.messages 3910 ? 0622U 3911 : 0600U)); 3912 3913 /* 3914 * set up the tty modes 3915 */ 3916 { 3917#ifdef TERMIO_STRUCT 3918#if defined(umips) || defined(CRAY) || defined(linux) 3919 /* If the control tty had its modes screwed around with, 3920 eg. by lineedit in the shell, or emacs, etc. then tio 3921 will have bad values. Let's just get termio from the 3922 new tty and tailor it. */ 3923 if (ttyGetAttr(ttyfd, &tio) == -1) 3924 SysError(ERROR_TIOCGETP); 3925 tio.c_lflag |= ECHOE; 3926#endif /* umips */ 3927 /* Now is also the time to change the modes of the 3928 * child pty. 3929 */ 3930 /* input: nl->nl, don't ignore cr, cr->nl */ 3931 UIntClr(tio.c_iflag, (INLCR | IGNCR)); 3932 tio.c_iflag |= ICRNL; 3933#if OPT_WIDE_CHARS && defined(IUTF8) 3934#if OPT_LUIT_PROG 3935 if (command_to_exec_with_luit == 0) 3936#endif 3937 if (screen->utf8_mode) 3938 tio.c_iflag |= IUTF8; 3939#endif 3940 /* ouput: cr->cr, nl is not return, no delays, ln->cr/nl */ 3941#ifndef USE_POSIX_TERMIOS 3942 UIntClr(tio.c_oflag, 3943 (OCRNL 3944 | ONLRET 3945 | NLDLY 3946 | CRDLY 3947 | TABDLY 3948 | BSDLY 3949 | VTDLY 3950 | FFDLY)); 3951#endif /* USE_POSIX_TERMIOS */ 3952#ifdef ONLCR 3953 tio.c_oflag |= ONLCR; 3954#endif /* ONLCR */ 3955#ifdef OPOST 3956 tio.c_oflag |= OPOST; 3957#endif /* OPOST */ 3958#ifndef USE_POSIX_TERMIOS 3959# if defined(Lynx) && !defined(CBAUD) 3960# define CBAUD V_CBAUD 3961# endif 3962 UIntClr(tio.c_cflag, CBAUD); 3963#ifdef BAUD_0 3964 /* baud rate is 0 (don't care) */ 3965#elif defined(HAVE_TERMIO_C_ISPEED) 3966 tio.c_ispeed = tio.c_ospeed = VAL_LINE_SPEED; 3967#else /* !BAUD_0 */ 3968 tio.c_cflag |= VAL_LINE_SPEED; 3969#endif /* !BAUD_0 */ 3970#else /* USE_POSIX_TERMIOS */ 3971 cfsetispeed(&tio, VAL_LINE_SPEED); 3972 cfsetospeed(&tio, VAL_LINE_SPEED); 3973#ifdef __MVS__ 3974 /* turn off bits that can't be set from the slave side */ 3975 tio.c_cflag &= ~(PACKET | PKT3270 | PTU3270 | PKTXTND); 3976#endif /* __MVS__ */ 3977 /* Clear CLOCAL so that SIGHUP is sent to us 3978 when the xterm ends */ 3979 tio.c_cflag &= ~CLOCAL; 3980#endif /* USE_POSIX_TERMIOS */ 3981 /* enable signals, canonical processing (erase, kill, etc), 3982 * echo 3983 */ 3984 tio.c_lflag |= ISIG | ICANON | ECHO | ECHOE | ECHOK; 3985#ifdef ECHOKE 3986 tio.c_lflag |= ECHOKE | IEXTEN; 3987#endif 3988#ifdef ECHOCTL 3989 tio.c_lflag |= ECHOCTL | IEXTEN; 3990#endif 3991 for (nn = 0; nn < XtNumber(known_ttyChars); ++nn) { 3992 if (validTtyChar(tio, nn)) { 3993 int sysMode = known_ttyChars[nn].sysMode; 3994#ifdef __MVS__ 3995 if (tio.c_cc[sysMode] != 0) { 3996 switch (sysMode) { 3997 case VEOL: 3998 case VEOF: 3999 continue; 4000 } 4001 } 4002#endif 4003 tio.c_cc[sysMode] = (cc_t) known_ttyChars[nn].myDefault; 4004 } 4005 } 4006 4007 if (override_tty_modes) { 4008 for (nn = 0; nn < XtNumber(known_ttyChars); ++nn) { 4009 if (validTtyChar(tio, nn)) { 4010 TMODE(known_ttyChars[nn].myMode, 4011 tio.c_cc[known_ttyChars[nn].sysMode]); 4012 } 4013 } 4014#ifdef HAS_LTCHARS 4015 /* both SYSV and BSD have ltchars */ 4016 TMODE(XTTYMODE_susp, ltc.t_suspc); 4017 TMODE(XTTYMODE_dsusp, ltc.t_dsuspc); 4018 TMODE(XTTYMODE_rprnt, ltc.t_rprntc); 4019 TMODE(XTTYMODE_flush, ltc.t_flushc); 4020 TMODE(XTTYMODE_weras, ltc.t_werasc); 4021 TMODE(XTTYMODE_lnext, ltc.t_lnextc); 4022#endif 4023 } 4024#ifdef HAS_LTCHARS 4025#ifdef __hpux 4026 /* ioctl chokes when the "reserved" process group controls 4027 * are not set to _POSIX_VDISABLE */ 4028 ltc.t_rprntc = ltc.t_rprntc = ltc.t_flushc = 4029 ltc.t_werasc = ltc.t_lnextc = _POSIX_VDISABLE; 4030#endif /* __hpux */ 4031 if (ioctl(ttyfd, TIOCSLTC, <c) == -1) 4032 HsSysError(ERROR_TIOCSETC); 4033#endif /* HAS_LTCHARS */ 4034#ifdef TIOCLSET 4035 if (ioctl(ttyfd, TIOCLSET, (char *) &lmode) == -1) 4036 HsSysError(ERROR_TIOCLSET); 4037#endif /* TIOCLSET */ 4038 if (ttySetAttr(ttyfd, &tio) == -1) 4039 HsSysError(ERROR_TIOCSETP); 4040 4041 /* ignore errors here - some platforms don't work */ 4042 UIntClr(tio.c_cflag, CSIZE); 4043 if (screen->input_eight_bits) 4044 tio.c_cflag |= CS8; 4045 else 4046 tio.c_cflag |= CS7; 4047 (void) ttySetAttr(ttyfd, &tio); 4048 4049#else /* !TERMIO_STRUCT */ 4050 sg.sg_flags &= ~(ALLDELAY | XTABS | CBREAK | RAW); 4051 sg.sg_flags |= ECHO | CRMOD; 4052 /* make sure speed is set on pty so that editors work right */ 4053 sg.sg_ispeed = VAL_LINE_SPEED; 4054 sg.sg_ospeed = VAL_LINE_SPEED; 4055 /* reset t_brkc to default value */ 4056 tc.t_brkc = -1; 4057#ifdef LPASS8 4058 if (screen->input_eight_bits) 4059 lmode |= LPASS8; 4060 else 4061 lmode &= ~(LPASS8); 4062#endif 4063#ifdef sony 4064 jmode &= ~KM_KANJI; 4065#endif /* sony */ 4066 4067 ltc = d_ltc; 4068 4069 if (override_tty_modes) { 4070 TMODE(XTTYMODE_intr, tc.t_intrc); 4071 TMODE(XTTYMODE_quit, tc.t_quitc); 4072 TMODE(XTTYMODE_erase, sg.sg_erase); 4073 TMODE(XTTYMODE_kill, sg.sg_kill); 4074 TMODE(XTTYMODE_eof, tc.t_eofc); 4075 TMODE(XTTYMODE_start, tc.t_startc); 4076 TMODE(XTTYMODE_stop, tc.t_stopc); 4077 TMODE(XTTYMODE_brk, tc.t_brkc); 4078 /* both SYSV and BSD have ltchars */ 4079 TMODE(XTTYMODE_susp, ltc.t_suspc); 4080 TMODE(XTTYMODE_dsusp, ltc.t_dsuspc); 4081 TMODE(XTTYMODE_rprnt, ltc.t_rprntc); 4082 TMODE(XTTYMODE_flush, ltc.t_flushc); 4083 TMODE(XTTYMODE_weras, ltc.t_werasc); 4084 TMODE(XTTYMODE_lnext, ltc.t_lnextc); 4085 } 4086 4087 if (ioctl(ttyfd, TIOCSETP, (char *) &sg) == -1) 4088 HsSysError(ERROR_TIOCSETP); 4089 if (ioctl(ttyfd, TIOCSETC, (char *) &tc) == -1) 4090 HsSysError(ERROR_TIOCSETC); 4091 if (ioctl(ttyfd, TIOCSETD, (char *) &discipline) == -1) 4092 HsSysError(ERROR_TIOCSETD); 4093 if (ioctl(ttyfd, TIOCSLTC, (char *) <c) == -1) 4094 HsSysError(ERROR_TIOCSLTC); 4095 if (ioctl(ttyfd, TIOCLSET, (char *) &lmode) == -1) 4096 HsSysError(ERROR_TIOCLSET); 4097#ifdef sony 4098 if (ioctl(ttyfd, TIOCKSET, (char *) &jmode) == -1) 4099 HsSysError(ERROR_TIOCKSET); 4100 if (ioctl(ttyfd, TIOCKSETC, (char *) &jtc) == -1) 4101 HsSysError(ERROR_TIOCKSETC); 4102#endif /* sony */ 4103#endif /* TERMIO_STRUCT */ 4104#if defined(TIOCCONS) || defined(SRIOCSREDIR) 4105 if (Console) { 4106#ifdef TIOCCONS 4107 int on = 1; 4108 if (ioctl(ttyfd, TIOCCONS, (char *) &on) == -1) 4109 xtermPerror("cannot open console"); 4110#endif 4111#ifdef SRIOCSREDIR 4112 int fd = open("/dev/console", O_RDWR); 4113 if (fd == -1 || ioctl(fd, SRIOCSREDIR, ttyfd) == -1) 4114 xtermPerror("cannot open console"); 4115 IGNORE_RC(close(fd)); 4116#endif 4117 } 4118#endif /* TIOCCONS */ 4119 } 4120 4121 signal(SIGCHLD, SIG_DFL); 4122#ifdef USE_SYSV_SIGHUP 4123 /* watch out for extra shells (I don't understand either) */ 4124 signal(SIGHUP, SIG_DFL); 4125#else 4126 signal(SIGHUP, SIG_IGN); 4127#endif 4128 /* restore various signals to their defaults */ 4129 signal(SIGINT, SIG_DFL); 4130 signal(SIGQUIT, SIG_DFL); 4131 signal(SIGTERM, SIG_DFL); 4132 4133 /* 4134 * If we're not asked to let the parent process set the terminal's 4135 * erase mode, or if we had the ttyModes erase resource, then set 4136 * the terminal's erase mode from our best guess. 4137 */ 4138#if OPT_INITIAL_ERASE 4139 TRACE(("check if we should set erase to %d:%s\n\tptyInitialErase:%d,\n\toveride_tty_modes:%d,\n\tXTTYMODE_erase:%d\n", 4140 initial_erase, 4141 setInitialErase ? "YES" : "NO", 4142 resource.ptyInitialErase, 4143 override_tty_modes, 4144 ttymodelist[XTTYMODE_erase].set)); 4145 if (setInitialErase) { 4146#if OPT_TRACE 4147 int old_erase; 4148#endif 4149#ifdef TERMIO_STRUCT 4150 if (ttyGetAttr(ttyfd, &tio) == -1) 4151 tio = d_tio; 4152#if OPT_TRACE 4153 old_erase = tio.c_cc[VERASE]; 4154#endif 4155 tio.c_cc[VERASE] = (cc_t) initial_erase; 4156 TRACE_RC(rc, ttySetAttr(ttyfd, &tio)); 4157#else /* !TERMIO_STRUCT */ 4158 if (ioctl(ttyfd, TIOCGETP, (char *) &sg) == -1) 4159 sg = d_sg; 4160#if OPT_TRACE 4161 old_erase = sg.sg_erase; 4162#endif 4163 sg.sg_erase = initial_erase; 4164 rc = ioctl(ttyfd, TIOCSETP, (char *) &sg); 4165#endif /* TERMIO_STRUCT */ 4166 TRACE(("%s setting erase to %d (was %d)\n", 4167 rc ? "FAIL" : "OK", initial_erase, old_erase)); 4168 } 4169#endif 4170 4171 xtermCopyEnv(environ); 4172 4173 /* 4174 * standards.freedesktop.org/startup-notification-spec/ 4175 * notes that this variable is used when a "reliable" mechanism is 4176 * not available; in practice it must be unset to avoid confusing 4177 * GTK applications. 4178 */ 4179 xtermUnsetenv("DESKTOP_STARTUP_ID"); 4180 /* 4181 * We set this temporarily to work around poor design of Xcursor. 4182 * Unset it here to avoid confusion. 4183 */ 4184 xtermUnsetenv("XCURSOR_PATH"); 4185 4186 xtermSetenv("TERM", resource.term_name); 4187 if (!resource.term_name) 4188 *get_tcap_buffer(xw) = 0; 4189 4190 sprintf(buf, "%lu", 4191 ((unsigned long) XtWindow(SHELL_OF(CURRENT_EMU())))); 4192 xtermSetenv("WINDOWID", buf); 4193 4194 /* put the display into the environment of the shell */ 4195 xtermSetenv("DISPLAY", XDisplayString(screen->display)); 4196 4197 xtermSetenv("XTERM_VERSION", xtermVersion()); 4198 xtermSetenv("XTERM_LOCALE", xtermEnvLocale()); 4199 4200 /* 4201 * For debugging only, add environment variables that can be used 4202 * in scripts to selectively kill xterm's parent or child 4203 * processes. 4204 */ 4205#if OPT_TRACE 4206 sprintf(buf, "%lu", (unsigned long) xterm_parent); 4207 xtermSetenv("XTERM_PARENT", buf); 4208 sprintf(buf, "%lu", (unsigned long) getpid()); 4209 xtermSetenv("XTERM_CHILD", buf); 4210#endif 4211 4212 signal(SIGTERM, SIG_DFL); 4213 4214 /* this is the time to go and set up stdin, out, and err 4215 */ 4216 { 4217#if defined(CRAY) && (OSMAJORVERSION >= 6) 4218 close_fd(ttyfd); 4219 4220 IGNORE_RC(close(0)); 4221 4222 if (open("/dev/tty", O_RDWR)) { 4223 SysError(ERROR_OPDEVTTY); 4224 } 4225 IGNORE_RC(close(1)); 4226 IGNORE_RC(close(2)); 4227 dup(0); 4228 dup(0); 4229#else 4230 /* dup the tty */ 4231 for (i = 0; i <= 2; i++) 4232 if (i != ttyfd) { 4233 IGNORE_RC(close(i)); 4234 IGNORE_RC(dup(ttyfd)); 4235 } 4236#ifndef ATT 4237 /* and close the tty */ 4238 if (ttyfd > 2) 4239 close_fd(ttyfd); 4240#endif 4241#endif /* CRAY */ 4242 } 4243 4244#if !defined(USE_SYSV_PGRP) 4245#ifdef TIOCSCTTY 4246 setsid(); 4247 ioctl(0, TIOCSCTTY, 0); 4248#endif 4249 ioctl(0, TIOCSPGRP, (char *) &pgrp); 4250 setpgrp(0, 0); 4251 close(open(ttydev, O_WRONLY)); 4252 setpgrp(0, pgrp); 4253#if defined(__QNX__) 4254 tcsetpgrp(0, pgrp /*setsid() */ ); 4255#endif 4256#endif /* !USE_SYSV_PGRP */ 4257 4258#ifdef Lynx 4259 { 4260 TERMIO_STRUCT t; 4261 if (ttyGetAttr(0, &t) >= 0) { 4262 /* this gets lost somewhere on our way... */ 4263 t.c_oflag |= OPOST; 4264 ttySetAttr(0, &t); 4265 } 4266 } 4267#endif 4268 4269#ifdef HAVE_UTMP 4270 login_name = NULL; 4271 if (x_getpwuid(screen->uid, &pw)) { 4272 login_name = x_getlogin(screen->uid, &pw); 4273 } 4274 if (login_name != NULL) { 4275 xtermSetenv("LOGNAME", login_name); /* for POSIX */ 4276 } 4277#ifndef USE_UTEMPTER 4278#ifdef USE_UTMP_SETGID 4279 setEffectiveGroup(save_egid); 4280 TRACE_IDS; 4281#endif 4282#ifdef USE_SYSV_UTMP 4283 /* Set up our utmp entry now. We need to do it here 4284 * for the following reasons: 4285 * - It needs to have our correct process id (for 4286 * login). 4287 * - If our parent was to set it after the fork(), 4288 * it might make it out before we need it. 4289 * - We need to do it before we go and change our 4290 * user and group id's. 4291 */ 4292 (void) call_setutent(); 4293 init_utmp(DEAD_PROCESS, &utmp); 4294 4295 /* position to entry in utmp file */ 4296 /* Test return value: beware of entries left behind: PSz 9 Mar 00 */ 4297 utret = find_utmp(&utmp); 4298 if (utret == 0) { 4299 (void) call_setutent(); 4300 init_utmp(USER_PROCESS, &utmp); 4301 utret = find_utmp(&utmp); 4302 if (utret == 0) { 4303 (void) call_setutent(); 4304 } 4305 } 4306#if OPT_TRACE 4307 if (!utret) 4308 TRACE(("getutid: NULL\n")); 4309 else 4310 TRACE(("getutid: pid=%d type=%d user=%s line=%.*s id=%.*s\n", 4311 (int) utret->ut_pid, utret->ut_type, utret->ut_user, 4312 (int) sizeof(utret->ut_line), utret->ut_line, 4313 (int) sizeof(utret->ut_id), utret->ut_id)); 4314#endif 4315 4316 /* set up the new entry */ 4317 utmp.ut_type = USER_PROCESS; 4318#ifdef HAVE_UTMP_UT_XSTATUS 4319 utmp.ut_xstatus = 2; 4320#endif 4321 copy_filled(utmp.ut_user, 4322 (login_name != NULL) ? login_name : "????", 4323 sizeof(utmp.ut_user)); 4324 /* why are we copying this string again? (see above) */ 4325 copy_filled(utmp.ut_id, my_utmp_id(ttydev), sizeof(utmp.ut_id)); 4326 copy_filled(utmp.ut_line, 4327 my_pty_name(ttydev), sizeof(utmp.ut_line)); 4328 4329#ifdef HAVE_UTMP_UT_HOST 4330 SetUtmpHost(utmp.ut_host, screen); 4331#endif 4332#ifdef HAVE_UTMP_UT_SYSLEN 4333 SetUtmpSysLen(utmp); 4334#endif 4335 4336 copy_filled(utmp.ut_name, 4337 (login_name) ? login_name : "????", 4338 sizeof(utmp.ut_name)); 4339 4340 utmp.ut_pid = getpid(); 4341#if defined(HAVE_UTMP_UT_XTIME) 4342#if defined(HAVE_UTMP_UT_SESSION) 4343 utmp.ut_session = getsid(0); 4344#endif 4345 utmp.ut_xtime = time((time_t *) 0); 4346 utmp.ut_tv.tv_usec = 0; 4347#else 4348 utmp.ut_time = time((time_t *) 0); 4349#endif 4350 4351 /* write out the entry */ 4352 if (!resource.utmpInhibit) { 4353 errno = 0; 4354 call_pututline(&utmp); 4355 TRACE(("pututline: id %.*s, line %.*s, pid %ld, errno %d %s\n", 4356 (int) sizeof(utmp.ut_id), utmp.ut_id, 4357 (int) sizeof(utmp.ut_line), utmp.ut_line, 4358 (long) utmp.ut_pid, 4359 errno, (errno != 0) ? strerror(errno) : "")); 4360 } 4361#ifdef WTMP 4362#if defined(WTMPX_FILE) && (defined(SVR4) || defined(__SCO__)) 4363 if (xw->misc.login_shell) 4364 updwtmpx(WTMPX_FILE, &utmp); 4365#elif defined(linux) && defined(__GLIBC__) && (__GLIBC__ >= 2) && !(defined(__powerpc__) && (__GLIBC__ == 2) && (__GLIBC_MINOR__ == 0)) 4366 if (xw->misc.login_shell) 4367 call_updwtmp(etc_wtmp, &utmp); 4368#else 4369 if (xw->misc.login_shell && 4370 (i = open(etc_wtmp, O_WRONLY | O_APPEND)) >= 0) { 4371 IGNORE_RC(write(i, (char *) &utmp, sizeof(utmp))); 4372 close(i); 4373 } 4374#endif 4375#endif 4376 /* close the file */ 4377 (void) call_endutent(); 4378 4379#else /* USE_SYSV_UTMP */ 4380 /* We can now get our ttyslot! We can also set the initial 4381 * utmp entry. 4382 */ 4383 tslot = ttyslot(); 4384 added_utmp_entry = False; 4385 { 4386 if (tslot > 0 && OkPasswd(&pw) && !resource.utmpInhibit && 4387 (i = open(etc_utmp, O_WRONLY)) >= 0) { 4388 memset(&utmp, 0, sizeof(utmp)); 4389 copy_filled(utmp.ut_line, 4390 my_pty_name(ttydev), 4391 sizeof(utmp.ut_line)); 4392 copy_filled(utmp.ut_name, login_name, 4393 sizeof(utmp.ut_name)); 4394#ifdef HAVE_UTMP_UT_HOST 4395 SetUtmpHost(utmp.ut_host, screen); 4396#endif 4397#ifdef HAVE_UTMP_UT_SYSLEN 4398 SetUtmpSysLen(utmp); 4399#endif 4400 4401 utmp.ut_time = time((time_t *) 0); 4402 lseek(i, (long) (tslot * sizeof(utmp)), 0); 4403 IGNORE_RC(write(i, (char *) &utmp, sizeof(utmp))); 4404 close(i); 4405 added_utmp_entry = True; 4406#if defined(WTMP) 4407 if (xw->misc.login_shell && 4408 (i = open(etc_wtmp, O_WRONLY | O_APPEND)) >= 0) { 4409 int status; 4410 status = write(i, (char *) &utmp, sizeof(utmp)); 4411 status = close(i); 4412 } 4413#elif defined(MNX_LASTLOG) 4414 if (xw->misc.login_shell && 4415 (i = open(_U_LASTLOG, O_WRONLY)) >= 0) { 4416 lseek(i, (long) (screen->uid * 4417 sizeof(utmp)), 0); 4418 IGNORE_RC(write(i, (char *) &utmp, sizeof(utmp))); 4419 close(i); 4420 } 4421#endif /* WTMP or MNX_LASTLOG */ 4422 } else 4423 tslot = -tslot; 4424 } 4425 4426 /* Let's pass our ttyslot to our parent so that it can 4427 * clean up after us. 4428 */ 4429#if OPT_PTY_HANDSHAKE 4430 if (resource.ptyHandshake) { 4431 handshake.tty_slot = tslot; 4432 } 4433#endif /* OPT_PTY_HANDSHAKE */ 4434#endif /* USE_SYSV_UTMP */ 4435 4436#ifdef USE_LASTLOGX 4437 if (xw->misc.login_shell) { 4438 memset(&lastlogx, 0, sizeof(lastlogx)); 4439 (void) strncpy(lastlogx.ll_line, 4440 my_pty_name(ttydev), 4441 sizeof(lastlogx.ll_line)); 4442 X_GETTIMEOFDAY(&lastlogx.ll_tv); 4443 SetUtmpHost(lastlogx.ll_host, screen); 4444 updlastlogx(_PATH_LASTLOGX, screen->uid, &lastlogx); 4445 } 4446#endif 4447 4448#ifdef USE_LASTLOG 4449 if (xw->misc.login_shell && 4450 (i = open(etc_lastlog, O_WRONLY)) >= 0) { 4451 size_t size = sizeof(struct lastlog); 4452 off_t offset = (off_t) (screen->uid * size); 4453 4454 memset(&lastlog, 0, size); 4455 (void) strncpy(lastlog.ll_line, 4456 my_pty_name(ttydev), 4457 sizeof(lastlog.ll_line)); 4458 SetUtmpHost(lastlog.ll_host, screen); 4459 lastlog.ll_time = time((time_t *) 0); 4460 if (lseek(i, offset, 0) != (off_t) (-1)) { 4461 IGNORE_RC(write(i, (char *) &lastlog, size)); 4462 } 4463 close(i); 4464 } 4465#endif /* USE_LASTLOG */ 4466 4467#if defined(USE_UTMP_SETGID) 4468 disableSetGid(); 4469 TRACE_IDS; 4470#endif 4471 4472#if OPT_PTY_HANDSHAKE 4473 /* Let our parent know that we set up our utmp entry 4474 * so that it can clean up after us. 4475 */ 4476 if (resource.ptyHandshake) { 4477 handshake.status = UTMP_ADDED; 4478 handshake.error = 0; 4479 strncpy(handshake.buffer, ttydev, sizeof(handshake.buffer)); 4480 TRACE_HANDSHAKE("writing", &handshake); 4481 IGNORE_RC(write(cp_pipe[1], (char *) &handshake, sizeof(handshake))); 4482 } 4483#endif /* OPT_PTY_HANDSHAKE */ 4484#endif /* USE_UTEMPTER */ 4485#endif /* HAVE_UTMP */ 4486 4487 IGNORE_RC(setgid(screen->gid)); 4488 TRACE_IDS; 4489#ifdef HAVE_INITGROUPS 4490 if (geteuid() == 0 && OkPasswd(&pw)) { 4491 if (initgroups(login_name, pw.pw_gid)) { 4492 perror("initgroups failed"); 4493 SysError(ERROR_INIGROUPS); 4494 } 4495 } 4496#endif 4497 if (setuid(screen->uid)) { 4498 SysError(ERROR_SETUID); 4499 } 4500 TRACE_IDS; 4501#if OPT_PTY_HANDSHAKE 4502 if (resource.ptyHandshake) { 4503 /* mark the pipes as close on exec */ 4504 (void) fcntl(cp_pipe[1], F_SETFD, 1); 4505 (void) fcntl(pc_pipe[0], F_SETFD, 1); 4506 4507 /* We are at the point where we are going to 4508 * exec our shell (or whatever). Let our parent 4509 * know we arrived safely. 4510 */ 4511 handshake.status = PTY_GOOD; 4512 handshake.error = 0; 4513 (void) strncpy(handshake.buffer, ttydev, sizeof(handshake.buffer)); 4514 TRACE_HANDSHAKE("writing", &handshake); 4515 IGNORE_RC(write(cp_pipe[1], 4516 (const char *) &handshake, 4517 sizeof(handshake))); 4518 4519 if (resource.wait_for_map) { 4520 i = (int) read(pc_pipe[0], (char *) &handshake, 4521 sizeof(handshake)); 4522 if (i != sizeof(handshake) || 4523 handshake.status != PTY_EXEC) { 4524 /* some very bad problem occurred */ 4525 exit(ERROR_PTY_EXEC); 4526 } 4527 if (handshake.rows > 0 && handshake.cols > 0) { 4528 TRACE(("handshake ttysize: %dx%d\n", 4529 handshake.rows, handshake.cols)); 4530 set_max_row(screen, handshake.rows); 4531 set_max_col(screen, handshake.cols); 4532#ifdef TTYSIZE_STRUCT 4533 got_handshake_size = True; 4534 TTYSIZE_ROWS(ts) = (ttySize_t) MaxRows(screen); 4535 TTYSIZE_COLS(ts) = (ttySize_t) MaxCols(screen); 4536#if defined(USE_STRUCT_WINSIZE) 4537 ts.ws_xpixel = (ttySize_t) FullWidth(screen); 4538 ts.ws_ypixel = (ttySize_t) FullHeight(screen); 4539#endif 4540#endif /* TTYSIZE_STRUCT */ 4541 } 4542 } 4543 } 4544#endif /* OPT_PTY_HANDSHAKE */ 4545 4546#ifdef USE_SYSV_ENVVARS 4547 { 4548 char numbuf[12]; 4549 sprintf(numbuf, "%d", MaxCols(screen)); 4550 xtermSetenv("COLUMNS", numbuf); 4551 sprintf(numbuf, "%d", MaxRows(screen)); 4552 xtermSetenv("LINES", numbuf); 4553 } 4554#ifdef HAVE_UTMP 4555 if (OkPasswd(&pw)) { /* SVR4 doesn't provide these */ 4556 if (!x_getenv("HOME")) 4557 xtermSetenv("HOME", pw.pw_dir); 4558 if (!x_getenv("SHELL")) 4559 xtermSetenv("SHELL", pw.pw_shell); 4560 } 4561#endif /* HAVE_UTMP */ 4562#ifdef OWN_TERMINFO_DIR 4563 xtermSetenv("TERMINFO", OWN_TERMINFO_DIR); 4564#endif 4565#else /* USE_SYSV_ENVVARS */ 4566 if (*(newtc = get_tcap_buffer(xw)) != '\0') { 4567 resize_termcap(xw); 4568 if (xw->misc.titeInhibit && !xw->misc.tiXtraScroll) { 4569 remove_termcap_entry(newtc, "ti="); 4570 remove_termcap_entry(newtc, "te="); 4571 } 4572 /* 4573 * work around broken termcap entries */ 4574 if (resource.useInsertMode) { 4575 remove_termcap_entry(newtc, "ic="); 4576 /* don't get duplicates */ 4577 remove_termcap_entry(newtc, "im="); 4578 remove_termcap_entry(newtc, "ei="); 4579 remove_termcap_entry(newtc, "mi"); 4580 if (*newtc) 4581 strcat(newtc, ":im=\\E[4h:ei=\\E[4l:mi:"); 4582 } 4583 if (*newtc) { 4584#if OPT_INITIAL_ERASE 4585 unsigned len; 4586 remove_termcap_entry(newtc, TERMCAP_ERASE "="); 4587 len = (unsigned) strlen(newtc); 4588 if (len != 0 && newtc[len - 1] == ':') 4589 len--; 4590 sprintf(newtc + len, ":%s=\\%03o:", 4591 TERMCAP_ERASE, 4592 CharOf(initial_erase)); 4593#endif 4594 xtermSetenv("TERMCAP", newtc); 4595 } 4596 } 4597#endif /* USE_SYSV_ENVVARS */ 4598 4599#if OPT_PTY_HANDSHAKE 4600 /* 4601 * Need to reset after all the ioctl bashing we did above. 4602 * 4603 * If we expect the waitForMap logic to set the handshake-size, 4604 * use that to prevent races. 4605 */ 4606 if (resource.ptyHandshake 4607 && resource.ptySttySize 4608 && (got_handshake_size || !resource.wait_for_map0)) { 4609#ifdef TTYSIZE_STRUCT 4610 TRACE_RC(i, SET_TTYSIZE(0, ts)); 4611 TRACE(("ptyHandshake SET_TTYSIZE %dx%d return %d\n", 4612 TTYSIZE_ROWS(ts), 4613 TTYSIZE_COLS(ts), i)); 4614#endif /* TTYSIZE_STRUCT */ 4615 } 4616#endif /* OPT_PTY_HANDSHAKE */ 4617 signal(SIGHUP, SIG_DFL); 4618 4619 /* 4620 * If we have an explicit shell to run, make that set $SHELL. 4621 * Next, allow an existing setting of $SHELL, for absolute paths. 4622 * Otherwise, if $SHELL is not set, determine it from the user's 4623 * password information, if possible. 4624 * 4625 * Incidentally, our setting of $SHELL tells luit to use that 4626 * program rather than choosing between $SHELL and "/bin/sh". 4627 */ 4628 if (validShell(explicit_shname)) { 4629 xtermSetenv("SHELL", explicit_shname); 4630 } else if (validProgram(shell_path = x_getenv("SHELL"))) { 4631 if (!validShell(shell_path)) { 4632 xtermUnsetenv("SHELL"); 4633 } 4634 } else if ((!OkPasswd(&pw) && !x_getpwuid(screen->uid, &pw)) 4635 || *(shell_path = x_strdup(pw.pw_shell)) == 0) { 4636 shell_path = resetShell(shell_path); 4637 } else if (validShell(shell_path)) { 4638 xtermSetenv("SHELL", shell_path); 4639 } else { 4640 shell_path = resetShell(shell_path); 4641 } 4642 4643 /* 4644 * Set $XTERM_SHELL, which is not necessarily a valid shell, but 4645 * is executable. 4646 */ 4647 if (validProgram(explicit_shname)) { 4648 shell_path = explicit_shname; 4649 } else if (shell_path == 0) { 4650 /* this could happen if the explicit shname lost a race */ 4651 shell_path = resetShell(shell_path); 4652 } 4653 xtermSetenv("XTERM_SHELL", shell_path); 4654 4655 shname = x_basename(shell_path); 4656 TRACE(("shell path '%s' leaf '%s'\n", shell_path, shname)); 4657 4658#if OPT_LUIT_PROG 4659 /* 4660 * Use two copies of command_to_exec, in case luit is not actually 4661 * there, or refuses to run. In that case we will fall-through to 4662 * to command that the user gave anyway. 4663 */ 4664 if (command_to_exec_with_luit && command_to_exec) { 4665 char *myShell = xtermFindShell(*command_to_exec_with_luit, False); 4666 xtermSetenv("XTERM_SHELL", myShell); 4667 free(myShell); 4668 TRACE_ARGV("spawning luit command", command_to_exec_with_luit); 4669 execvp(*command_to_exec_with_luit, command_to_exec_with_luit); 4670 xtermPerror("Can't execvp %s", *command_to_exec_with_luit); 4671 xtermWarning("cannot support your locale.\n"); 4672 } 4673#endif 4674 if (command_to_exec) { 4675 char *myShell = xtermFindShell(*command_to_exec, False); 4676 xtermSetenv("XTERM_SHELL", myShell); 4677 free(myShell); 4678 TRACE_ARGV("spawning command", command_to_exec); 4679 execvp(*command_to_exec, command_to_exec); 4680 if (command_to_exec[1] == 0) 4681 execlp(shell_path, shname, "-c", command_to_exec[0], 4682 (void *) 0); 4683 xtermPerror("Can't execvp %s", *command_to_exec); 4684 } 4685#ifdef USE_SYSV_SIGHUP 4686 /* fix pts sh hanging around */ 4687 signal(SIGHUP, SIG_DFL); 4688#endif 4689 4690 shname_minus = CastMallocN(char, strlen(shname) + 2); 4691 (void) strcpy(shname_minus, "-"); 4692 (void) strcat(shname_minus, shname); 4693#ifndef TERMIO_STRUCT 4694 ldisc = (!XStrCmp("csh", shname + strlen(shname) - 3) 4695 ? NTTYDISC 4696 : 0); 4697 ioctl(0, TIOCSETD, (char *) &ldisc); 4698#endif /* !TERMIO_STRUCT */ 4699 4700#ifdef USE_LOGIN_DASH_P 4701 if (xw->misc.login_shell && OkPasswd(&pw) && added_utmp_entry) 4702 execl(bin_login, "login", "-p", "-f", login_name, (void *) 0); 4703#endif 4704 4705#if OPT_LUIT_PROG 4706 if (command_to_exec_with_luit) { 4707 if (xw->misc.login_shell) { 4708 char *params[4]; 4709 params[0] = x_strdup("-argv0"); 4710 params[1] = shname_minus; 4711 params[2] = NULL; 4712 x_appendargv(command_to_exec_with_luit 4713 + command_length_with_luit, 4714 params); 4715 } 4716 TRACE_ARGV("final luit command", command_to_exec_with_luit); 4717 execvp(*command_to_exec_with_luit, command_to_exec_with_luit); 4718 /* Exec failed. */ 4719 xtermPerror("Can't execvp %s", *command_to_exec_with_luit); 4720 } 4721#endif 4722 execlp(shell_path, 4723 (xw->misc.login_shell ? shname_minus : shname), 4724 (void *) 0); 4725 4726 /* Exec failed. */ 4727 xtermPerror("Could not exec %s", shell_path); 4728 IGNORE_RC(sleep(5)); 4729 free(shell_path); 4730 exit(ERROR_EXEC); 4731 } 4732 /* end if in child after fork */ 4733#if OPT_PTY_HANDSHAKE 4734 if (resource.ptyHandshake) { 4735 /* Parent process. Let's handle handshaked requests to our 4736 * child process. 4737 */ 4738 4739 /* close childs's sides of the pipes */ 4740 close(cp_pipe[1]); 4741 close(pc_pipe[0]); 4742 4743 for (done = 0; !done;) { 4744 if (read(cp_pipe[0], 4745 (char *) &handshake, 4746 sizeof(handshake)) <= 0) { 4747 /* Our child is done talking to us. If it terminated 4748 * due to an error, we will catch the death of child 4749 * and clean up. 4750 */ 4751 break; 4752 } 4753 4754 TRACE_HANDSHAKE("read", &handshake); 4755 switch (handshake.status) { 4756 case PTY_GOOD: 4757 /* Success! Let's free up resources and 4758 * continue. 4759 */ 4760 done = 1; 4761 break; 4762 4763 case PTY_BAD: 4764 /* The open of the pty failed! Let's get 4765 * another one. 4766 */ 4767 IGNORE_RC(close(screen->respond)); 4768 if (get_pty(&screen->respond, XDisplayString(screen->display))) { 4769 /* no more ptys! */ 4770 xtermPerror("child process can find no available ptys"); 4771 handshake.status = PTY_NOMORE; 4772 TRACE_HANDSHAKE("writing", &handshake); 4773 IGNORE_RC(write(pc_pipe[1], 4774 (const char *) &handshake, 4775 sizeof(handshake))); 4776 exit(ERROR_PTYS); 4777 } 4778 handshake.status = PTY_NEW; 4779 (void) strncpy(handshake.buffer, ttydev, sizeof(handshake.buffer)); 4780 TRACE_HANDSHAKE("writing", &handshake); 4781 IGNORE_RC(write(pc_pipe[1], 4782 (const char *) &handshake, 4783 sizeof(handshake))); 4784 break; 4785 4786 case PTY_FATALERROR: 4787 errno = handshake.error; 4788 close(cp_pipe[0]); 4789 close(pc_pipe[1]); 4790 SysError(handshake.fatal_error); 4791 /*NOTREACHED */ 4792 4793 case UTMP_ADDED: 4794 /* The utmp entry was set by our slave. Remember 4795 * this so that we can reset it later. 4796 */ 4797 added_utmp_entry = True; 4798#ifndef USE_SYSV_UTMP 4799 tslot = handshake.tty_slot; 4800#endif /* USE_SYSV_UTMP */ 4801 free(ttydev); 4802 ttydev = x_strdup(handshake.buffer); 4803 break; 4804 case PTY_NEW: 4805 case PTY_NOMORE: 4806 case UTMP_TTYSLOT: 4807 case PTY_EXEC: 4808 default: 4809 xtermWarning("unexpected handshake status %d\n", 4810 (int) handshake.status); 4811 } 4812 } 4813 /* close our sides of the pipes */ 4814 if (!resource.wait_for_map) { 4815 close(cp_pipe[0]); 4816 close(pc_pipe[1]); 4817 } 4818 } 4819#endif /* OPT_PTY_HANDSHAKE */ 4820 } 4821 4822 /* end if no slave */ 4823 /* 4824 * still in parent (xterm process) 4825 */ 4826#ifdef USE_SYSV_SIGHUP 4827 /* hung sh problem? */ 4828 signal(SIGHUP, SIG_DFL); 4829#else 4830 signal(SIGHUP, SIG_IGN); 4831#endif 4832 4833/* 4834 * Unfortunately, System V seems to have trouble divorcing the child process 4835 * from the process group of xterm. This is a problem because hitting the 4836 * INTR or QUIT characters on the keyboard will cause xterm to go away if we 4837 * don't ignore the signals. This is annoying. 4838 */ 4839 4840#if defined(USE_SYSV_SIGNALS) && !defined(SIGTSTP) 4841 signal(SIGINT, SIG_IGN); 4842 4843#ifndef SYSV 4844 /* hung shell problem */ 4845 signal(SIGQUIT, SIG_IGN); 4846#endif 4847 signal(SIGTERM, SIG_IGN); 4848#elif defined(SYSV) || defined(__osf__) 4849 /* if we were spawned by a jobcontrol smart shell (like ksh or csh), 4850 * then our pgrp and pid will be the same. If we were spawned by 4851 * a jobcontrol dumb shell (like /bin/sh), then we will be in our 4852 * parent's pgrp, and we must ignore keyboard signals, or we will 4853 * tank on everything. 4854 */ 4855 if (getpid() == getpgrp()) { 4856 (void) signal(SIGINT, Exit); 4857 (void) signal(SIGQUIT, Exit); 4858 (void) signal(SIGTERM, Exit); 4859 } else { 4860 (void) signal(SIGINT, SIG_IGN); 4861 (void) signal(SIGQUIT, SIG_IGN); 4862 (void) signal(SIGTERM, SIG_IGN); 4863 } 4864 (void) signal(SIGPIPE, Exit); 4865#else /* SYSV */ 4866 signal(SIGINT, Exit); 4867 signal(SIGQUIT, Exit); 4868 signal(SIGTERM, Exit); 4869 signal(SIGPIPE, Exit); 4870#endif /* USE_SYSV_SIGNALS and not SIGTSTP */ 4871#ifdef NO_LEAKS 4872 if (ok_termcap != True) 4873 free(TermName); 4874#endif 4875 4876 return 0; 4877} /* end spawnXTerm */ 4878 4879void 4880Exit(int n) 4881{ 4882 XtermWidget xw = term; 4883 TScreen *screen = TScreenOf(xw); 4884 4885#ifdef USE_UTEMPTER 4886 DEBUG_MSG("handle:Exit USE_UTEMPTER\n"); 4887 if (!resource.utmpInhibit && added_utmp_entry) { 4888 TRACE(("...calling removeFromUtmp\n")); 4889 removeFromUtmp(); 4890 } 4891#elif defined(HAVE_UTMP) 4892#ifdef USE_SYSV_UTMP 4893 struct UTMP_STR utmp; 4894 struct UTMP_STR *utptr; 4895 4896 DEBUG_MSG("handle:Exit USE_SYSV_UTMP\n"); 4897 /* don't do this more than once */ 4898 if (xterm_exiting) { 4899 exit(n); 4900 } 4901 xterm_exiting = True; 4902 4903#ifdef PUCC_PTYD 4904 closepty(ttydev, ptydev, (resource.utmpInhibit ? OPTY_NOP : OPTY_LOGIN), screen->respond); 4905#endif /* PUCC_PTYD */ 4906 4907 /* cleanup the utmp entry we forged earlier */ 4908 if (!resource.utmpInhibit 4909#if OPT_PTY_HANDSHAKE /* without handshake, no way to know */ 4910 && (resource.ptyHandshake && added_utmp_entry) 4911#endif /* OPT_PTY_HANDSHAKE */ 4912 ) { 4913#if defined(USE_UTMP_SETGID) 4914 setEffectiveGroup(save_egid); 4915 TRACE_IDS; 4916#endif 4917 init_utmp(USER_PROCESS, &utmp); 4918 (void) call_setutent(); 4919 4920 /* 4921 * We could use getutline() if we didn't support old systems. 4922 */ 4923 while ((utptr = find_utmp(&utmp)) != 0) { 4924 if (utptr->ut_pid == screen->pid) { 4925 utptr->ut_type = DEAD_PROCESS; 4926#if defined(HAVE_UTMP_UT_XTIME) 4927#if defined(HAVE_UTMP_UT_SESSION) 4928 utptr->ut_session = getsid(0); 4929#endif 4930 utptr->ut_xtime = time((time_t *) 0); 4931 utptr->ut_tv.tv_usec = 0; 4932#else 4933 *utptr->ut_user = 0; 4934 utptr->ut_time = time((time_t *) 0); 4935#endif 4936 (void) call_pututline(utptr); 4937#ifdef WTMP 4938#if defined(WTMPX_FILE) && (defined(SVR4) || defined(__SCO__)) 4939 if (xw->misc.login_shell) 4940 updwtmpx(WTMPX_FILE, utptr); 4941#elif defined(linux) && defined(__GLIBC__) && (__GLIBC__ >= 2) && !(defined(__powerpc__) && (__GLIBC__ == 2) && (__GLIBC_MINOR__ == 0)) 4942 copy_filled(utmp.ut_line, utptr->ut_line, sizeof(utmp.ut_line)); 4943 if (xw->misc.login_shell) 4944 call_updwtmp(etc_wtmp, utptr); 4945#else 4946 /* set wtmp entry if wtmp file exists */ 4947 if (xw->misc.login_shell) { 4948 int fd; 4949 if ((fd = open(etc_wtmp, O_WRONLY | O_APPEND)) >= 0) { 4950 IGNORE_RC(write(fd, utptr, sizeof(*utptr))); 4951 close(fd); 4952 } 4953 } 4954#endif 4955#endif 4956 break; 4957 } 4958 memset(utptr, 0, sizeof(*utptr)); /* keep searching */ 4959 } 4960 (void) call_endutent(); 4961#ifdef USE_UTMP_SETGID 4962 disableSetGid(); 4963 TRACE_IDS; 4964#endif 4965 } 4966#else /* not USE_SYSV_UTMP */ 4967 int wfd; 4968 struct utmp utmp; 4969 4970 DEBUG_MSG("handle:Exit !USE_SYSV_UTMP\n"); 4971 if (!resource.utmpInhibit && added_utmp_entry && 4972 (am_slave < 0 && tslot > 0)) { 4973#if defined(USE_UTMP_SETGID) 4974 setEffectiveGroup(save_egid); 4975 TRACE_IDS; 4976#endif 4977 if ((wfd = open(etc_utmp, O_WRONLY)) >= 0) { 4978 memset(&utmp, 0, sizeof(utmp)); 4979 lseek(wfd, (long) (tslot * sizeof(utmp)), 0); 4980 IGNORE_RC(write(wfd, (char *) &utmp, sizeof(utmp))); 4981 close(wfd); 4982 } 4983#ifdef WTMP 4984 if (xw->misc.login_shell && 4985 (wfd = open(etc_wtmp, O_WRONLY | O_APPEND)) >= 0) { 4986 copy_filled(utmp.ut_line, 4987 my_pty_name(ttydev), 4988 sizeof(utmp.ut_line)); 4989 utmp.ut_time = time((time_t *) 0); 4990 IGNORE_RC(write(wfd, (char *) &utmp, sizeof(utmp))); 4991 close(wfd); 4992 } 4993#endif /* WTMP */ 4994#ifdef USE_UTMP_SETGID 4995 disableSetGid(); 4996 TRACE_IDS; 4997#endif 4998 } 4999#endif /* USE_SYSV_UTMP */ 5000#endif /* HAVE_UTMP */ 5001 5002 cleanup_colored_cursor(); 5003 5004 /* 5005 * Flush pending data before releasing ownership, so nobody else can write 5006 * in the middle of the data. 5007 */ 5008 ttyFlush(screen->respond); 5009 5010#ifdef USE_PTY_SEARCH 5011 if (am_slave < 0) { 5012 TRACE_IDS; 5013 /* restore ownership of tty and pty */ 5014 set_owner(ttydev, 0, 0, 0666U); 5015#if (defined(USE_PTY_DEVICE) && !defined(__sgi) && !defined(__hpux)) 5016 set_owner(ptydev, 0, 0, 0666U); 5017#endif 5018 } 5019#endif 5020 5021 /* 5022 * Close after releasing ownership to avoid race condition: other programs 5023 * grabbing it, and *then* having us release ownership.... 5024 */ 5025 close(screen->respond); /* close explicitly to avoid race with slave side */ 5026#ifdef ALLOWLOGGING 5027 if (screen->logging) 5028 CloseLog(xw); 5029#endif 5030 5031 xtermPrintOnXError(xw, n); 5032 5033#ifdef NO_LEAKS 5034 if (n == 0) { 5035 Display *dpy = TScreenOf(xw)->display; 5036 5037 TRACE(("Freeing memory leaks\n")); 5038 5039 if (toplevel) { 5040 XtDestroyWidget(toplevel); 5041 TRACE(("destroyed top-level widget\n")); 5042 } 5043 sortedOpts(0, 0, 0); 5044 noleaks_charproc(); 5045 noleaks_ptydata(); 5046#if OPT_GRAPHICS 5047 noleaks_graphics(); 5048#endif 5049#if OPT_WIDE_CHARS 5050 noleaks_CharacterClass(); 5051#endif 5052 /* XrmSetDatabase(dpy, 0); increases leaks ;-) */ 5053 XtCloseDisplay(dpy); 5054 XtDestroyApplicationContext(app_con); 5055 xtermCloseSession(); 5056 TRACE(("closed display\n")); 5057 5058 TRACE_CLOSE(); 5059 } 5060#endif 5061 5062 exit(n); 5063} 5064 5065/* ARGSUSED */ 5066static void 5067resize_termcap(XtermWidget xw) 5068{ 5069 char *newtc = get_tcap_buffer(xw); 5070 5071#ifndef USE_SYSV_ENVVARS 5072 if (!TEK4014_ACTIVE(xw) && *newtc) { 5073 TScreen *screen = TScreenOf(xw); 5074 char *ptr1, *ptr2; 5075 size_t i; 5076 int li_first = 0; 5077 char *temp; 5078 char oldtc[TERMCAP_SIZE]; 5079 5080 strcpy(oldtc, newtc); 5081 TRACE(("resize %s\n", oldtc)); 5082 if ((ptr1 = x_strindex(oldtc, "co#")) == NULL) { 5083 strcat(oldtc, "co#80:"); 5084 ptr1 = x_strindex(oldtc, "co#"); 5085 } 5086 if ((ptr2 = x_strindex(oldtc, "li#")) == NULL) { 5087 strcat(oldtc, "li#24:"); 5088 ptr2 = x_strindex(oldtc, "li#"); 5089 } 5090 if (ptr1 > ptr2) { 5091 li_first++; 5092 temp = ptr1; 5093 ptr1 = ptr2; 5094 ptr2 = temp; 5095 } 5096 ptr1 += 3; 5097 ptr2 += 3; 5098 strncpy(newtc, oldtc, i = (size_t) (ptr1 - oldtc)); 5099 temp = newtc + i; 5100 sprintf(temp, "%d", (li_first 5101 ? MaxRows(screen) 5102 : MaxCols(screen))); 5103 temp += strlen(temp); 5104 if ((ptr1 = strchr(ptr1, ':')) != 0 && (ptr1 < ptr2)) { 5105 strncpy(temp, ptr1, i = (size_t) (ptr2 - ptr1)); 5106 temp += i; 5107 sprintf(temp, "%d", (li_first 5108 ? MaxCols(screen) 5109 : MaxRows(screen))); 5110 if ((ptr2 = strchr(ptr2, ':')) != 0) { 5111 strcat(temp, ptr2); 5112 } 5113 } 5114 TRACE((" ==> %s\n", newtc)); 5115 TRACE((" new size %dx%d\n", MaxRows(screen), MaxCols(screen))); 5116 } 5117#endif /* USE_SYSV_ENVVARS */ 5118} 5119 5120#endif /* ! VMS */ 5121 5122/* 5123 * Does a non-blocking wait for a child process. If the system 5124 * doesn't support non-blocking wait, do nothing. 5125 * Returns the pid of the child, or 0 or -1 if none or error. 5126 */ 5127int 5128nonblocking_wait(void) 5129{ 5130#ifdef USE_POSIX_WAIT 5131 pid_t pid; 5132 5133 pid = waitpid(-1, NULL, WNOHANG); 5134#elif defined(USE_SYSV_SIGNALS) && (defined(CRAY) || !defined(SIGTSTP)) 5135 /* cannot do non-blocking wait */ 5136 int pid = 0; 5137#else /* defined(USE_SYSV_SIGNALS) && (defined(CRAY) || !defined(SIGTSTP)) */ 5138#if defined(Lynx) 5139 int status; 5140#else 5141 union wait status; 5142#endif 5143 int pid; 5144 5145 pid = wait3(&status, WNOHANG, (struct rusage *) NULL); 5146#endif /* USE_POSIX_WAIT else */ 5147 return pid; 5148} 5149 5150#ifndef VMS 5151 5152/* ARGSUSED */ 5153static void 5154reapchild(int n GCC_UNUSED) 5155{ 5156 int olderrno = errno; 5157 int pid; 5158 5159 DEBUG_MSG("handle:reapchild\n"); 5160 5161 pid = wait(NULL); 5162 5163#ifdef USE_SYSV_SIGNALS 5164 /* cannot re-enable signal before waiting for child 5165 * because then SVR4 loops. Sigh. HP-UX 9.01 too. 5166 */ 5167 (void) signal(SIGCHLD, reapchild); 5168#endif 5169 5170 do { 5171 if (pid == TScreenOf(term)->pid) { 5172 DEBUG_MSG("Exiting\n"); 5173 if (!hold_screen) 5174 need_cleanup = True; 5175 } 5176 } while ((pid = nonblocking_wait()) > 0); 5177 5178 errno = olderrno; 5179} 5180#endif /* !VMS */ 5181 5182static void 5183remove_termcap_entry(char *buf, const char *str) 5184{ 5185 char *base = buf; 5186 char *first = base; 5187 int count = 0; 5188 size_t len = strlen(str); 5189 5190 TRACE(("*** remove_termcap_entry('%s', '%s')\n", str, buf)); 5191 5192 while (*buf != 0) { 5193 if (!count && !strncmp(buf, str, len)) { 5194 while (*buf != 0) { 5195 if (*buf == '\\') 5196 buf++; 5197 else if (*buf == ':') 5198 break; 5199 if (*buf != 0) 5200 buf++; 5201 } 5202 while ((*first++ = *buf++) != 0) { 5203 ; 5204 } 5205 TRACE(("...removed_termcap_entry('%s', '%s')\n", str, base)); 5206 return; 5207 } else if (*buf == '\\') { 5208 buf++; 5209 } else if (*buf == ':') { 5210 first = buf; 5211 count = 0; 5212 } else if (!isspace(CharOf(*buf))) { 5213 count++; 5214 } 5215 if (*buf != 0) 5216 buf++; 5217 } 5218 TRACE(("...cannot remove\n")); 5219} 5220 5221/* 5222 * parse_tty_modes accepts lines of the following form: 5223 * 5224 * [SETTING] ... 5225 * 5226 * where setting consists of the words in the modelist followed by a character 5227 * or ^char. 5228 */ 5229static int 5230parse_tty_modes(char *s, struct _xttymodes *modelist) 5231{ 5232 struct _xttymodes *mp; 5233 int c; 5234 int count = 0; 5235 5236 TRACE(("parse_tty_modes\n")); 5237 for (;;) { 5238 size_t len; 5239 5240 while (*s && isascii(CharOf(*s)) && isspace(CharOf(*s))) 5241 s++; 5242 if (!*s) 5243 return count; 5244 5245 for (len = 0; isalnum(CharOf(s[len])); ++len) ; 5246 for (mp = modelist; mp->name; mp++) { 5247 if (len == mp->len 5248 && strncmp(s, mp->name, mp->len) == 0) 5249 break; 5250 } 5251 if (!mp->name) 5252 return -1; 5253 5254 s += mp->len; 5255 while (*s && isascii(CharOf(*s)) && isspace(CharOf(*s))) 5256 s++; 5257 if (!*s) 5258 return -1; 5259 5260 if ((c = decode_keyvalue(&s, False)) != -1) { 5261 mp->value = c; 5262 mp->set = 1; 5263 count++; 5264 TRACE(("...parsed #%d: %s=%#x\n", count, mp->name, c)); 5265 } 5266 } 5267} 5268 5269#ifndef VMS /* don't use pipes on OpenVMS */ 5270int 5271GetBytesAvailable(int fd) 5272{ 5273#if defined(FIONREAD) 5274 int arg; 5275 ioctl(fd, FIONREAD, (char *) &arg); 5276 return (int) arg; 5277#elif defined(__CYGWIN__) 5278 fd_set set; 5279 struct timeval select_timeout = 5280 {0, 0}; 5281 5282 FD_ZERO(&set); 5283 FD_SET(fd, &set); 5284 if (Select(fd + 1, &set, NULL, NULL, &select_timeout) > 0) 5285 return 1; 5286 else 5287 return 0; 5288#elif defined(FIORDCK) 5289 return (ioctl(fd, FIORDCHK, NULL)); 5290#else /* !FIORDCK */ 5291 struct pollfd pollfds[1]; 5292 5293 pollfds[0].fd = fd; 5294 pollfds[0].events = POLLIN; 5295 return poll(pollfds, 1, 0); 5296#endif 5297} 5298#endif /* !VMS */ 5299 5300/* Utility function to try to hide system differences from 5301 everybody who used to call killpg() */ 5302 5303int 5304kill_process_group(int pid, int sig) 5305{ 5306 TRACE(("kill_process_group(pid=%d, sig=%d)\n", pid, sig)); 5307#if defined(SVR4) || defined(SYSV) || !defined(X_NOT_POSIX) 5308 return kill(-pid, sig); 5309#else 5310 return killpg(pid, sig); 5311#endif 5312} 5313 5314#if OPT_EBCDIC 5315int 5316A2E(int x) 5317{ 5318 char c; 5319 c = x; 5320 __atoe_l(&c, 1); 5321 return c; 5322} 5323 5324int 5325E2A(int x) 5326{ 5327 char c; 5328 c = x; 5329 __etoa_l(&c, 1); 5330 return c; 5331} 5332#endif 5333 5334#if defined(__QNX__) && !defined(__QNXNTO__) 5335#include <sys/types.h> 5336#include <sys/proc_msg.h> 5337#include <sys/kernel.h> 5338#include <string.h> 5339#include <errno.h> 5340 5341struct _proc_session ps; 5342struct _proc_session_reply rps; 5343 5344int 5345qsetlogin(char *login, char *ttyname) 5346{ 5347 int v = getsid(getpid()); 5348 5349 memset(&ps, 0, sizeof(ps)); 5350 memset(&rps, 0, sizeof(rps)); 5351 5352 ps.type = _PROC_SESSION; 5353 ps.subtype = _PROC_SUB_ACTION1; 5354 ps.sid = v; 5355 strcpy(ps.name, login); 5356 5357 Send(1, &ps, &rps, sizeof(ps), sizeof(rps)); 5358 5359 if (rps.status < 0) 5360 return (rps.status); 5361 5362 ps.type = _PROC_SESSION; 5363 ps.subtype = _PROC_SUB_ACTION2; 5364 ps.sid = v; 5365 sprintf(ps.name, "//%d%s", getnid(), ttyname); 5366 Send(1, &ps, &rps, sizeof(ps), sizeof(rps)); 5367 5368 return (rps.status); 5369} 5370#endif 5371