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