# # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright (c) 2018, Joyent, Inc. PROG= stty ttymon ttyadm sttydefs XPG4PROG= stty # the 'stty' objects have to be made in a separate directory # since only they are built with the -DEUC flag (see below). STTYOBJ= sttyobjs/stty.o sttyobjs/sttytable.o sttyobjs/sttyparse.o XPG4STTYOBJ= sttyobjs.xpg4/stty.o sttyobjs.xpg4/sttytable.o \ sttyobjs.xpg4/sttyparse.o TTYMONOBJ= ttymon.o tmglobal.o tmhandler.o tmpmtab.o tmttydefs.o \ tmparse.o tmsig.o tmsac.o tmchild.o tmautobaud.o tmterm.o \ tmutmp.o tmpeek.o tmlog.o tmlock.o tmutil.o tmexpress.o \ sttytable.o sttyparse.o ulockf.o TTYADMOBJ= ttyadm.o tmutil.o admutil.o STTYDEFSOBJ= sttydefs.o admutil.o tmttydefs.o tmparse.o sttytable.o \ sttyparse.o tmglobal.o OBJS= $(STTYOBJ) $(XPG4STTYOBJ) $(TTYMONOBJ) $(TTYADMOBJ) $(STTYDEFSOBJ) STTYSRC= stty.c sttytable.c sttyparse.c TTYMONSRC= $(TTYMONOBJ:%.o=%.c) TTYADMSRC= $(TTYADMOBJ:%.o=%.c) STTYDEFSSRC= $(STTYDEFSOBJ:%.o=%.c) SRCS= $(STTYSRC) $(TTYMONSRC) $(TTYADMSRC) $(STTYDEFSSRC) .PARALLEL: $(OBJS) $(PROG) include ../Makefile.cmd # If machine name and /etc/issue file need to be printed # before the service prompt is printed, then: CPPFLAGS += -DSYS_NAME # If debug is needed, then: CPPFLAGS += -DDEBUG # fix for 1111333 - turn on SYS_NAME so /etc/issue will be read if it exists CPPFLAGS += -DSYS_NAME $(XPG4): CPPFLAGS += -DXPG4 sttydefs : LDLIBS += -lnsl ttymon : LDLIBS += -lnsl -lsec -ldevinfo # Only stty can be built with -DEUC. ttymon will dump core unless further # changes are made to it. $(STTYOBJ) : CPPFLAGS += -DEUC $(XPG4STTYOBJ) : CPPFLAGS += -DEUC CFLAGS += $(CCVERBOSE) LDFLAGS += $(MAPFILE.NGB:%=-Wl,-M%) # Hammerhead: GNU ld --version-script for executables does NOT export symbols # to .dynsym (unlike illumos ld mapfiles). ttymon uses putenv("TTYPROMPT=...") # then execve(argv, environ) — both must see the same environ pointer. Without # .dynsym export, libc's putenv modifies its own copy while ttymon's execve # reads the unmodified BSS copy, so TTYPROMPT is lost and login re-prompts. ttymon : LDFLAGS += -Wl,--export-dynamic-symbol=environ \ -Wl,--export-dynamic-symbol=_environ # # Message catalog # POFILES= $(STTYOBJ:sttyobjs/%.o=%.po) POFILE= ttymon.po LIBSAFD= $(ROOTLIB)/saf DIRS= $(LIBSAFD) LIBSAF= ttymon USRSBINF= sttydefs ttyadm # Hammerhead: stty installs to /bin with /usr/bin compat symlink BINF= stty ROOTLIBF= $(LIBSAF:%=$(LIBSAFD)/%) ROOTUSRSBINF= $(USRSBINF:%=$(ROOTUSRSBIN)/%) ROOTBINF= $(BINF:%=$(ROOTESSBIN)/%) # Hammerhead: ROOTUSRBINLINKS removed — /bin is usr/bin (path flattening), # so ../../bin symlinks from /usr/bin would be circular. $(ROOTLIBF) : FILEMODE = 0555 $(ROOTUSRSBINF) : FILEMODE = 0755 $(LIBSAFD)/% : % $(INS.file) # Hammerhead: /usr/xpg6/bin/stty → /bin/stty (3 levels up) ROOTXPG6SYMLINK= $(ROOTXPG6BIN)/stty .KEEP_STATE: all: $(PROG) $(XPG4) stty: sttyobjs $(STTYOBJ) $(MAPFILE.NGB) $(LINK.c) $(STTYOBJ) -o $@ $(LDLIBS) $(POST_PROCESS) stty.xpg4: $(XPG4STTYOBJ) $(MAPFILE.NGB) $(LINK.c) $(XPG4STTYOBJ) -o $@ $(LDLIBS) $(POST_PROCESS) $(XPG4STTYOBJ): sttyobjs.xpg4 sttyobjs/%.o: %.c $(COMPILE.c) -o $@ $< sttyobjs.xpg4/%.o: %.c $(COMPILE.c) -o $@ $< sttyobjs: -@mkdir -p $@ sttyobjs.xpg4: -@mkdir -p $@ ttymon: $(TTYMONOBJ) $(MAPFILE.NGB) $(LINK.c) $(TTYMONOBJ) -o $@ $(LDLIBS) -lpam $(POST_PROCESS) ttyadm: $(TTYADMOBJ) $(MAPFILE.NGB) $(LINK.c) $(TTYADMOBJ) -o $@ $(LDLIBS) $(POST_PROCESS) sttydefs: $(STTYDEFSOBJ) $(MAPFILE.NGB) $(LINK.c) $(STTYDEFSOBJ) -o $@ $(LDLIBS) $(POST_PROCESS) install: all $(DIRS) $(ROOTLIBF) $(ROOTUSRSBINF) $(ROOTBINF) \ $(ROOTXPG4PROG) $(ROOTXPG6SYMLINK) -$(RM) $(ROOTETC)/getty -$(SYMLINK) ../usr/lib/saf/ttymon $(ROOTETC)/getty $(ROOTXPG6SYMLINK): $(ROOTBINF) -$(RM) $@ -$(SYMLINK) ../../../bin/$(BINF) $@ # Hammerhead: /usr/bin/stty compat symlink removed — /bin is usr/bin (path flattening) $(POFILE): $(POFILES) $(RM) $@ cat $(POFILES) > $@ $(DIRS): $(INS.dir) clean: $(RM) $(OBJS) include ../Makefile.targ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #include #include #include #include #include #include #include #include "tmstruct.h" #include "ttymon.h" /* * find_label - return 1 if ttylabel already exists * - return 0 otherwise */ int find_label(FILE *fp, char *ttylabel) { char *p; /* working pointer */ int line = 0; /* line number we found entry on */ static char buf[BUFSIZ]; /* scratch buffer */ while (fgets(buf, BUFSIZ, fp)) { line++; p = buf; while (isspace(*p)) p++; if ((p = strtok(p, " :")) != NULL) { if (strcmp(p, ttylabel) == 0) return (line); } } if (!feof(fp)) { (void) fprintf(stderr, "error reading \"%s\"\n", TTYDEFS); return (0); } return (0); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #ifndef _PARMS_H #define _PARMS_H /* If running SVR3, #define both ATTSVR3 and ATTSV */ #define ATTSVR3 /* System V Release 3 */ /* One of the following four lines should not be commented out. * The other three should be unless you are running a unique hybrid. */ #define ATTSV /* System III or System V */ /* #define V7 */ /* Version 7 systems (32V, Berkeley 4BSD, 4.1BSD) */ /* #define BSD4_2 */ /* Berkeley 4.2BSD */ /* #define V8 */ /* Research Eighth Edition */ /* Owner of setud files running on behalf of uucp. Needed in case * root runs uucp and euid is not honored by kernel. * GID is needed for some chown() calls. * Also used if guinfo() cannot find the current users ID in the * password file. */ #define UUCPUID (uid_t) 5 /* */ #define UUCPGID (gid_t) 5 /* */ /* define ATTSVKILL if your system has a kill(2) that accepts kill(0, pid) * as a test for killability. If ATTSV is defined this will automatically * be defined anyway. */ #define ATTSVKILL /* */ /* * the next two lines control high resolution sleeps, called naps. * * most UNIX versions have no nap() system call; they want NONAP defined, * in which case one is provided in the code. this includes all standard * versions of UNIX. * * some sites use a fast timer that reads a number of clock ticks and naps * for that interval; they want NONAP defined, and FASTTIMER defined as * the name of the device, e.g., /dev/ft. * * repeating, NONAP should be disabled *only* if your standard library has a * function called nap. */ #define NONAP /* nominal case -- no nap() in the standard library */ /* #define FASTTIMER "/dev/ft" */ /* identify the device used for naps */ /* * we use ustat to decide whether there's enough space to receive a * file. if you're not ATTSV, you can use a setgid program to read the * number of free blocks and free inodes directly off the disk. if you * choose this course, do not define NOUSTAT; rather, define V7USTAT to * be the name of that program. be sure it accepts 2 args, major and minor * device numbers, and returns two numbers, blocks and inodes, in * "%d %d" format, or you'll never receive another file. */ /* #define V7USTAT "/usr/local/lib/ustat" */ /* #define NOUSTAT */ /* define NOUSTAT if you don't have ustat */ /* define GRPCHK if you want to restrict the ability to read */ /* Systems file stuff by way of the DEBUG flags based on a group id range */ /* ex: if (GRPCHK(getgid()) no_secrets(); */ #define GRPMIN (gid_t) 2 /* */ #define GRPMAX (gid_t) 10 /* */ #define GRPCHK(gid) ( gid >= GRPMIN && gid <= GRPMAX ? 1 : 0 ) /* */ /* #define GRPCHK(gid) 1 */ /* Systems info is not protected from DEBUG */ /* definitions for the types of networks and dialers that are available */ /* used to depend on STANDALONE, but now done at runtime via Sysfiles */ #define DATAKIT /* define DATAKIT if datakit is available. */ /* #define UNET */ /* define UNET if you have 3com ethernet software */ /* #define TCP */ /* TCP (bsd systems) */ /* #define SYTEK*/ /* for sytek network */ #ifdef ATTSVR3 #define TLI /* for AT&T Transport Layer Interface networks */ #define TLIS /* for AT&T Transport Layer Interface networks */ /* with streams module "tirdwr" */ #endif /* ATTSVR3 */ #define DIAL801 /* 801/212-103 auto dialers */ /* define DUMB_DN if your dn driver (801 acu) cannot handle '=' */ /* #define DUMB_DN */ /* * Define protocols that are to be linked into uucico: * * The following table shows which protocols and networks work well * together. The g protocol works over noisy links. The e protocol * assumes that the underlying network provides an error free communications * channel that transfers the data in sequence without duplication. The * d protocols makes the same assumptions as the e protocol, but in addition * it does Datakit specific ioctl's. The g protocol is always included in * uucico. To include the other protocols, 1) insure that the symbol from * the Symbol column is defined in this file and 2) include the file from * the File comlumn in the definition of PROTOCOLS in uucp.mk. * * Prot. * Letter Symbol File Applicable Media * * g none - - * e E_PROTOCOL eio.c TCP, UNET, TLI, and DATAKIT. * d D_PROTOCOL dio.c DATAKIT * x X_PROTOCOL xio.c - * * The next six lines conditionally define the protocol symbols for d * and e protocols based on the networks that were chosen above. For the * x protocol you must explicitly define X_PROTOCOL. */ #ifdef DATAKIT /* Should include D protocol for Datakit. */ #define D_PROTOCOL #endif /* DATAKIT */ #if defined TCP || defined UNET || defined TLI || defined DATAKIT #define E_PROTOCOL /* Include e protocol. */ #endif /* TCP || UNET || TLI || DATAKIT */ /* #define X_PROTOCOL */ /* define X_PROTOCOL to use the xio protocol */ #define X_PROTOCOL /* aeh - to check compilation */ #define MAXCALLTRIES 2 /* maximum call attempts per Systems file line */ /* define DEFAULT_BAUDRATE to be the baud rate you want to use when both */ /* Systems file and Devices file allow Any */ #define DEFAULT_BAUDRATE "9600" /* */ /*define permission modes for the device */ #define M_DEVICEMODE (mode_t) 0600 /* manager device mode */ #define S_DEVICEMODE (mode_t) 0600 /* subsidiary device mode */ #define R_DEVICEMODE (mode_t) 0600 /* default mode to restore */ /* NO_MODEM_CTRL - define this if you have very old hardware * that does not know how to correctly handle modem control * Some old pdp/11 hardware such as dk, dl * If you define this, and have DH devices for direct lines, * the ports will often hang and be unusable. */ /*#define NO_MODEM_CTRL */ /* UUSTAT_TBL - this is the maximum number of machines that * status may be needed at any instant. * If you are not concerned with memory for a seldom used program, * make it very large. * This number is also used in uusched for its machine table -- it has * the same properties as the one in uustat. */ /* #define UUSTAT_TBL 1000 */ /* big machine with lots of traffic */ #define UUSTAT_TBL 200 /* define UNAME if uname() should be used to get uucpname * This will be defined automatically if ATTSV is defined */ #define UNAME /* */ /* initial wait time after failure before retry */ #define RETRYTIME 300 /* 5 minutes */ /* MAXRETRYTIME is for exponential backoff limit. * NOTE - this should not be 24 hours so that * retry is not always at the same time each day */ #define MAXRETRYTIME 82800 /* 23 hours */ #define ASSERT_RETRYTIME 86400 /* retry time for ASSERT errors */ /* This is the path that will be used for uuxqt command executions */ #define PATH "PATH=/usr/bin " /* */ /* This is the set of default commands that can be executed */ /* if non is given for the system name in PERMISSIONS file */ /* It is a colon separated list as in PERMISSIONS file */ #define DEFAULTCMDS "rmail" /* standard default command list */ /* define HZ to be the number of clock ticks per second */ /* #define HZ 60 */ /* not needed for ATTSV or above */ /* * put in local uucp name of this machine if there is no "/etc/whoami" * and no uname() (this is a last resort) */ #define MYNAME "kilroy" /* */ /* define NOSTRANGERS if you want to reject calls from systems which * are not in your Systems file. If defined, NOSTRANGERS should be the name * of the program to execute when such a system dials in. The argument * to said program will be the name of said system. Typically this is a shell * procedure that sends mail to the uucp administrator informing them of an * attempt to communicate by an unknown system. * NOTE - if this is defined, it can be overridden by the administrator * by making the command non-executable. (It can be turned on and off * by changing the mode of the command.) */ #define NOSTRANGERS "/usr/lib/uucp/remote.unknown" /* */ /* define LIMITS to be the name of a file which contains information * about the number of simultaneous uucicos,uuxqts, and uuscheds * that are allowed to run. If it is not defined, then there may be * "many" uucicos, uuxqts, and uuscheds running. */ #define LIMITS "/etc/uucp/Limits" /* */ /* define USRSPOOLLOCKS if you like your lock files in /var/spool/locks * be sure other programs such as 'cu' and 'ct' know about this * * WARNING: if you do not define USRSPOOLLOCKS, then $LOCK in * uudemon.cleanup must be changed. */ #define USRSPOOLLOCKS /* define to use /var/spool/locks for LCK files */ /* define PKSPEEDUP if you want to try the recommended speedup in pkcget. * this entails sleeping between reads at low baud rates. */ #define PKSPEEDUP /* */ #endif /* !_PARMS_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 1999-2002 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright (c) 2014, Joyent, Inc. All rights reserved. */ /* * Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T * All Rights Reserved * */ #include #include #include #include #include #include #include #include #include #include #ifdef EUC #include #include #include #include #include #include #include #include #include #include #include #endif /* EUC */ #include "stty.h" #include "tmextern.h" static char *STTY = "stty: "; static int pitt = 0; static struct termios cb; static struct termio ocb; /* for non-streams devices */ static struct stio stio; static struct termiox termiox; static struct winsize winsize, owinsize; static int term; #ifdef EUC static struct eucioc kwp; static eucwidth_t wp; static ldterm_cs_data_user_t cswp; /* User side codeset width data */ static ldterm_cs_data_user_t kcswp; /* Kernel side codeset width data */ static int invalid_ldterm_dat_file; #endif /* EUC */ static void prmodes(void); static void pramodes(void); static void pit(unsigned char what, char *itsname, char *sep); static void delay(int m, char *s); static void prspeed(char *c, int s); static void prencode(void); int main(int argc, char *argv[]) { int i; int fd; char *s_arg; /* s_arg: ptr to mode to be set */ #ifdef EUC char *lc; char tmps[PATH_MAX]; #endif /* EUC */ (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) #define TEXT_DOMAIN "SYS_TEST" #endif (void) textdomain(TEXT_DOMAIN); #ifdef EUC lc = setlocale(LC_CTYPE, (const char *)NULL); if (lc) { (void) sprintf(tmps, _LDTERM_DAT_PATH, lc); fd = open(tmps, O_RDONLY, 0); if (fd != -1) { if (read(fd, (void *)&cswp, sizeof (cswp)) < sizeof (cswp)) { (void) fprintf(stderr, gettext( "cannot read entire %s file\n"), tmps); exit(2); } (void) close(fd); /* * If the ldterm.dat contains invalid data or * the current locale name is too long, we clear * the 'cswp' and flag the invalid ldterm.dat since * we are not going to use the data. */ if (cswp.version > LDTERM_DATA_VERSION || cswp.codeset_type < LDTERM_CS_TYPE_MIN || cswp.codeset_type > LDTERM_CS_TYPE_MAX || strlen(lc) >= MAXNAMELEN || (cswp.codeset_type == LDTERM_CS_TYPE_EUC && cswp.csinfo_num > LDTERM_CS_TYPE_EUC_MAX_SUBCS) || (cswp.codeset_type == LDTERM_CS_TYPE_PCCS && (cswp.csinfo_num < LDTERM_CS_TYPE_PCCS_MIN_SUBCS || cswp.csinfo_num > LDTERM_CS_TYPE_PCCS_MAX_SUBCS))) { (void) memset((void *)&cswp, 0, sizeof (cswp)); invalid_ldterm_dat_file = 1; } else { (void) strcpy(cswp.locale_name, lc); } } } getwidth(&wp); #endif /* EUC */ if ((term = get_ttymode(0, &ocb, &cb, &stio, &termiox, &winsize #ifdef EUC /* */, &kwp, &kcswp #endif /* EUC */ /* */)) < 0) { perror(STTY); exit(2); } owinsize = winsize; if (argc == 1) { prmodes(); exit(0); } if ((argc == 2) && (argv[1][0] == '-') && (argv[1][2] == '\0')) { switch (argv[1][1]) { case 'a': pramodes(); return (0); case 'g': prencode(); return (0); case '-': prmodes(); /* stty -- */ return (0); default: (void) fprintf(stderr, gettext( "usage: stty [-a| -g]\n")); (void) fprintf(stderr, gettext( " stty [modes]\n")); return (2); } } if ((argc == 3) && (argv[1][0] == '-') && (argv[1][2] == '\0') && (argv[2][0] == '-') && (argv[2][1] == '-') && (argv[2][2] == '\0')) { switch (argv[1][1]) { case 'a': pramodes(); return (0); case 'g': prencode(); return (0); default: (void) fprintf(stderr, gettext( "usage: stty [-a| -g]\n")); (void) fprintf(stderr, gettext( " stty [modes]\n")); return (2); } } if ((argc >= 3) && (argv[1][0] == '-') && (argv[1][1] == '-') && (argv[1][2] == '\0')) { /* ignore -- */ --argc; ++argv; } #ifdef EUC s_arg = sttyparse(argc, argv, term, &ocb, &cb, &termiox, &winsize, &wp, &kwp, &cswp, &kcswp); #else s_arg = sttyparse(argc, argv, term, &ocb, &cb, &termiox, &winsize); #endif /* EUC */ if (s_arg != NULL) { char *s = s_arg; if (*s == '-') s++; for (i = 0; not_supported[i]; i++) { if (strcmp(not_supported[i], s) == 0) { (void) fprintf(stderr, gettext( "mode not supported on this device: %s\n"), s_arg); exit(2); } } (void) fprintf(stderr, gettext("unknown mode: %s\n"), s_arg); return (2); } if (set_ttymode(0, term, &ocb, &cb, &stio, &termiox, &winsize, &owinsize #ifdef EUC /* */, &kwp, &kcswp, invalid_ldterm_dat_file #endif /* EUC */ /* */) == -1) { perror(STTY); return (2); } return (0); } static void prmodes(void) /* print modes, no options, argc is 1 */ { int m; if (!(term & ASYNC)) { m = stio.imode; if (m & IUCLC) (void) printf("iuclc "); else (void) printf("-iuclc "); m = stio.omode; if (m & OLCUC) (void) printf("olcuc "); else (void) printf("-olcuc "); if (m & TAB3) (void) printf("tab3 "); m = stio.lmode; if (m & XCASE) (void) printf("xcase "); else (void) printf("-xcase "); if (m & STFLUSH) (void) printf("stflush "); else (void) printf("-stflush "); if (m & STWRAP) (void) printf("stwrap "); else (void) printf("-stwrap "); if (m & STAPPL) (void) printf("stappl "); else (void) printf("-stappl "); (void) printf("\n"); } if (term & ASYNC) { m = cb.c_cflag; if ((term & TERMIOS) && cfgetispeed(&cb) != 0 && cfgetispeed(&cb) != cfgetospeed(&cb)) { prspeed("ispeed ", cfgetispeed(&cb)); prspeed("ospeed ", cfgetospeed(&cb)); } else prspeed("speed ", cfgetospeed(&cb)); if (m&PARENB) { if ((m&PAREXT) && (term & TERMIOS)) { if (m&PARODD) (void) printf("markp "); else (void) printf("spacep "); } else { if (m&PARODD) (void) printf("oddp "); else (void) printf("evenp "); } } else (void) printf("-parity "); if (((m&PARENB) && !(m&CS7)) || (!(m&PARENB) && !(m&CS8))) (void) printf("cs%c ", '5'+(m&CSIZE)/CS6); if (m&CSTOPB) (void) printf("cstopb "); if (m&HUPCL) (void) printf("hupcl "); if (!(m&CREAD)) (void) printf("-cread "); if (m&CLOCAL) (void) printf("clocal "); if (m&LOBLK) (void) printf("loblk "); (void) printf("\n"); if (ocb.c_line != 0) (void) printf(gettext("line = %d; "), ocb.c_line); if (term & WINDOW) { (void) printf(gettext("rows = %d; columns = %d;"), winsize.ws_row, winsize.ws_col); (void) printf(gettext( " ypixels = %d; xpixels = %d;\n"), winsize.ws_ypixel, winsize.ws_xpixel); } if ((cb.c_lflag&ICANON) == 0) (void) printf(gettext("min = %d; time = %d;\n"), cb.c_cc[VMIN], cb.c_cc[VTIME]); if (cb.c_cc[VINTR] != CINTR) pit(cb.c_cc[VINTR], "intr", "; "); if (cb.c_cc[VQUIT] != CQUIT) pit(cb.c_cc[VQUIT], "quit", "; "); if (cb.c_cc[VERASE] != CERASE) pit(cb.c_cc[VERASE], "erase", "; "); if (term & TERMIOS) { if (cb.c_cc[VERASE2] != CERASE2) pit(cb.c_cc[VERASE], "erase2", "; "); } if (cb.c_cc[VKILL] != CKILL) pit(cb.c_cc[VKILL], "kill", "; "); if (cb.c_cc[VEOF] != CEOF) pit(cb.c_cc[VEOF], "eof", "; "); if (cb.c_cc[VEOL] != CNUL) pit(cb.c_cc[VEOL], "eol", "; "); if (cb.c_cc[VEOL2] != CNUL) pit(cb.c_cc[VEOL2], "eol2", "; "); if (cb.c_cc[VSWTCH] != CSWTCH) pit(cb.c_cc[VSWTCH], "swtch", "; "); if (term & TERMIOS) { if (cb.c_cc[VSTART] != CSTART) pit(cb.c_cc[VSTART], "start", "; "); if (cb.c_cc[VSTOP] != CSTOP) pit(cb.c_cc[VSTOP], "stop", "; "); if (cb.c_cc[VSUSP] != CSUSP) pit(cb.c_cc[VSUSP], "susp", "; "); if (cb.c_cc[VDSUSP] != CDSUSP) pit(cb.c_cc[VDSUSP], "dsusp", "; "); if (cb.c_cc[VREPRINT] != CRPRNT) pit(cb.c_cc[VREPRINT], "rprnt", "; "); if (cb.c_cc[VDISCARD] != CFLUSH) pit(cb.c_cc[VDISCARD], "flush", "; "); if (cb.c_cc[VWERASE] != CWERASE) pit(cb.c_cc[VWERASE], "werase", "; "); if (cb.c_cc[VLNEXT] != CLNEXT) pit(cb.c_cc[VLNEXT], "lnext", "; "); if (cb.c_cc[VSTATUS] != CSTATUS) pit(cb.c_cc[VSTATUS], "status", "; "); } if (pitt) (void) printf("\n"); m = cb.c_iflag; if (m&IGNBRK) (void) printf("ignbrk "); else if (m&BRKINT) (void) printf("brkint "); if (!(m&INPCK)) (void) printf("-inpck "); else if (m&IGNPAR) (void) printf("ignpar "); if (m&PARMRK) (void) printf("parmrk "); if (!(m&ISTRIP)) (void) printf("-istrip "); if (m&INLCR) (void) printf("inlcr "); if (m&IGNCR) (void) printf("igncr "); if (m&ICRNL) (void) printf("icrnl "); if (m&IUCLC) (void) printf("iuclc "); if (!(m&IXON)) (void) printf("-ixon "); else if (!(m&IXANY)) (void) printf("-ixany "); if (m&IXOFF) (void) printf("ixoff "); if ((term & TERMIOS) && (m&IMAXBEL)) (void) printf("imaxbel "); m = cb.c_oflag; if (!(m&OPOST)) (void) printf("-opost "); else { if (m&OLCUC) (void) printf("olcuc "); if (m&ONLCR) (void) printf("onlcr "); if (m&OCRNL) (void) printf("ocrnl "); if (m&ONOCR) (void) printf("onocr "); if (m&ONLRET) (void) printf("onlret "); if (m&OFILL) { if (m&OFDEL) (void) printf("del-fill "); else (void) printf("nul-fill "); } delay((m&CRDLY)/CR1, "cr"); delay((m&NLDLY)/NL1, "nl"); delay((m&TABDLY)/TAB1, "tab"); delay((m&BSDLY)/BS1, "bs"); delay((m&VTDLY)/VT1, "vt"); delay((m&FFDLY)/FF1, "ff"); } (void) printf("\n"); m = cb.c_lflag; if (!(m&ISIG)) (void) printf("-isig "); if (!(m&ICANON)) (void) printf("-icanon "); if (m&XCASE) (void) printf("xcase "); (void) printf("-echo "+((m&ECHO) != 0)); (void) printf("-echoe "+((m&ECHOE) != 0)); (void) printf("-echok "+((m&ECHOK) != 0)); if (m&ECHONL) (void) printf("echonl "); if (m&NOFLSH) (void) printf("noflsh "); if (m&TOSTOP) (void) printf("tostop "); if (m&ECHOCTL) (void) printf("echoctl "); if (m&ECHOPRT) (void) printf("echoprt "); if (m&ECHOKE) (void) printf("echoke "); if (m&DEFECHO) (void) printf("defecho "); if (m&FLUSHO) (void) printf("flusho "); if (m&PENDIN) (void) printf("pendin "); if (m&IEXTEN) (void) printf("iexten "); (void) printf("\n"); } if (term & FLOW) { m = termiox.x_hflag; if (m & RTSXOFF) (void) printf("rtsxoff "); if (m & CTSXON) (void) printf("ctsxon "); if (m & DTRXOFF) (void) printf("dtrxoff "); if (m & CDXON) (void) printf("cdxon "); if (m & ISXOFF) (void) printf("isxoff "); m = termiox.x_cflag; switch (m & XMTCLK) { case XCIBRG: (void)printf("xcibrg "); break; case XCTSET: (void)printf("xctset "); break; case XCRSET: (void)printf("xcrset "); } switch (m & RCVCLK) { case RCIBRG: (void)printf("rcibrg "); break; case RCTSET: (void)printf("rctset "); break; case RCRSET: (void)printf("rcrset "); } switch (m & TSETCLK) { case TSETCOFF: (void)printf("tsetcoff "); break; case TSETCRBRG: (void)printf("tsetcrbrg "); break; case TSETCTBRG: (void)printf("tsetctbrg "); break; case TSETCTSET: (void)printf("tsetctset "); break; case TSETCRSET: (void)printf("tsetcrset "); } switch (m & RSETCLK) { case RSETCOFF: (void)printf("rsetcoff "); break; case RSETCRBRG: (void)printf("rsetcrbrg "); break; case RSETCTBRG: (void)printf("rsetctbrg "); break; case RSETCTSET: (void)printf("rsetctset "); break; case RSETCRSET: (void)printf("rsetcrset "); } (void) printf("\n"); } } static void pramodes(void) /* print all modes, -a option */ { int m; m = cb.c_cflag; if (term & ASYNC) { if ((term & TERMIOS) && cfgetispeed(&cb) != 0 && cfgetispeed(&cb) != cfgetospeed(&cb)) { prspeed("ispeed ", cfgetispeed(&cb)); prspeed("ospeed ", cfgetospeed(&cb)); } else prspeed("speed ", cfgetospeed(&cb)); if (!(term & TERMIOS)) (void) printf(gettext("line = %d; "), ocb.c_line); (void) printf("\n"); if (term & WINDOW) { (void) printf(gettext("rows = %d; columns = %d;"), winsize.ws_row, winsize.ws_col); (void) printf(gettext( " ypixels = %d; xpixels = %d;\n"), winsize.ws_ypixel, winsize.ws_xpixel); } #ifdef EUC if ((term & CSIW) && kcswp.locale_name[0]) { (void) printf("csdata %s\n", kcswp.locale_name); } else { (void) printf("csdata ?\n"); } /* * If kwp.eucw[0] is zero, it means the current codeset type * in the ldterm is not EUC. */ if ((term & EUCW) && kwp.eucw[0]) { (void) printf("eucw %d:%d:%d:%d, ", kwp.eucw[0], kwp.eucw[1], kwp.eucw[2], kwp.eucw[3]); (void) printf("scrw %d:%d:%d:%d\n", kwp.scrw[0], kwp.scrw[1], kwp.scrw[2], kwp.scrw[3]); } else (void) printf("eucw ?, scrw ?\n"); #endif /* EUC */ if ((cb.c_lflag&ICANON) == 0) (void) printf(gettext("min = %d; time = %d;\n"), cb.c_cc[VMIN], cb.c_cc[VTIME]); pit(cb.c_cc[VINTR], "intr", "; "); pit(cb.c_cc[VQUIT], "quit", "; "); pit(cb.c_cc[VERASE], "erase", "; "); if (term & TERMIOS) pit(cb.c_cc[VERASE2], "erase2", "; "); pit(cb.c_cc[VKILL], "kill", ";\n"); pit(cb.c_cc[VEOF], "eof", "; "); pit(cb.c_cc[VEOL], "eol", "; "); pit(cb.c_cc[VEOL2], "eol2", "; "); pit(cb.c_cc[VSWTCH], "swtch", ";\n"); if (term & TERMIOS) { pit(cb.c_cc[VSTART], "start", "; "); pit(cb.c_cc[VSTOP], "stop", "; "); pit(cb.c_cc[VSUSP], "susp", "; "); pit(cb.c_cc[VDSUSP], "dsusp", ";\n"); pit(cb.c_cc[VREPRINT], "rprnt", "; "); pit(cb.c_cc[VDISCARD], "flush", "; "); pit(cb.c_cc[VWERASE], "werase", "; "); pit(cb.c_cc[VLNEXT], "lnext", ";\n"); pit(cb.c_cc[VSTATUS], "status", ";\n"); } } else pit((unsigned)stio.tab, "ctab", "\n"); m = cb.c_cflag; (void) printf("-parenb "+((m&PARENB) != 0)); (void) printf("-parodd "+((m&PARODD) != 0)); (void) printf("cs%c ", '5'+(m&CSIZE)/CS6); (void) printf("-cstopb "+((m&CSTOPB) != 0)); (void) printf("-hupcl "+((m&HUPCL) != 0)); (void) printf("-cread "+((m&CREAD) != 0)); (void) printf("-clocal "+((m&CLOCAL) != 0)); (void) printf("-loblk "+((m&LOBLK) != 0)); (void) printf("-crtscts "+((m&CRTSCTS) != 0)); (void) printf("-crtsxoff "+((m&CRTSXOFF) != 0)); if (term & TERMIOS) (void) printf("-parext "+((m&PAREXT) != 0)); (void) printf("\n"); m = cb.c_iflag; (void) printf("-ignbrk "+((m&IGNBRK) != 0)); (void) printf("-brkint "+((m&BRKINT) != 0)); (void) printf("-ignpar "+((m&IGNPAR) != 0)); (void) printf("-parmrk "+((m&PARMRK) != 0)); (void) printf("-inpck "+((m&INPCK) != 0)); (void) printf("-istrip "+((m&ISTRIP) != 0)); (void) printf("-inlcr "+((m&INLCR) != 0)); (void) printf("-igncr "+((m&IGNCR) != 0)); (void) printf("-icrnl "+((m&ICRNL) != 0)); (void) printf("-iuclc "+((m&IUCLC) != 0)); (void) printf("\n"); (void) printf("-ixon "+((m&IXON) != 0)); (void) printf("-ixany "+((m&IXANY) != 0)); (void) printf("-ixoff "+((m&IXOFF) != 0)); if (term & TERMIOS) (void) printf("-imaxbel "+((m&IMAXBEL) != 0)); (void) printf("\n"); m = cb.c_lflag; (void) printf("-isig "+((m&ISIG) != 0)); (void) printf("-icanon "+((m&ICANON) != 0)); (void) printf("-xcase "+((m&XCASE) != 0)); (void) printf("-echo "+((m&ECHO) != 0)); (void) printf("-echoe "+((m&ECHOE) != 0)); (void) printf("-echok "+((m&ECHOK) != 0)); (void) printf("-echonl "+((m&ECHONL) != 0)); (void) printf("-noflsh "+((m&NOFLSH) != 0)); if (term & TERMIOS) { (void) printf("\n"); (void) printf("-tostop "+((m&TOSTOP) != 0)); (void) printf("-echoctl "+((m&ECHOCTL) != 0)); (void) printf("-echoprt "+((m&ECHOPRT) != 0)); (void) printf("-echoke "+((m&ECHOKE) != 0)); (void) printf("-defecho "+((m&DEFECHO) != 0)); (void) printf("-flusho "+((m&FLUSHO) != 0)); (void) printf("-pendin "+((m&PENDIN) != 0)); (void) printf("-iexten "+((m&IEXTEN) != 0)); } if (!(term & ASYNC)) { (void) printf("-stflush "+((m&STFLUSH) != 0)); (void) printf("-stwrap "+((m&STWRAP) != 0)); (void) printf("-stappl "+((m&STAPPL) != 0)); } (void) printf("\n"); m = cb.c_oflag; (void) printf("-opost "+((m&OPOST) != 0)); (void) printf("-olcuc "+((m&OLCUC) != 0)); (void) printf("-onlcr "+((m&ONLCR) != 0)); (void) printf("-ocrnl "+((m&OCRNL) != 0)); (void) printf("-onocr "+((m&ONOCR) != 0)); (void) printf("-onlret "+((m&ONLRET) != 0)); (void) printf("-ofill "+((m&OFILL) != 0)); (void) printf("-ofdel "+((m&OFDEL) != 0)); delay((m&CRDLY)/CR1, "cr"); delay((m&NLDLY)/NL1, "nl"); delay((m&TABDLY)/TAB1, "tab"); delay((m&BSDLY)/BS1, "bs"); delay((m&VTDLY)/VT1, "vt"); delay((m&FFDLY)/FF1, "ff"); (void) printf("\n"); if (term & FLOW) { m = termiox.x_hflag; (void) printf("-rtsxoff "+((m&RTSXOFF) != 0)); (void) printf("-ctsxon "+((m&CTSXON) != 0)); (void) printf("-dtrxoff "+((m&DTRXOFF) != 0)); (void) printf("-cdxon "+((m&CDXON) != 0)); (void) printf("-isxoff "+((m&ISXOFF) != 0)); m = termiox.x_cflag; switch (m & XMTCLK) { case XCIBRG: (void)printf("xcibrg "); break; case XCTSET: (void)printf("xctset "); break; case XCRSET: (void)printf("xcrset "); } switch (m & RCVCLK) { case RCIBRG: (void)printf("rcibrg "); break; case RCTSET: (void)printf("rctset "); break; case RCRSET: (void)printf("rcrset "); } switch (m & TSETCLK) { case TSETCOFF: (void)printf("tsetcoff "); break; case TSETCRBRG: (void)printf("tsetcrbrg "); break; case TSETCTBRG: (void)printf("tsetctbrg "); break; case TSETCTSET: (void)printf("tsetctset "); break; case TSETCRSET: (void)printf("tsetcrset "); } switch (m & RSETCLK) { case RSETCOFF: (void)printf("rsetcoff "); break; case RSETCRBRG: (void)printf("rsetcrbrg "); break; case RSETCTBRG: (void)printf("rsetctbrg "); break; case RSETCTSET: (void)printf("rsetctset "); break; case RSETCRSET: (void)printf("rsetcrset "); } (void) printf("\n"); } } /* print function for prmodes() and pramodes() */ void pit(unsigned char what, char *itsname, char *sep) { pitt++; (void) printf("%s", itsname); if (((term & TERMIOS) && what == _POSIX_VDISABLE) || (!(term & TERMIOS) && what == 0200)) { (void) printf(" = %s", sep); return; } (void) printf(" = "); if (what & 0200 && !isprint(what)) { (void) printf("-"); what &= ~ 0200; } if (what == 0177) { (void) printf("^?%s", sep); return; } else if (what < ' ') { (void) printf("^"); what += '`'; if (what > 'z') what -= 'a' -'A'; } (void) printf("%c%s", what, sep); } void delay(int m, char *s) { if (m) (void) printf("%s%d ", s, m); } void prspeed(char *c, int scode) { int sval = -1; int i; for (i = 0; speeds[i].string; i++) { if (speeds[i].code == scode) { sval = speeds[i].value; break; } } (void) printf("%s%d baud; ", c, sval); } /* print current settings for use with */ static void prencode(void) /* another stty cmd, used for -g option */ { int i, last; /* * Although there are only 16 control chars defined as of April 1995, * prencode() and encode() will not have to be changed if up to MAX_CC * control chars are defined in the future. A maximum of MAX_CC rather * than NCCS control chars are printed because the last control slot * is unused. "stty -g" prints out a total of NUM_FIELDS fields * (NUM_MODES modes + MAX_CC control chars). First print the input, * output, control, and line discipline modes. */ (void) printf("%x:%x:%x:%x", cb.c_iflag, cb.c_oflag, cb.c_cflag, cb.c_lflag); /* Print the control character fields. */ if (term & TERMIOS) last = MAX_CC; else last = NCC; #ifdef EUC if (term & CSIW) { for (i = 0; i < MAX_CC; i++) (void) printf(":%x", (i >= last) ? 0 : cb.c_cc[i]); /* * Print out ldterm_cs_data_user_t data fields for * PSARC/1999/140 TCR2. This change introduces additional * 44 fields that come from the ldterm_cs_data_user_t data * structure. */ (void) printf(":%x:%x:%x:", kcswp.version, kcswp.codeset_type, kcswp.csinfo_num); if (*kcswp.locale_name == '\0') { (void) printf("00"); } else { for (i = 0; i < MAXNAMELEN && kcswp.locale_name[i] != '\0'; i++) (void) printf("%02x", kcswp.locale_name[i]); } for (i = 0; i < LDTERM_CS_MAX_CODESETS; i++) (void) printf(":%x:%x:%x:%x", kcswp.eucpc_data[i].byte_length, kcswp.eucpc_data[i].screen_width, kcswp.eucpc_data[i].msb_start, kcswp.eucpc_data[i].msb_end); } else { #endif /* EUC */ for (i = 0; i < last; i++) (void) printf(":%x", cb.c_cc[i]); #ifdef EUC } #endif /* EUC */ (void) printf("\n"); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 1999-2002 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T * All Rights Reserved * */ #ifndef _STTY_H #define _STTY_H #ifdef __cplusplus extern "C" { #endif /* Stty flags. */ #define ASYNC 1 #define FLOW 2 #define WINDOW 4 #define TERMIOS 8 #ifdef EUC #define EUCW 16 #define CSIW 32 #endif /* EUC */ /* Path to the locale-specific ldterm.dat file. */ #ifdef EUC #define _LDTERM_DAT_PATH "/usr/lib/locale/%s/LC_CTYPE/ldterm.dat" #endif /* EUC */ #define MAX_CC NCCS-1 /* max number of ctrl char fields printed by stty -g */ #define NUM_MODES 4 /* number of modes printed by stty -g */ #define NUM_FIELDS NUM_MODES+MAX_CC /* num modes + ctrl char fields (stty -g) */ struct speeds { const char *string; /* Speed in string form, e.g. "9600" */ int code; /* speed_t code for speed, e.g. B9600 */ int value; /* Integer value of speed, e.g. 9600 */ }; struct mds { const char *string; long set; long reset; }; extern const struct speeds speeds[]; extern const struct mds lmodes[]; extern const struct mds nlmodes[]; extern const struct mds cmodes[]; extern const struct mds ncmodes[]; extern const struct mds imodes[]; extern const struct mds nimodes[]; extern const struct mds omodes[]; extern const struct mds hmodes[]; extern const struct mds clkmodes[]; #ifdef __cplusplus } #endif #endif /* _STTY_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #include #include #include #include #include #include #include #include #include #include #include #include "tmstruct.h" #include "tmextern.h" #include "ttymon.h" static int nflg = 0; /* -n seen */ static int iflg = 0; /* -i seen */ static int fflg = 0; /* -f seen */ static int lflg = 0; /* -l seen */ static void usage(void); static void check_ref(void); static void add_entry(struct Gdef *) __NORETURN; static void remove_entry(char *); static int copy_file(FILE *, FILE *, int, int); static int verify(char *, int); static FILE *open_temp(char *); /* * sttydefs - add, remove or check entries in /etc/ttydefs * * Usage: sttydefs -a ttylabel [-n nextlabel] [-i initail-flags] * [-f final-flags] [-b] * sttydefs -r ttylabel * sttydefs -l [ttylabel] * */ int main(int argc, char *argv[]) { int c; /* option letter */ int errflg = 0; /* error indicator */ int aflg = 0; /* -a seen */ int bflg = 0; /* -b seen */ int ret; const char *argtmp; char *nextlabel; struct Gdef ttydef, *ptr; if (argc == 1) usage(); /* Initialize ttydef structure */ memset(&ttydef, 0, sizeof (ttydef)); ptr = &ttydef; while ((c = getopt(argc, argv, "a:n:i:f:br:l")) != -1) { switch (c) { case 'a': aflg = TRUE; ptr->g_id = optarg; break; case 'n': nflg = TRUE; ptr->g_nextid = optarg; break; case 'i': iflg = TRUE; ptr->g_iflags = optarg; break; case 'f': fflg = TRUE; ptr->g_fflags = optarg; break; case 'b': bflg = TRUE; ptr->g_autobaud |= A_FLAG; break; case 'r': if ((argc > 3) || (optind < argc)) usage(); remove_entry(optarg); break; case 'l': lflg = TRUE; if (argc > 3) usage(); if ((ret = check_version(TTYDEFS_VERS, TTYDEFS)) != 0) { if (ret != 2) { (void) fprintf(stderr, "%s version number is incorrect " "or missing.\n", TTYDEFS); exit(1); } (void) fprintf(stderr, "sttydefs: can't open %s.\n", TTYDEFS); exit(1); } if (argv[optind] == NULL) { read_ttydefs(NULL, TRUE); printf("\n"); check_ref(); } else { if (argc == 3) { /* -l ttylabel */ if (verify(argv[optind], 0) != 0) { errflg++; break; } argtmp = argv[optind]; } else { /* -lttylabel */ argtmp = argv[optind] + 2; } read_ttydefs(argtmp, TRUE); if (Ndefs == 0) { (void) fprintf(stderr, "ttylabel <%s> not found.\n", argtmp); exit(1); } nextlabel = Gdef[--Ndefs].g_nextid; Ndefs = 0; read_ttydefs(nextlabel, FALSE); if (Ndefs == 0) { (void) printf("\nWarning -- nextlabel " "<%s> of <%s> does not reference " "any existing ttylabel.\n", nextlabel, argtmp); } } exit(0); case '?': errflg++; break; } /* end switch */ if (errflg) usage(); } /* end while */ if (optind < argc) usage(); if (aflg) { add_entry(ptr); /* never return */ } if ((iflg) || (fflg) || (bflg) || (nflg)) usage(); return (0); } /* * verify - to check if arg is valid * - i.e. arg cannot start with '-' and * arg must not longer than maxarglen * - return 0 if ok. Otherwise return -1 */ static int verify(char *arg, int maxarglen) { if (*arg == '-') { (void) fprintf(stderr, "Invalid argument -- %s.\n", arg); return (-1); } if ((maxarglen) && ((int)strlen(arg) > maxarglen)) { arg[maxarglen] = '\0'; (void) fprintf(stderr, "string too long, truncated to %s.\n", arg); return (-1); } return (0); } /* * usage - print out a usage message */ static void usage(void) { (void) fprintf(stderr, "Usage:\tsttydefs -a ttylabel [-n nextlabel] " "[-i initial-flags]\n\t\t [-f final-flags] [-b]\n"); (void) fprintf(stderr, "\tsttydefs -r ttylabel\n"); (void) fprintf(stderr, "\tsttydefs -l [ttylabel]\n"); exit(2); } /* * add_entry - add an entry to /etc/ttydefs */ static void add_entry(struct Gdef *ttydef) { FILE *fp; int errflg = 0; char tbuf[BUFSIZ], *tp; int add_version = FALSE; if (getuid()) { (void) fprintf(stderr, "User not privileged for operation.\n"); exit(1); } tp = tbuf; *tp = '\0'; if ((fp = fopen(TTYDEFS, "r")) != NULL) { if (check_version(TTYDEFS_VERS, TTYDEFS) != 0) { (void) fprintf(stderr, "%s version number is incorrect or missing.\n", TTYDEFS); exit(1); } if (find_label(fp, ttydef->g_id)) { (void) fclose(fp); (void) fprintf(stderr, "Invalid request -- ttylabel <%s> already " "exists.\n", ttydef->g_id); exit(1); } (void) fclose(fp); } else { add_version = TRUE; } if ((fp = fopen(TTYDEFS, "a+")) == NULL) { (void) fprintf(stderr, "Could not open \"%s\": %s", TTYDEFS, strerror(errno)); exit(1); } if (add_version) { (void) fprintf(fp, "# VERSION=%d\n", TTYDEFS_VERS); } /* if optional fields are not provided, set to default */ if (!iflg) ttydef->g_iflags = DEFAULT.g_iflags; else if (check_flags(ttydef->g_iflags) != 0) errflg++; if (!fflg) ttydef->g_fflags = DEFAULT.g_fflags; else if (check_flags(ttydef->g_fflags) != 0) errflg++; if (errflg) exit(1); if (!nflg) ttydef->g_nextid = ttydef->g_id; if (ttydef->g_autobaud & A_FLAG) { (void) fprintf(fp, "%s:%s:%s:A:%s\n", ttydef->g_id, ttydef->g_iflags, ttydef->g_fflags, ttydef->g_nextid); } else { (void) fprintf(fp, "%s:%s:%s::%s\n", ttydef->g_id, ttydef->g_iflags, ttydef->g_fflags, ttydef->g_nextid); } (void) fclose(fp); exit(0); } static void remove_entry(char *ttylabel) { FILE *tfp; /* file pointer for temp file */ int line; /* line number entry is on */ FILE *fp; /* scratch file pointer */ char *tname = "/etc/.ttydefs"; if (getuid()) { (void) fprintf(stderr, "User not privileged for operation.\n"); exit(1); } fp = fopen(TTYDEFS, "r"); if (fp == NULL) { (void) fprintf(stderr, "Could not open \"%s\": %s", TTYDEFS, strerror(errno)); exit(1); } if (check_version(TTYDEFS_VERS, TTYDEFS) != 0) { (void) fprintf(stderr, "%s version number is incorrect or missing.\n", TTYDEFS); exit(1); } if ((line = find_label(fp, ttylabel)) == 0) { (void) fprintf(stderr, "Invalid request, ttylabel <%s> does not exist.\n", ttylabel); exit(1); } tfp = open_temp(tname); if (line != 1) if (copy_file(fp, tfp, 1, line - 1)) { (void) fprintf(stderr, "Error accessing temp file.\n"); exit(1); } if (copy_file(fp, tfp, line + 1, -1)) { (void) fprintf(stderr, "Error accessing temp file.\n"); exit(1); } (void) fclose(fp); if (fclose(tfp) == EOF) { (void) unlink(tname); (void) fprintf(stderr, "Error closing temp file.\n"); exit(1); } (void) unlink(TTYDEFS); if (rename(tname, TTYDEFS) != 0) { perror("Rename failed"); (void) unlink(tname); exit(1); } exit(0); } /* * open_temp - open up a temp file * * args: tname - temp file name */ static FILE * open_temp(char *tname) { FILE *fp; /* fp associated with tname */ struct sigaction sigact; /* for signal handling */ sigact.sa_flags = 0; sigact.sa_handler = SIG_IGN; (void) sigemptyset(&sigact.sa_mask); (void) sigaddset(&sigact.sa_mask, SIGHUP); (void) sigaddset(&sigact.sa_mask, SIGINT); (void) sigaddset(&sigact.sa_mask, SIGQUIT); (void) sigaction(SIGHUP, &sigact, NULL); (void) sigaction(SIGINT, &sigact, NULL); (void) sigaction(SIGQUIT, &sigact, NULL); (void) umask(0333); if (access(tname, 0) != -1) { (void) fprintf(stderr, "tempfile busy; try again later.\n"); exit(1); } fp = fopen(tname, "w"); if (fp == NULL) { perror("Cannot create tempfile"); exit(1); } return (fp); } /* * copy_file - copy information from one file to another, return 0 on * success, -1 on failure * * args: fp - source file's file pointer * tfp - destination file's file pointer * start - starting line number * finish - ending line number (-1 indicates entire file) */ static int copy_file(FILE *fp, FILE *tfp, int start, int finish) { int i; /* loop variable */ char dummy[BUFSIZ]; /* scratch buffer */ /* * always start from the beginning because line numbers are absolute */ rewind(fp); /* * get to the starting point of interest */ if (start != 1) { for (i = 1; i < start; i++) if (!fgets(dummy, BUFSIZ, fp)) return (-1); } /* * copy as much as was requested */ if (finish != -1) { for (i = start; i <= finish; i++) { if (!fgets(dummy, BUFSIZ, fp)) return (-1); if (fputs(dummy, tfp) == EOF) return (-1); } } else { for (;;) { if (fgets(dummy, BUFSIZ, fp) == NULL) { if (feof(fp)) break; else return (-1); } if (fputs(dummy, tfp) == EOF) return (-1); } } return (0); } /* * check_ref - to check if nextlabel are referencing * existing ttylabel */ static void check_ref(void) { int i; struct Gdef *np; np = &Gdef[0]; for (i = 0; i < Ndefs; i++, np++) { if (find_def(np->g_nextid) == NULL) { (void) printf("Warning -- nextlabel <%s> of <%s> " "does not reference any existing ttylabel.\n", np->g_nextid, np->g_id); } } } /* * log - print a message to stdout */ void log(const char *msg, ...) { va_list ap; if (lflg) { va_start(ap, msg); (void) vprintf(msg, ap); va_end(ap); (void) printf("\n"); } else { va_start(ap, msg); (void) vfprintf(stderr, msg, ap); va_end(ap); (void) fprintf(stderr, "\n"); } } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 1999-2002 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright (c) 2014, Joyent, Inc. All rights reserved. */ /* * Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T * All Rights Reserved * */ #include #include #include #include #include #include #include #include #include #ifdef EUC #include #include #include #include #include #include #include #include #endif /* EUC */ #include "stty.h" #include #include static char *s_arg; /* s_arg: ptr to mode to be set */ static int match; #ifdef EUC static int parse_encoded(struct termios *, ldterm_cs_data_user_t *, int); #else static int parse_encoded(struct termios *); #endif /* EUC */ static int eq(const char *string); static int gct(char *cp, int term); /* set terminal modes for supplied options */ char * sttyparse(int argc, char *argv[], int term, struct termio *ocb, struct termios *cb, struct termiox *termiox, struct winsize *winsize #ifdef EUC /* */, eucwidth_t *wp, struct eucioc *kwp, ldterm_cs_data_user_t *cswp, ldterm_cs_data_user_t *kcswp #endif /* EUC */ /* */) { int i; while (--argc > 0) { s_arg = *++argv; match = 0; if (term & ASYNC) { if (eq("erase") && --argc) cb->c_cc[VERASE] = gct(*++argv, term); else if (eq("intr") && --argc) cb->c_cc[VINTR] = gct(*++argv, term); else if (eq("quit") && --argc) cb->c_cc[VQUIT] = gct(*++argv, term); else if (eq("eof") && --argc) cb->c_cc[VEOF] = gct(*++argv, term); else if (eq("min") && --argc) { if (isdigit((unsigned char)argv[1][0])) cb->c_cc[VMIN] = atoi(*++argv); else cb->c_cc[VMIN] = gct(*++argv, term); } else if (eq("eol") && --argc) cb->c_cc[VEOL] = gct(*++argv, term); else if (eq("eol2") && --argc) cb->c_cc[VEOL2] = gct(*++argv, term); else if (eq("time") && --argc) { if (isdigit((unsigned char)argv[1][0])) cb->c_cc[VTIME] = atoi(*++argv); else cb->c_cc[VTIME] = gct(*++argv, term); } else if (eq("kill") && --argc) cb->c_cc[VKILL] = gct(*++argv, term); else if (eq("swtch") && --argc) cb->c_cc[VSWTCH] = gct(*++argv, term); if (match) continue; if (term & TERMIOS) { if (eq("start") && --argc) cb->c_cc[VSTART] = gct(*++argv, term); else if (eq("stop") && --argc) cb->c_cc[VSTOP] = gct(*++argv, term); else if (eq("susp") && --argc) cb->c_cc[VSUSP] = gct(*++argv, term); else if (eq("dsusp") && --argc) cb->c_cc[VDSUSP] = gct(*++argv, term); else if (eq("rprnt") && --argc) cb->c_cc[VREPRINT] = gct(*++argv, term); else if (eq("reprint") && --argc) cb->c_cc[VREPRINT] = gct(*++argv, term); else if (eq("discard") && --argc) cb->c_cc[VDISCARD] = gct(*++argv, term); else if (eq("flush") && --argc) cb->c_cc[VDISCARD] = gct(*++argv, term); else if (eq("werase") && --argc) cb->c_cc[VWERASE] = gct(*++argv, term); else if (eq("lnext") && --argc) cb->c_cc[VLNEXT] = gct(*++argv, term); else if (eq("status") && --argc) cb->c_cc[VSTATUS] = gct(*++argv, term); else if (eq("erase2") && --argc) cb->c_cc[VERASE2] = gct(*++argv, term); } if (match) continue; if (eq("ek")) { cb->c_cc[VERASE] = CERASE; if (term & TERMIOS) cb->c_cc[VERASE2] = CERASE2; cb->c_cc[VKILL] = CKILL; } else if (eq("line") && !(term & TERMIOS) && --argc) { ocb->c_line = atoi(*++argv); continue; } else if (eq("raw")) { cb->c_cc[VMIN] = 1; cb->c_cc[VTIME] = 0; } else if (eq("-raw") | eq("cooked")) { cb->c_cc[VEOF] = CEOF; cb->c_cc[VEOL] = CNUL; } else if (eq("sane")) { cb->c_cc[VERASE] = CERASE; if (term & TERMIOS) cb->c_cc[VERASE2] = CERASE2; cb->c_cc[VKILL] = CKILL; cb->c_cc[VQUIT] = CQUIT; cb->c_cc[VINTR] = CINTR; cb->c_cc[VEOF] = CEOF; cb->c_cc[VEOL] = CNUL; cb->c_cc[VSTATUS] = CSTATUS; /* SWTCH purposely not set */ #ifdef EUC } else if (eq("defeucw")) { kwp->eucw[0] = '\001'; kwp->eucw[1] = (unsigned char)(wp->_eucw1 & 0177); kwp->eucw[2] = (unsigned char)(wp->_eucw2 & 0177); kwp->eucw[3] = (unsigned char)(wp->_eucw3 & 0177); kwp->scrw[0] = '\001'; kwp->scrw[1] = (unsigned char)(wp->_scrw1 & 0177); kwp->scrw[2] = (unsigned char)(wp->_scrw2 & 0177); kwp->scrw[3] = (unsigned char)(wp->_scrw3 & 0177); (void) memcpy((void *)kcswp, (const void *)cswp, sizeof (ldterm_cs_data_user_t)); #endif /* EUC */ } else if ((term & TERMIOS) && eq("ospeed") && --argc) { s_arg = *++argv; for (match = 0, i = 0; speeds[i].string; i++) { if (eq(speeds[i].string)) { (void) cfsetospeed(cb, speeds[i].code); break; } } if (!match) return (s_arg); continue; } else if ((term & TERMIOS) && eq("ispeed") && --argc) { s_arg = *++argv; for (match = 0, i = 0; speeds[i].string; i++) { if (eq(speeds[i].string)) { (void) cfsetispeed(cb, speeds[i].code); break; } } if (!match) return (s_arg); continue; } else { for (match = 0, i = 0; speeds[i].string; i++) { if (eq(speeds[i].string)) { (void) cfsetospeed(cb, speeds[i].code); (void) cfsetispeed(cb, speeds[i].code); break; } } } } if (!(term & ASYNC) && eq("ctab") && --argc) { cb->c_cc[7] = gct(*++argv, term); continue; } for (i = 0; imodes[i].string; i++) if (eq(imodes[i].string)) { cb->c_iflag &= ~imodes[i].reset; cb->c_iflag |= imodes[i].set; #ifdef EUC if (wp->_multibyte && (eq("-raw") || eq("cooked") || eq("sane"))) cb->c_iflag &= ~ISTRIP; #endif /* EUC */ } if (term & TERMIOS) { for (i = 0; nimodes[i].string; i++) if (eq(nimodes[i].string)) { cb->c_iflag &= ~nimodes[i].reset; cb->c_iflag |= nimodes[i].set; } } for (i = 0; omodes[i].string; i++) if (eq(omodes[i].string)) { cb->c_oflag &= ~omodes[i].reset; cb->c_oflag |= omodes[i].set; } if (!(term & ASYNC) && eq("sane")) { cb->c_oflag |= TAB3; continue; } for (i = 0; cmodes[i].string; i++) if (eq(cmodes[i].string)) { cb->c_cflag &= ~cmodes[i].reset; cb->c_cflag |= cmodes[i].set; #ifdef EUC if (wp->_multibyte && (eq("-raw") || eq("cooked") || eq("sane"))) { cb->c_cflag &= ~(CS7|PARENB); cb->c_cflag |= CS8; } #endif /* EUC */ } if (term & TERMIOS) for (i = 0; ncmodes[i].string; i++) if (eq(ncmodes[i].string)) { cb->c_cflag &= ~ncmodes[i].reset; cb->c_cflag |= ncmodes[i].set; } for (i = 0; lmodes[i].string; i++) if (eq(lmodes[i].string)) { cb->c_lflag &= ~lmodes[i].reset; cb->c_lflag |= lmodes[i].set; } if (term & TERMIOS) for (i = 0; nlmodes[i].string; i++) if (eq(nlmodes[i].string)) { cb->c_lflag &= ~nlmodes[i].reset; cb->c_lflag |= nlmodes[i].set; } if (term & FLOW) { for (i = 0; hmodes[i].string; i++) if (eq(hmodes[i].string)) { termiox->x_hflag &= ~hmodes[i].reset; termiox->x_hflag |= hmodes[i].set; } for (i = 0; clkmodes[i].string; i++) if (eq(clkmodes[i].string)) { termiox->x_cflag &= ~clkmodes[i].reset; termiox->x_cflag |= clkmodes[i].set; } } if (eq("rows") && --argc) winsize->ws_row = atoi(*++argv); else if ((eq("columns") || eq("cols")) && --argc) winsize->ws_col = atoi(*++argv); else if (eq("xpixels") && --argc) winsize->ws_xpixel = atoi(*++argv); else if (eq("ypixels") && --argc) winsize->ws_ypixel = atoi(*++argv); if (!match) { #ifdef EUC if (!parse_encoded(cb, kcswp, term)) { #else if (!parse_encoded(cb)) { #endif /* EUC */ return (s_arg); /* parsing failed */ } } } return ((char *)0); } static int eq(const char *string) { int i; if (!s_arg) return (0); i = 0; loop: if (s_arg[i] != string[i]) return (0); if (s_arg[i++] != '\0') goto loop; match++; return (1); } /* get pseudo control characters from terminal */ /* and convert to internal representation */ static int gct(char *cp, int term) { int c; c = *cp; if (c == '^') { c = *++cp; if (c == '?') c = 0177; /* map '^?' to 0177 */ else if (c == '-') { /* map '^-' to undefined */ c = (term & TERMIOS) ? _POSIX_VDISABLE : 0200; } else c &= 037; } else if (strcmp(cp, "undef") == 0) { /* map "undef" to undefined */ c = (term & TERMIOS) ? _POSIX_VDISABLE : 0200; } return (c); } /* get modes of tty device and fill in applicable structures */ int get_ttymode(int fd, struct termio *termio, struct termios *termios, struct stio *stermio, struct termiox *termiox, struct winsize *winsize #ifdef EUC /* */, struct eucioc *kwp, ldterm_cs_data_user_t *kcswp #endif /* EUC */ /* */) { int i; int term = 0; #ifdef EUC struct strioctl cmd; #endif /* EUC */ if (ioctl(fd, STGET, stermio) == -1) { term |= ASYNC; if (ioctl(fd, TCGETS, termios) == -1) { if (ioctl(fd, TCGETA, termio) == -1) return (-1); termios->c_lflag = termio->c_lflag; termios->c_oflag = termio->c_oflag; termios->c_iflag = termio->c_iflag; termios->c_cflag = termio->c_cflag; for (i = 0; i < NCC; i++) termios->c_cc[i] = termio->c_cc[i]; } else term |= TERMIOS; } else { termios->c_cc[7] = (unsigned)stermio->tab; termios->c_lflag = stermio->lmode; termios->c_oflag = stermio->omode; termios->c_iflag = stermio->imode; } if (ioctl(fd, TCGETX, termiox) == 0) term |= FLOW; if (ioctl(fd, TIOCGWINSZ, winsize) == 0) term |= WINDOW; #ifdef EUC cmd.ic_cmd = EUC_WGET; cmd.ic_timout = 0; cmd.ic_len = sizeof (struct eucioc); cmd.ic_dp = (char *)kwp; if (ioctl(fd, I_STR, &cmd) == 0) term |= EUCW; cmd.ic_cmd = CSDATA_GET; cmd.ic_timout = 0; cmd.ic_len = sizeof (ldterm_cs_data_user_t); cmd.ic_dp = (char *)kcswp; if (ioctl(fd, I_STR, &cmd) == 0) term |= CSIW; else (void) memset((void *)kcswp, 0, sizeof (ldterm_cs_data_user_t)); #endif /* EUC */ return (term); } /* set tty modes */ int set_ttymode(int fd, int term, struct termio *termio, struct termios *termios, struct stio *stermio, struct termiox *termiox, struct winsize *winsize, struct winsize *owinsize #ifdef EUC /* */, struct eucioc *kwp, ldterm_cs_data_user_t *kcswp, int invalid_ldterm_dat_file #endif /* EUC */ /* */) { int i; #ifdef EUC struct strioctl cmd; #endif /* EUC */ if (term & ASYNC) { if (term & TERMIOS) { if (ioctl(fd, TCSETSW, termios) == -1) return (-1); } else { termio->c_lflag = termios->c_lflag; termio->c_oflag = termios->c_oflag; termio->c_iflag = termios->c_iflag; termio->c_cflag = termios->c_cflag; for (i = 0; i < NCC; i++) termio->c_cc[i] = termios->c_cc[i]; if (ioctl(fd, TCSETAW, termio) == -1) return (-1); } } else { stermio->imode = termios->c_iflag; stermio->omode = termios->c_oflag; stermio->lmode = termios->c_lflag; stermio->tab = termios->c_cc[7]; if (ioctl(fd, STSET, stermio) == -1) return (-1); } if (term & FLOW) { if (ioctl(fd, TCSETXW, termiox) == -1) return (-1); } if ((owinsize->ws_col != winsize->ws_col || owinsize->ws_row != winsize->ws_row || owinsize->ws_xpixel != winsize->ws_xpixel || owinsize->ws_ypixel != winsize->ws_ypixel) && ioctl(0, TIOCSWINSZ, winsize) != 0) return (-1); #ifdef EUC /* * If the ldterm.dat file contains valid, non-EUC codeset info, * send downstream CSDATA_SET. Otherwise, try EUC_WSET. */ if (invalid_ldterm_dat_file) { (void) fprintf(stderr, gettext( "stty: can't set codeset width due to invalid ldterm.dat.\n")); return (-1); } else if ((term & CSIW) && kcswp->version) { cmd.ic_cmd = CSDATA_SET; cmd.ic_timout = 0; cmd.ic_len = sizeof (ldterm_cs_data_user_t); cmd.ic_dp = (char *)kcswp; if (ioctl(fd, I_STR, &cmd) != 0) { (void) fprintf(stderr, gettext( "stty: can't set codeset width.\n")); return (-1); } } else if (term & EUCW) { cmd.ic_cmd = EUC_WSET; cmd.ic_timout = 0; cmd.ic_len = sizeof (struct eucioc); cmd.ic_dp = (char *)kwp; if (ioctl(fd, I_STR, &cmd) != 0) { (void) fprintf(stderr, gettext( "stty: can't set EUC codeset width.\n")); return (-1); } } #endif /* EUC */ return (0); } static int parse_encoded(struct termios *cb #ifdef EUC /* */, ldterm_cs_data_user_t *kcswp, int term #endif /* EUC */ /* */) { unsigned long grab[NUM_FIELDS]; int last, i; #ifdef EUC long l; char s[3]; char *t; char *r; uchar_t *g; ldterm_cs_data_user_t ecswp; #endif /* EUC */ /* * Although there are only 16 control chars defined as of April 1995, * parse_encoded() and prencode() will not have to be changed if up to * MAX_CC control chars are defined in the future. * Scan the fields of "stty -g" output into the grab array. * Set a total of NUM_FIELDS fields (NUM_MODES modes + MAX_CC * control chars). */ i = sscanf(s_arg, "%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx:" "%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx", &grab[0], &grab[1], &grab[2], &grab[3], &grab[4], &grab[5], &grab[6], &grab[7], &grab[8], &grab[9], &grab[10], &grab[11], &grab[12], &grab[13], &grab[14], &grab[15], &grab[16], &grab[17], &grab[18], &grab[19], &grab[20], &grab[21]); if (i < 12) return (0); cb->c_iflag = grab[0]; cb->c_oflag = grab[1]; cb->c_cflag = grab[2]; cb->c_lflag = grab[3]; last = i - NUM_MODES; for (i = 0; i < last; i++) cb->c_cc[i] = (unsigned char) grab[i+NUM_MODES]; #ifdef EUC /* This is to fulfill PSARC/1999/140 TCR2. */ if (term & CSIW) { r = strdup(s_arg); if (r == (char *)NULL) { (void) fprintf(stderr, gettext( "no more memory - try again later\n")); return (0); } t = strtok(r, ":"); for (i = 0; t != NULL && i < 22; i++) { t = strtok(NULL, ":"); } if (t == NULL) { free((void *)r); return (0); } ecswp.version = (uchar_t)strtol(t, (char **)NULL, 16); if (ecswp.version > LDTERM_DATA_VERSION || ecswp.version == 0) { free((void *)r); return (0); } if ((t = strtok(NULL, ":")) == NULL) { free((void *)r); return (0); } ecswp.codeset_type = (uchar_t)strtol(t, (char **)NULL, 16); if (ecswp.codeset_type < LDTERM_CS_TYPE_MIN || ecswp.codeset_type > LDTERM_CS_TYPE_MAX) { free((void *)r); return (0); } if ((t = strtok(NULL, ":")) == NULL) { free((void *)r); return (0); } ecswp.csinfo_num = (uchar_t)strtol(t, (char **)NULL, 16); if ((ecswp.codeset_type == LDTERM_CS_TYPE_EUC && ecswp.csinfo_num > 3) || (ecswp.codeset_type == LDTERM_CS_TYPE_PCCS && (ecswp.csinfo_num < 1 || ecswp.csinfo_num > 10))) { free((void *)r); return (0); } if ((t = strtok(NULL, ":")) == NULL) { free((void *)r); return (0); } s[2] = '\0'; for (i = 0; *t != 0 && i < MAXNAMELEN; i++) { if (*(t + 1) == '\0') { free((void *)r); return (0); } s[0] = *t++; s[1] = *t++; ecswp.locale_name[i] = (char)strtol(s, (char **)NULL, 16); } if (i >= MAXNAMELEN) { free((void *)r); return (0); } ecswp.locale_name[i] = '\0'; g = (uchar_t *)ecswp.eucpc_data; for (i = 0; i < (LDTERM_CS_MAX_CODESETS * 4); i++) { if ((t = strtok(NULL, ":")) == NULL) { free((void *)r); return (0); } l = strtol(t, (char **)NULL, 16); if (l < 0 || l > 255) { free((void *)r); return (0); } *g++ = (uchar_t)l; } /* We got the 'ecswp' all filled up now; let's copy. */ (void) memcpy((void *)kcswp, (const void *)&ecswp, sizeof (ldterm_cs_data_user_t)); } #endif /* EUC */ return (1); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T * All Rights Reserved * */ #include #include #include #include #include #include "stty.h" const struct speeds speeds[] = { "0", B0, 0, "50", B50, 50, "75", B75, 75, "110", B110, 110, "134", B134, 134, "134.5", B134, 134, "150", B150, 150, "200", B200, 200, "300", B300, 300, "600", B600, 600, "1200", B1200, 1200, "1800", B1800, 1800, "2400", B2400, 2400, "4800", B4800, 4800, "9600", B9600, 9600, "19200", B19200, 19200, "19.2", B19200, 19200, "38400", B38400, 38400, "38.4", B38400, 38400, "57600", B57600, 57600, "57.6", B57600, 57600, "76800", B76800, 76800, "76.8", B76800, 76800, "115200", B115200, 115200, "115.2", B115200, 115200, "153600", B153600, 153600, "153.6", B153600, 153600, "230400", B230400, 230400, "230.4", B230400, 230400, "307200", B307200, 307200, "307.2", B307200, 307200, "460800", B460800, 460800, "460.8", B460800, 460800, "921600", B921600, 921600, "921.6", B921600, 921600, "1000000", B1000000, 1000000, "1152000", B1152000, 1152000, "1500000", B1500000, 1500000, "2000000", B2000000, 2000000, "2500000", B2500000, 2500000, "3000000", B3000000, 3000000, "3500000", B3500000, 3500000, "4000000", B4000000, 4000000, 0, }; const struct mds cmodes[] = { "-parity", CS8, PARENB|CSIZE, "-evenp", CS8, PARENB|CSIZE, "-oddp", CS8, PARENB|PARODD|CSIZE, "parity", PARENB|CS7, PARODD|CSIZE, "evenp", PARENB|CS7, PARODD|CSIZE, "oddp", PARENB|PARODD|CS7, CSIZE, "parenb", PARENB, 0, "-parenb", 0, PARENB, "parodd", PARODD, 0, "-parodd", 0, PARODD, "cs8", CS8, CSIZE, "cs7", CS7, CSIZE, "cs6", CS6, CSIZE, "cs5", CS5, CSIZE, "cstopb", CSTOPB, 0, "-cstopb", 0, CSTOPB, "hupcl", HUPCL, 0, "hup", HUPCL, 0, "-hupcl", 0, HUPCL, "-hup", 0, HUPCL, "clocal", CLOCAL, 0, "-clocal", 0, CLOCAL, "loblk", LOBLK, 0, "-loblk", 0, LOBLK, "cread", CREAD, 0, "-cread", 0, CREAD, "crtscts", (long)CRTSCTS, 0, "-crtscts", 0, (long)CRTSCTS, "crtsxoff", CRTSXOFF, 0, "-crtsxoff", 0, CRTSXOFF, "raw", CS8, (CSIZE|PARENB), "-raw", (CS7|PARENB), CSIZE, "cooked", (CS7|PARENB), CSIZE, "sane", (CS7|PARENB|CREAD), (CSIZE|PARODD|CLOCAL), 0 }; const struct mds ncmodes[] = { "parext", PAREXT, 0, "-parext", 0, PAREXT, "markp", (PARENB|PARODD|CS7|PAREXT), CSIZE, "-markp", CS8, (PARENB|PARODD|CSIZE|PAREXT), "spacep", (PARENB|CS7|PAREXT), PARODD|CSIZE, "-spacep", CS8, (PARENB|CSIZE|PAREXT), 0 }; const struct mds imodes[] = { "ignbrk", IGNBRK, 0, "-ignbrk", 0, IGNBRK, "brkint", BRKINT, 0, "-brkint", 0, BRKINT, "ignpar", IGNPAR, 0, "-ignpar", 0, IGNPAR, "parmrk", PARMRK, 0, "-parmrk", 0, PARMRK, "inpck", INPCK, 0, "-inpck", 0, INPCK, "istrip", ISTRIP, 0, "-istrip", 0, ISTRIP, "inlcr", INLCR, 0, "-inlcr", 0, INLCR, "igncr", IGNCR, 0, "-igncr", 0, IGNCR, "icrnl", ICRNL, 0, "-icrnl", 0, ICRNL, #ifdef XPG4 "-nl", 0, (ICRNL|INLCR|IGNCR), "nl", ICRNL, 0, #else "-nl", ICRNL, (INLCR|IGNCR), "nl", 0, ICRNL, #endif "iuclc", IUCLC, 0, "-iuclc", 0, IUCLC, "lcase", IUCLC, 0, "-lcase", 0, IUCLC, "LCASE", IUCLC, 0, "-LCASE", 0, IUCLC, "ixon", IXON, 0, "-ixon", 0, IXON, "ixany", IXANY, 0, "-ixany", 0, IXANY, "ixoff", IXOFF, 0, "-ixoff", 0, IXOFF, "raw", 0, -1, "-raw", (BRKINT|IGNPAR|ISTRIP|ICRNL|IXON), 0, "cooked", (BRKINT|IGNPAR|ISTRIP|ICRNL|IXON), 0, "sane", (BRKINT|IGNPAR|ISTRIP|ICRNL|IXON|IMAXBEL), (IGNBRK|PARMRK|INPCK|INLCR|IGNCR|IUCLC|IXOFF|IXANY), 0 }; const struct mds nimodes[] = { "imaxbel", IMAXBEL, 0, "-imaxbel", 0, IMAXBEL, 0 }; const struct mds lmodes[] = { "isig", ISIG, 0, "-isig", 0, ISIG, "icanon", ICANON, 0, "-icanon", 0, ICANON, "xcase", XCASE, 0, "-xcase", 0, XCASE, "lcase", XCASE, 0, "-lcase", 0, XCASE, "LCASE", XCASE, 0, "-LCASE", 0, XCASE, "echo", ECHO, 0, "-echo", 0, ECHO, "echoe", ECHOE, 0, "-echoe", 0, ECHOE, "echok", ECHOK, 0, "-echok", 0, ECHOK, "lfkc", ECHOK, 0, "-lfkc", 0, ECHOK, "echonl", ECHONL, 0, "-echonl", 0, ECHONL, "noflsh", NOFLSH, 0, "-noflsh", 0, NOFLSH, "raw", 0, (ISIG|ICANON|XCASE), "-raw", (ISIG|ICANON), 0, "cooked", (ISIG|ICANON), 0, "sane", (ISIG|ICANON|IEXTEN|ECHO|ECHOK|ECHOE|ECHOKE|ECHOCTL), (XCASE|ECHONL|NOFLSH|STFLUSH|STWRAP|STAPPL), "stflush", STFLUSH, 0, "-stflush", 0, STFLUSH, "stwrap", STWRAP, 0, "-stwrap", 0, STWRAP, "stappl", STAPPL, 0, "-stappl", 0, STAPPL, 0, }; const struct mds nlmodes[] = { "tostop", TOSTOP, 0, "-tostop", 0, TOSTOP, "echoctl", ECHOCTL, 0, "-echoctl", 0, ECHOCTL, "echoprt", ECHOPRT, 0, "-echoprt", 0, ECHOPRT, "echoke", ECHOKE, 0, "-echoke", 0, ECHOKE, "defecho", DEFECHO, 0, "-defecho", 0, DEFECHO, "flusho", FLUSHO, 0, "-flusho", 0, FLUSHO, "pendin", PENDIN, 0, "-pendin", 0, PENDIN, "iexten", IEXTEN, 0, "-iexten", 0, IEXTEN, 0 }; const struct mds omodes[] = { "opost", OPOST, 0, "-opost", 0, OPOST, "olcuc", OLCUC, 0, "-olcuc", 0, OLCUC, "lcase", OLCUC, 0, "-lcase", 0, OLCUC, "LCASE", OLCUC, 0, "-LCASE", 0, OLCUC, "onlcr", ONLCR, 0, "-onlcr", 0, ONLCR, "-nl", ONLCR, (OCRNL|ONLRET), "nl", 0, ONLCR, "ocrnl", OCRNL, 0, "-ocrnl", 0, OCRNL, "onocr", ONOCR, 0, "-onocr", 0, ONOCR, "onlret", ONLRET, 0, "-onlret", 0, ONLRET, "fill", OFILL, OFDEL, "-fill", 0, OFILL|OFDEL, "nul-fill", OFILL, OFDEL, "del-fill", OFILL|OFDEL, 0, "ofill", OFILL, 0, "-ofill", 0, OFILL, "ofdel", OFDEL, 0, "-ofdel", 0, OFDEL, "cr0", CR0, CRDLY, "cr1", CR1, CRDLY, "cr2", CR2, CRDLY, "cr3", CR3, CRDLY, "tab0", TAB0, TABDLY, "tabs", TAB0, TABDLY, "tab1", TAB1, TABDLY, "tab2", TAB2, TABDLY, "tab3", TAB3, TABDLY, "-tabs", TAB3, TABDLY, "tab8", TAB3, TABDLY, "nl0", NL0, NLDLY, "nl1", NL1, NLDLY, "ff0", FF0, FFDLY, "ff1", FF1, FFDLY, "vt0", VT0, VTDLY, "vt1", VT1, VTDLY, "bs0", BS0, BSDLY, "bs1", BS1, BSDLY, "raw", 0, OPOST, "-raw", OPOST, 0, "cooked", OPOST, 0, "tty33", CR1, (CRDLY|TABDLY|NLDLY|FFDLY|VTDLY|BSDLY), "tn300", CR1, (CRDLY|TABDLY|NLDLY|FFDLY|VTDLY|BSDLY), "ti700", CR2, (CRDLY|TABDLY|NLDLY|FFDLY|VTDLY|BSDLY), "vt05", NL1, (CRDLY|TABDLY|NLDLY|FFDLY|VTDLY|BSDLY), "tek", FF1, (CRDLY|TABDLY|NLDLY|FFDLY|VTDLY|BSDLY), "tty37", (FF1|VT1|CR2|TAB1|NL1), (NLDLY|CRDLY|TABDLY|BSDLY|VTDLY|FFDLY), "sane", (OPOST|ONLCR), (OLCUC|OCRNL|ONOCR|ONLRET|OFILL|OFDEL| NLDLY|CRDLY|TABDLY|BSDLY|VTDLY|FFDLY), 0, }; const struct mds hmodes[] = { "-rtsxoff", 0, RTSXOFF, "rtsxoff", RTSXOFF, 0, "-ctsxon", 0, CTSXON, "ctsxon", CTSXON, 0, "-dtrxoff", 0, DTRXOFF, "dtrxoff", DTRXOFF, 0, "-cdxon", 0, CDXON, "cdxon", CDXON, 0, "-isxoff", 0, ISXOFF, "isxoff", ISXOFF, 0, 0, }; const struct mds clkmodes[] = { "xcibrg", XCIBRG, XMTCLK, "xctset", XCTSET, XMTCLK, "xcrset", XCRSET, XMTCLK, "rcibrg", RCIBRG, RCVCLK, "rctset", RCTSET, RCVCLK, "rcrset", RCRSET, RCVCLK, "tsetcoff", TSETCOFF, TSETCLK, "tsetcrbrg", TSETCRBRG, TSETCLK, "tsetctbrg", TSETCTBRG, TSETCLK, "tsetctset", TSETCTSET, TSETCLK, "tsetcrset", TSETCRSET, TSETCLK, "rsetcoff", RSETCOFF, RSETCLK, "rsetcrbrg", RSETCRBRG, RSETCLK, "rsetctbrg", RSETCTBRG, RSETCLK, "rsetctset", RSETCTSET, RSETCLK, "rsetcrset", RSETCRSET, RSETCLK, "async", XCIBRG|RCIBRG|TSETCOFF|RSETCOFF, XMTCLK|RCVCLK|TSETCLK|RSETCLK, 0, }; const char *not_supported[] = { "rtsxoff", "ctsxon", "dtrxoff", "cdxon", "isxoff", "xcibrg", "xctset", "scrset", "rcibrg", "rctset", "rcrset", "tsetcoff", "tsetcrbrg", "rsetcbrg", "rsetctbrg", "rsetctset", "rsetcrset", 0, }; /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #include #include #include #include #include #include #include #include #include #include #include "tmextern.h" #define NTRY 5 /* * At this time, we only recognize certain speeds. * This table can be expanded if new patterns are found */ static struct autobaud { char *a_speed; char *a_pattern; /* first byte is length */ } autob2400[] = { #ifdef i386 /* * These are the bit patterns returned on x86 boxes */ "110", "\1\000", #else "110", "\3\000\000\000", #endif "1200", "\2\346\200", "2400", "\1\15", "4800", "\1\371", "4800", "\1\362", "9600", "\1\377", 0, 0 }; /* * auto_termio - set termio to allow autobaud * - the line is set to raw mode, with VMIN = 5, VTIME = 1 * - baud rate is set to 2400 */ int auto_termio(int fd) { struct termio termio; struct termios termios; if (ioctl(fd, TCGETS, &termios) == -1) { if (ioctl(fd, TCGETA, &termio) == -1) { log("auto_termio: ioctl TCGETA failed, fd = %d: %s", fd, strerror(errno)); return (-1); } termio.c_iflag = 0; termio.c_cflag &= ~(CBAUD|CSIZE|PARENB); termio.c_cflag |= CREAD|HUPCL|(CS8&CSIZE)|(B2400&CBAUD); termio.c_lflag &= ~(ISIG|ICANON|ECHO|ECHOE|ECHOK); termio.c_oflag = 0; termio.c_cc[VMIN] = 5; termio.c_cc[VTIME] = 1; if (ioctl(fd, TCSETAF, &termio) == -1) { log("auto_termio: ioctl TCSETAF failed, fd = %d: %s", fd, strerror(errno)); return (-1); } } else { termios.c_iflag &= 0xffff0000; termios.c_cflag &= ~(CSIZE|PARENB); termios.c_cflag |= CREAD|HUPCL|(CS8&CSIZE); termios.c_lflag &= ~(ISIG|ICANON|ECHO|ECHOE|ECHOK); termios.c_oflag &= 0xffff0000; termios.c_cc[VMIN] = 5; termios.c_cc[VTIME] = 1; (void) cfsetospeed(&termios, B2400); if (ioctl(fd, TCSETSF, &termios) == -1) { log("auto_termio: ioctl TCSETSF failed, fd = %d: %s", fd, strerror(errno)); return (-1); } } return (0); } /* * autobaud - determine the baudrate by reading data at 2400 baud rate * - the program is anticipating * - the bit pattern is matched again an autobaud table * - if a match is found, the matched speed is returned * - otherwise, NULL is returned */ char * autobaud(int fd, int timeout) { int i, k, count; static char buf[5]; char *cp = buf; struct autobaud *tp; struct sigaction sigact; #ifdef DEBUG debug("in autobaud"); #endif (void) auto_termio(fd); sigact.sa_flags = 0; sigact.sa_handler = SIG_IGN; (void) sigemptyset(&sigact.sa_mask); (void) sigaction(SIGINT, &sigact, NULL); count = NTRY; while (count--) { if (timeout) { sigact.sa_flags = 0; sigact.sa_handler = timedout; (void) sigemptyset(&sigact.sa_mask); (void) sigaction(SIGALRM, &sigact, NULL); (void) alarm((unsigned)timeout); } cp = &buf[1]; if (peek_ptr != NULL) { k = peek_ptr->len; (void) strncpy(cp, peek_ptr->buf, k); peek_ptr = NULL; } else if ((k = read(fd, cp, 5)) < 0) { fatal("autobaud: read failed: %s", strerror(errno)); } if (timeout) (void) alarm((unsigned)0); buf[0] = (char)k; for (tp = autob2400; tp->a_speed; tp++) { for (i = 0; ; i++) { if (buf[i] != tp->a_pattern[i]) break; if (i == buf[0]) { return (tp->a_speed); } } } flush_input(fd); } /* end while */ return (NULL); /* autobaud failed */ } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #include #include #include #include #include #include #include #include #include #include #include "sys/stropts.h" #include #include "sac.h" #include "ttymon.h" #include "tmstruct.h" #include "tmextern.h" #ifdef SYS_NAME #include #endif static void openline(struct pmtab *, struct Gdef *); static void invoke_service(struct pmtab *); static char *do_autobaud(struct pmtab *, struct Gdef *); static struct Gdef *next_speed(struct Gdef *); static int check_hup(int); /* * tmchild - process that handles peeking data, determine baud rate * and invoke service on each individual port. * */ void tmchild(struct pmtab *pmtab) { struct Gdef *speedef; char *auto_speed = ""; struct sigaction sigact; #ifdef DEBUG debug("in tmchild"); #endif peek_ptr = NULL; if (pmtab->p_status != GETTY) { child_sigcatch(); (void) close(PCpipe[0]); /* close parent end of the pipe */ if (ioctl(PCpipe[1], I_SETSIG, S_HANGUP) == -1) { log("I_SETSIG failed: %s", strerror(errno)); exit(1); } /* * the following check is to make sure no hangup * happens before registering for SIGPOLL */ if (check_hup(PCpipe[1])) { #ifdef DEBUG debug("PCpipe hungup, tmchild exiting"); #endif exit(1); } if (pmtab->p_ttyflags & (C_FLAG|B_FLAG)) { if (pmtab->p_fd > 0) { (void) close(pmtab->p_fd); pmtab->p_fd = 0; } } /* * become the session leader so that a controlling tty * will be allocated. */ (void) setsid(); } speedef = get_speed(pmtab); openline(pmtab, speedef); if (pmtab->p_ttyflags & (C_FLAG|B_FLAG)) { if (pmtab->p_fd >= 0) { if ((pmtab->p_modules != NULL) && (*(pmtab->p_modules) != '\0')) { if (push_linedisc(pmtab->p_fd, pmtab->p_modules, pmtab->p_device) == -1) { (void) close(pmtab->p_fd); return; } } } } if ((pmtab->p_ttyflags & C_FLAG) && (State != PM_DISABLED) && (!(pmtab->p_flags & X_FLAG))) { /* * if "c" flag is set, and the port is not disabled * invoke service immediately */ if (set_termio(0, speedef->g_fflags, NULL, FALSE, CANON) == -1) { log("set final termio failed"); exit(1); } invoke_service(pmtab); exit(1); /*NOTREACHED*/ } if (speedef->g_autobaud & A_FLAG) { auto_speed = do_autobaud(pmtab, speedef); } if (set_termio(0, speedef->g_fflags, NULL, FALSE, CANON) == -1) { log("set final termio failed"); exit(1); } if ((pmtab->p_ttyflags & (R_FLAG|A_FLAG)) || (pmtab->p_status == GETTY) || (pmtab->p_timeout > 0)) { write_prompt(1, pmtab, TRUE, TRUE); if (pmtab->p_timeout) { sigact.sa_flags = 0; sigact.sa_handler = timedout; (void) sigemptyset(&sigact.sa_mask); (void) sigaction(SIGALRM, &sigact, NULL); (void) alarm((unsigned)pmtab->p_timeout); } } else if ((pmtab->p_ttyflags & (B_FLAG))) write_prompt(pmtab->p_fd, pmtab, TRUE, TRUE); /* Loop until user is successful in invoking service. */ for (;;) { /* Peek the user's typed response and respond appropriately. */ switch (poll_data()) { case GOODNAME: #ifdef DEBUG debug("got GOODNAME"); #endif if (pmtab->p_timeout) { (void) alarm((unsigned)0); sigact.sa_flags = 0; sigact.sa_handler = SIG_DFL; (void) sigemptyset(&sigact.sa_mask); (void) sigaction(SIGALRM, &sigact, NULL); } if ((State == PM_DISABLED) || (pmtab->p_flags & X_FLAG)) { write_prompt(1, pmtab, TRUE, FALSE); break; } if (set_termio(0, speedef->g_fflags, auto_speed, FALSE, CANON) == -1) { log("set final termio failed"); exit(1); } invoke_service(pmtab); exit(1); /*NOTREACHED*/ case BADSPEED: /* wrong speed! try next speed in the list. */ speedef = next_speed(speedef); #ifdef DEBUG debug("BADSPEED: setup next speed"); #endif if (speedef->g_autobaud & A_FLAG) { if (auto_termio(0) == -1) { exit(1); } auto_speed = do_autobaud(pmtab, speedef); } else { auto_speed = NULL; /* * this reset may fail if the speed is not * supported by the system * we just cycle through it to the next one */ if (set_termio(0, speedef->g_iflags, NULL, FALSE, CANON) != 0) { log("Warning -- speed of <%s> may " "be not supported by the system", speedef->g_id); } } write_prompt(1, pmtab, TRUE, TRUE); break; case NONAME: #ifdef DEBUG debug("got NONAME"); #endif write_prompt(1, pmtab, FALSE, FALSE); break; } /* end switch */ peek_ptr = NULL; if (pmtab->p_timeout) { sigact.sa_flags = 0; sigact.sa_handler = timedout; (void) sigemptyset(&sigact.sa_mask); (void) sigaction(SIGALRM, &sigact, NULL); (void) alarm((unsigned)pmtab->p_timeout); } } /* end for loop */ } static void openline(struct pmtab *pmtab, struct Gdef *speedef) { char buffer[5]; int rtn = 0; int line_count; #ifdef DEBUG debug("in openline"); #endif if (pmtab->p_status != GETTY) { (void) close(0); /* open should return fd 0, if not, then close it */ if ((pmtab->p_fd = open(pmtab->p_device, O_RDWR)) != 0) { log("open \"%s\" failed: %s", pmtab->p_device, strerror(errno)); exit(1); } } (void) close(1); (void) close(2); (void) dup(0); (void) dup(0); if (pmtab->p_ttyflags & R_FLAG) { /* wait_read is needed */ if (pmtab->p_count) { if (peek_ptr != NULL) if ((peek_ptr->buf[0]&0x7F) == '\n' || (peek_ptr->buf[0]&0x7F) == '\r') pmtab->p_count--; /* * - wait for "p_count" lines * - datakit switch does not * know you are a host or a terminal * - so it send you several lines of msg * - we need to swallow that msg * - we assume the baud rate is correct * - if it is not, '\n' will not look like '\n' * and we will wait forever here */ if (set_termio(0, speedef->g_fflags, NULL, TRUE, CANON) == -1) { log("set final termio failed"); exit(1); } for (line_count = 0; line_count < pmtab->p_count; ) { if (read(0, buffer, 1) < 0 || *buffer == '\0' || *buffer == '\004') { (void) close(0); exit(0); } if (*buffer == '\n') line_count++; } } else { /* wait for 1 char */ if (peek_ptr == NULL) { if (set_termio(0, NULL, NULL, TRUE, RAW) == -1) { log("set termio RAW failed"); exit(1); } rtn = read(0, buffer, 1); } else *buffer = (peek_ptr->buf[0]&0x7F); /* * NOTE: Cu on a direct line when ~. is encountered will * send EOTs to the other side. EOT=\004 */ if (rtn < 0 || *buffer == '\004') { (void) close(0); exit(0); } } peek_ptr = NULL; if (!(pmtab->p_ttyflags & A_FLAG)) { /* autobaud not enabled */ if (set_termio(0, speedef->g_fflags, NULL, TRUE, CANON) == -1) { log("set final termio failed"); exit(1); } } } if (pmtab->p_ttyflags & B_FLAG) { /* port is bi-directional */ /* set advisory lock on the line */ if (tm_lock(0) != 0) { /* * device is locked * child exits and let the parent wait for * the lock to go away */ exit(0); } /* change ownership back to root */ (void) fchown(0, ROOTUID, Tty_gid); (void) fchmod(0, 0620); } } /* * write_prompt - write the msg to fd * - if flush is set, flush input queue * - if clear is set, write a new line */ void write_prompt(int fd, struct pmtab *pmtab, int flush, int clear) { #ifdef DEBUG debug("in write_prompt"); #endif if (flush) flush_input(fd); if (clear) { (void) write(fd, "\r\n", 2); } #ifdef SYS_NAME sys_name(fd); #endif /* Print prompt/disable message. */ if ((State == PM_DISABLED) || (pmtab->p_flags & X_FLAG)) (void) write(fd, pmtab->p_dmsg, (unsigned)strlen(pmtab->p_dmsg)); else (void) write(fd, pmtab->p_prompt, (unsigned)strlen(pmtab->p_prompt)); } /* * timedout - input period timed out */ void timedout(int signal __unused) { exit(1); } #ifdef SYS_NAME /* * void sys_name() - generate a msg with system id * - print out /etc/issue file if it exists */ void sys_name(int fd) { char *ptr, buffer[BUFSIZ]; FILE *fp; #if 0 /* 1111333 - don't print node name, we already do this elsewhere */ struct utsname utsname; if (uname(&utsname) != FAILURE) { (void) sprintf(buffer, "%.9s\r\n", utsname.nodename); (void) write(fd, buffer, strlen(buffer)); } #endif if ((fp = fopen(ISSUEFILE, "r")) != NULL) { while ((ptr = fgets(buffer, sizeof (buffer), fp)) != NULL) { (void) write(fd, ptr, strlen(ptr)); } (void) fclose(fp); } } #endif /* * do_autobaud - do autobaud * - if it succeed, set the new speed and return * - if it failed, it will get the nextlabel * - if next entry is also autobaud, * it will loop back to do autobaud again * - otherwise, it will set new termio and return */ static char * do_autobaud(struct pmtab *pmtab, struct Gdef *speedef) { int done = FALSE; char *auto_speed; #ifdef DEBUG debug("in do_autobaud"); #endif while (!done) { if ((auto_speed = autobaud(0, pmtab->p_timeout)) == NULL) { speedef = next_speed(speedef); if (speedef->g_autobaud & A_FLAG) { continue; } else { if (set_termio(0, speedef->g_iflags, NULL, TRUE, CANON) != 0) { exit(1); } done = TRUE; } } else { if (set_termio(0, speedef->g_fflags, auto_speed, TRUE, CANON) != 0) { exit(1); } done = TRUE; } } #ifdef DEBUG debug("autobaud done"); #endif return (auto_speed); } /* * next_speed(speedef) * - find the next entry according to nextlabel. If "nextlabel" * is not valid, go back to the old ttylabel. */ static struct Gdef * next_speed(struct Gdef *speedef) { struct Gdef *sp; if (strcmp(speedef->g_nextid, speedef->g_id) == 0) return (speedef); if ((sp = find_def(speedef->g_nextid)) == NULL) { log("%s's next speed-label (%s) is bad.", speedef->g_id, speedef->g_nextid); /* go back to the original entry. */ if ((sp = find_def(speedef->g_id)) == NULL) { /* if failed, complain and quit. */ log("unable to find (%s) again", speedef->g_id); exit(1); } } return (sp); } /* * inform_parent() - inform ttymon that tmchild is going to exec service */ static void inform_parent(int fd) { pid_t pid; pid = getpid(); (void) write(fd, &pid, sizeof (pid)); } static char pbuf[BUFSIZ]; /* static buf for TTYPROMPT */ static char hbuf[BUFSIZ]; /* static buf for HOME */ static char tbuf[BUFSIZ]; /* static buf for TERM */ /* * void invoke_service - invoke the service */ static void invoke_service(struct pmtab *pmtab) { char *argvp[MAXARGS]; /* service cmd args */ int cnt = 0; /* arg counter */ int i; struct sigaction sigact; #ifdef DEBUG debug("in invoke_service"); #endif if (tcgetsid(0) != getsid(getpid())) { cons_printf("Warning -- ttymon cannot allocate controlling " "tty on \"%s\",\n", pmtab->p_device); cons_printf("\tThere may be another session active on this " "port.\n"); if (strcmp("/dev/console", pmtab->p_device) != 0) { /* * if not on console, write to stderr to warn the user * also. */ (void) fprintf(stderr, "Warning -- ttymon cannot " "allocate controlling tty on \"%s\",\n", pmtab->p_device); (void) fprintf(stderr, "\tthere may be another session " "active on this port.\n"); } } if (pmtab->p_status != GETTY) { inform_parent(PCpipe[1]); sigact.sa_flags = 0; sigact.sa_handler = SIG_DFL; (void) sigemptyset(&sigact.sa_mask); (void) sigaction(SIGPOLL, &sigact, NULL); } if (pmtab->p_flags & U_FLAG) { if (account(pmtab->p_device) != 0) { log("invoke_service: account failed"); exit(1); } } /* parse command line */ mkargv(pmtab->p_server, &argvp[0], &cnt, MAXARGS-1); if (!(pmtab->p_ttyflags & C_FLAG)) { (void) sprintf(pbuf, "TTYPROMPT=%s", pmtab->p_prompt); if (putenv(pbuf)) { log("cannot expand service <%s> environment", argvp[0]); exit(1); } } if (pmtab->p_status != GETTY) { (void) sprintf(hbuf, "HOME=%s", pmtab->p_dir); if (putenv(hbuf)) { log("cannot expand service <%s> environment", argvp[0]); exit(1); } #ifdef DEBUG debug("about to run config script"); #endif if ((i = doconfig(0, pmtab->p_tag, 0)) != 0) { if (i < 0) { log("doconfig failed, system error"); } else { log("doconfig failed on line %d of script %s", i, pmtab->p_tag); } exit(1); } } if (setgid(pmtab->p_gid)) { log("cannot set group id to %ld: %s", pmtab->p_gid, strerror(errno)); exit(1); } if (setuid(pmtab->p_uid)) { log("cannot set user id to %ld: %s", pmtab->p_uid, strerror(errno)); exit(1); } if (chdir(pmtab->p_dir)) { log("cannot chdir to %s: %s", pmtab->p_dir, strerror(errno)); exit(1); } if (pmtab->p_uid != ROOTUID) { /* change ownership and mode of device */ (void) fchown(0, pmtab->p_uid, Tty_gid); (void) fchmod(0, 0620); } if (pmtab->p_status != GETTY) { sigact.sa_flags = 0; sigact.sa_handler = SIG_DFL; (void) sigemptyset(&sigact.sa_mask); (void) sigaction(SIGINT, &sigact, NULL); if (setrlimit(RLIMIT_NOFILE, &Rlimit) == -1) { log("setrlimit failed: %s", strerror(errno)); exit(1); } /* invoke the service */ log("Starting service (%s) on %s", argvp[0], pmtab->p_device); } if (pmtab->p_termtype != (char *)NULL) { (void) sprintf(tbuf, "TERM=%s", pmtab->p_termtype); if (putenv(tbuf)) { log("cannot expand service <%s> environment", argvp[0]); exit(1); } } /* restore signal handlers and mask */ (void) sigaction(SIGINT, &Sigint, NULL); (void) sigaction(SIGALRM, &Sigalrm, NULL); (void) sigaction(SIGPOLL, &Sigpoll, NULL); (void) sigaction(SIGQUIT, &Sigquit, NULL); (void) sigaction(SIGCLD, &Sigcld, NULL); (void) sigaction(SIGTERM, &Sigterm, NULL); #ifdef DEBUG (void) sigaction(SIGUSR1, &Sigusr1, NULL); (void) sigaction(SIGUSR2, &Sigusr2, NULL); #endif (void) sigprocmask(SIG_SETMASK, &Origmask, NULL); (void) execve(argvp[0], argvp, environ); /* exec returns only on failure! */ log("tmchild: exec service failed: %s", strerror(errno)); exit(1); } /* * check_hup(fd) - do a poll on fd to check if it is in hangup state * - return 1 if hangup, otherwise return 0 */ static int check_hup(int fd) { int ret; struct pollfd pfd[1]; pfd[0].fd = fd; pfd[0].events = POLLHUP; for (;;) { ret = poll(pfd, 1, 0); if (ret < 0) { if (errno == EINTR) continue; log("check_hup: poll failed: %s", strerror(errno)); exit(1); } else if (ret > 0) { if (pfd[0].revents & POLLHUP) { return (1); } } return (0); } } /* * sigpoll() - SIGPOLL handle for tmchild * - when SIGPOLL is received by tmchild, * the pipe between ttymon and tmchild is broken. * Something must happen to ttymon. */ void sigpoll(int s __unused) { #ifdef DEBUG debug("tmchild got SIGPOLL, exiting"); #endif exit(1); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ttymon.h" #include "tmextern.h" #include "tmstruct.h" static char devbuf[BUFSIZ]; static char *devname; static int parse_args(int, char **, struct pmtab *); static void ttymon_options(int, char **, struct pmtab *); static void getty_options(int, char **, struct pmtab *); static void usage(void); static char *find_ttyname(int); /* * ttymon_express - This is call when ttymon is invoked with args * or invoked as getty * - This special version of ttymon will monitor * one port only * - It is intended to be used when some process * wants to have a login session on the fly */ void ttymon_express(int argc, char **argv) { struct pmtab *pmtab; struct sigaction sigact; #ifdef DEBUG #endif #ifdef DEBUG opendebug(TRUE); #endif sigact.sa_flags = 0; sigact.sa_handler = SIG_IGN; (void) sigemptyset(&sigact.sa_mask); (void) sigaction(SIGINT, &sigact, NULL); if ((pmtab = ALLOC_PMTAB) == NULL) { log("ttymon_express: ALLOC_PMTAB failed"); exit(1); } if (parse_args(argc, argv, pmtab) != 0) { log("ttymon_express: parse_args failed"); exit(1); } read_ttydefs(NULL, FALSE); if ((pmtab->p_device != NULL) && (*(pmtab->p_device) != '\0')) while (checkut_line(pmtab->p_device)) (void) sleep(15); if ((pmtab->p_device == NULL) || (*(pmtab->p_device) == '\0')) { devname = find_ttyname(0); if ((devname == NULL) || (*devname == '\0')) { log("ttyname cannot find the device on fd 0"); exit(1); } pmtab->p_device = devname; #ifdef DEBUG debug("ttymon_express: devname = %s", devname); #endif /* * become session leader * fd 0 is closed and reopened just to make sure * controlling tty is set up right */ (void) setsid(); (void) close(0); revokedevaccess(pmtab->p_device, 0, 0, 0); if (open(pmtab->p_device, O_RDWR) < 0) { log("open %s failed: %s", pmtab->p_device, strerror(errno)); exit(1); } if ((pmtab->p_modules != NULL) && (*(pmtab->p_modules) != '\0')) { if (push_linedisc(0, pmtab->p_modules, pmtab->p_device) == -1) exit(1); } if (initial_termio(0, pmtab) == -1) exit(1); (void) di_devperm_logout((const char *)pmtab->p_device); } else { (void) setsid(); (void) close(0); Retry = FALSE; open_device(pmtab); if (Retry) /* open failed */ exit(1); } tmchild(pmtab); exit(1); /*NOTREACHED*/ } /* * For serial device, return ttyX-mode property value. */ static char * get_ttymode_prop(dev_t rconsdev) { char *rootpath = "/"; char path[MAXPATHLEN]; di_node_t root; char *propname, *v; struct stat st; (void) snprintf(path, sizeof (path), "/dev/tty%c", 'a' + minor(rconsdev)); if (stat(path, &st) < 0) return (NULL); if (st.st_rdev != rconsdev) return (NULL); if (asprintf(&propname, "%s-mode", path + 5) <= 0) return (NULL); root = di_init(rootpath, DINFOPROP); if (root == DI_NODE_NIL) { free(propname); return (NULL); } v = NULL; if (di_prop_lookup_strings(DDI_DEV_T_ANY, root, propname, &v) > 0) v = strdup(v); di_fini(root); free(propname); return (v); } /* * parse_arg - parse cmd line arguments */ static int parse_args(int argc, char **argv, struct pmtab *pmtab) { static char p_server[] = "/usr/bin/login"; static char termbuf[MAX_TERM_TYPE_LEN]; static struct cons_getterm cnterm = {sizeof (termbuf), termbuf}; /* initialize fields to some default first */ pmtab->p_tag = ""; pmtab->p_flags = 0; pmtab->p_identity = "root"; pmtab->p_res1 = "reserved"; pmtab->p_res2 = "reserved"; pmtab->p_res3 = "reserved"; pmtab->p_uid = 0; pmtab->p_gid = 0; pmtab->p_dir = "/"; pmtab->p_ttyflags = 0; pmtab->p_count = 0; pmtab->p_server = p_server; pmtab->p_timeout = 0; pmtab->p_modules = ""; pmtab->p_prompt = "login: "; pmtab->p_dmsg = ""; pmtab->p_termtype = ""; pmtab->p_device = ""; pmtab->p_status = GETTY; pmtab->p_ttymode = NULL; if (strcmp(lastname(argv[0]), "getty") == 0) { pmtab->p_ttylabel = "300"; getty_options(argc, argv, pmtab); } else { int cn_fd; struct cons_getdev cnd; pmtab->p_ttylabel = "9600"; ttymon_options(argc, argv, pmtab); /* * The following code is only reached if -g was specified. * It attempts to determine a suitable terminal type for * the console login process, and in case we are using * serial console, tty mode line. * * If -d /dev/console also specified, we send an ioctl * to the console device to query the TERM type. * * If any of the tests, system calls, or ioctls fail * then pmtab->p_termtype retains its default value * of "". otherwise it is set to a term type value * that was returned. */ if (strcmp(pmtab->p_device, "/dev/console") == 0 && (cn_fd = open("/dev/console", O_RDONLY)) != -1) { if (strlen(pmtab->p_termtype) == 0 && ioctl(cn_fd, CONS_GETTERM, &cnterm) != -1) { pmtab->p_termtype = cnterm.cn_term_type; } if (ioctl(cn_fd, CONS_GETDEV, &cnd) != -1) pmtab->p_ttymode = get_ttymode_prop(cnd.cnd_rconsdev); (void) close(cn_fd); } } if ((pmtab->p_device != NULL) && (*(pmtab->p_device) != '\0')) getty_account(pmtab->p_device); /* utmp accounting */ return (0); } /* * ttymon_options - scan and check args for ttymon express */ static void ttymon_options(int argc, char **argv, struct pmtab *pmtab) { int c; /* option letter */ char *timeout; int gflag = 0; /* -g seen */ int size = 0; char tbuf[BUFSIZ]; while ((c = getopt(argc, argv, "T:gd:ht:p:m:l:")) != -1) { switch (c) { case 'g': gflag = 1; break; case 'd': pmtab->p_device = optarg; break; case 'h': pmtab->p_ttyflags &= ~H_FLAG; break; case 'T': pmtab->p_termtype = optarg; break; /* * case 'b': * pmtab->p_ttyflags |= B_FLAG; * pmtab->p_ttyflags |= R_FLAG; * break; */ case 't': timeout = optarg; while (*optarg) { if (!isdigit(*optarg++)) { log("Invalid argument for " "\"-t\" -- number expected."); usage(); } } pmtab->p_timeout = atoi(timeout); break; case 'p': copystr(tbuf, optarg); pmtab->p_prompt = strsave(getword(tbuf, &size, TRUE)); break; case 'm': pmtab->p_modules = optarg; if (vml(pmtab->p_modules) != 0) usage(); break; case 'l': pmtab->p_ttylabel = optarg; break; case '?': usage(); break; /*NOTREACHED*/ } } if (optind < argc) usage(); if (!gflag) usage(); } /* * usage - print out a usage message */ static void usage(void) { char *umsg = "Usage: ttymon\n ttymon -g [-h] [-d device] " "[-l ttylabel] [-t timeout] [-p prompt] [-m modules]\n"; if (isatty(STDERR_FILENO)) (void) fprintf(stderr, "%s", umsg); else cons_printf(umsg); exit(1); } /* * getty_options - this is cut from getty.c * - it scan getty cmd args * - modification is made to stuff args in pmtab */ static void getty_options(int argc, char **argv, struct pmtab *pmtab) { char *ptr; /* * the pre-4.0 getty's hang_up_line() is a no-op. * For compatibility, H_FLAG cannot be set for this "getty". */ pmtab->p_ttyflags &= ~(H_FLAG); while (--argc && **++argv == '-') { for (ptr = *argv + 1; *ptr; ptr++) { switch (*ptr) { case 'h': break; case 't': if (isdigit(*++ptr)) { (void) sscanf(ptr, "%d", &(pmtab->p_timeout)); while (isdigit(*++ptr)) ; ptr--; } else if (--argc) { if (isdigit(*(ptr = *++argv))) (void) sscanf(ptr, "%d", &(pmtab->p_timeout)); else { log("getty: timeout argument " "<%s> invalid", *argv); exit(1); } } break; case 'c': log("Use \"sttydefs -l\" to check " "/etc/ttydefs."); exit(0); default: break; } } } if (argc < 1) { log("getty: no terminal line specified."); exit(1); } else { (void) strcat(devbuf, "/dev/"); (void) strcat(devbuf, *argv); pmtab->p_device = devbuf; } if (--argc > 0) { pmtab->p_ttylabel = *++argv; } /* * every thing after this will be ignored * i.e. termtype and linedisc are ignored */ } /* * find_ttyname(fd) - find the name of device associated with fd. * - it first tries utmpx to see if an entry exists * - with my pid and ut_line is defined. If ut_line * - is defined, it will see if the major and minor * - number of fd and devname from utmpx match. * - If utmpx search fails, ttyname(fd) will be called. */ static char * find_ttyname(int fd) { pid_t ownpid; struct utmpx *u; static struct stat statf, statu; static char buf[BUFSIZ]; ownpid = getpid(); setutxent(); while ((u = getutxent()) != NULL) { if (u->ut_pid == ownpid) { if (strlen(u->ut_line) != 0) { if (*(u->ut_line) != '/') { (void) strcpy(buf, "/dev/"); (void) strncat(buf, u->ut_line, sizeof (u->ut_line)); } else { (void) strncat(buf, u->ut_line, sizeof (u->ut_line)); } } else u = NULL; break; } } endutxent(); if ((u != NULL) && (fstat(fd, &statf) == 0) && (stat(buf, &statu) == 0) && (statf.st_dev == statu.st_dev) && (statf.st_rdev == statu.st_rdev)) { #ifdef DEBUG debug("ttymon_express: find device name from utmpx."); #endif return (buf); } else { #ifdef DEBUG debug("ttymon_express: calling ttyname to find device name."); #endif return (ttyname(fd)); } } /* * Revoke all access to a device node and make sure that there are * no interposed streams devices attached. Must be called before a * device is actually opened. * When fdetach is called, the underlying device node is revealed; it * will have the previous owner and that owner can re-attach; so we * retry until we win. * Ignore non-existent devices. */ void revokedevaccess(char *dev, uid_t uid, gid_t gid, mode_t mode) { do { if (chown(dev, uid, gid) == -1) return; } while (fdetach(dev) == 0); /* Remove ACLs */ (void) acl_strip(dev, uid, gid, mode); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #ifndef _TMEXTERN_H #define _TMEXTERN_H #include #include #include #include #include #include #include #include #include "tmstruct.h" #ifdef __cplusplus extern "C" { #endif /* admutil.c */ extern int find_label(FILE *, char *); /* sttytable.c */ extern const char *not_supported[]; /* sttyparse.c */ #ifdef EUC extern char *sttyparse(int, char *[], int, struct termio *, struct termios *, struct termiox *, struct winsize *, eucwidth_t *, struct eucioc *, ldterm_cs_data_user_t *, ldterm_cs_data_user_t *); extern int get_ttymode(int, struct termio *, struct termios *, struct stio *, struct termiox *, struct winsize *, struct eucioc *, ldterm_cs_data_user_t *); extern int set_ttymode(int, int, struct termio *, struct termios *, struct stio *, struct termiox *, struct winsize *, struct winsize *, struct eucioc *, ldterm_cs_data_user_t *, int); #else extern char *sttyparse(int, char *[], int, struct termio *, struct termios *, struct termiox *, struct winsize *); extern int get_ttymode(int, struct termio *, struct termios *, struct stio *, struct termiox *, struct winsize *); extern int set_ttymode(int, int, struct termio *, struct termios *, struct stio *, struct termiox *, struct winsize *, struct winsize *); #endif /* tmautobaud.c */ extern int auto_termio(int); extern char *autobaud(int, int); /* tmchild.c */ extern void write_prompt(int, struct pmtab *, int, int); extern void timedout(int); extern void tmchild(struct pmtab *); extern void sigpoll(int); /* tmexpress.c */ extern void ttymon_express(int, char **); extern void revokedevaccess(char *, uid_t, gid_t, mode_t); /* tmhandler.c */ extern void do_poll(struct pollfd *, int); extern void sigalarm(int); extern void sigterm(int); extern void sigchild(int); extern void sigpoll_catch(int); extern void state_change(void); extern void re_read(void); extern void got_carrier(struct pmtab *); /* tmlock.c */ extern int tm_checklock(int); extern int tm_lock(int); extern int check_session(int); extern char *lastname(char *); /* tmlog.c */ extern void log(const char *, ...); extern void fatal(const char *, ...); extern void openttymonlog(void); /* tmparse.c */ extern char *getword(char *, int *, int); extern char quoted(char *, int *); /* tmpeek.c */ extern int poll_data(void); extern void sigint(int); /* tmpmtab.c */ extern void read_pmtab(void); extern void purge(void); /* tmsac.c */ extern void openpid(void); extern void openpipes(void); extern void get_environ(void); extern void sacpoll(void); /* tmsig.c */ extern void catch_signals(void); extern void child_sigcatch(void); /* tmterm.c */ extern int push_linedisc(int, char *, char *); extern int set_termio(int, char *, char *, int, long); extern int initial_termio(int, struct pmtab *); extern int hang_up_line(int); extern void flush_input(int); /* tmttydefs.c */ extern void read_ttydefs(const char *, int); extern struct Gdef *find_def(char *); extern void mkargv(char *, char **, int *, int); extern int check_flags(char *); extern char *strsave(char *); /* tmutmp.c */ extern int account(char *); extern void cleanut(pid_t, int); extern int checkut_line(char *line); extern void getty_account(char *); /* tmutil.c */ extern int check_device(char *); extern int check_cmd(char *); extern void cons_printf(const char *, ...); extern void copystr(char *, char *); extern int strcheck(char *, int); extern int vml(char *); /* misc sys call or lib function call */ extern int check_version(int, char *); #ifdef SYS_NAME extern void sys_name(int); #endif /* tmglobal.c */ extern struct Gdef DEFAULT; extern int Retry; extern struct rlimit Rlimit; extern struct pmtab *PMtab; extern struct pollfd *Pollp; extern int Nentries; extern int Npollfd; extern struct Gdef Gdef[]; extern int Ndefs; extern long Mtime; extern FILE *Logfp; extern int Sfd, Pfd; extern int PCpipe[]; extern int Lckfd; extern char State; extern char *Istate; extern char *Tag; extern int Reread_flag; extern int Maxfiles; extern int Maxfds; extern char **environ; extern char *optarg; extern int optind, opterr; extern int Nlocked; extern sigset_t Origmask; extern struct sigaction Sigalrm; /* SIGALRM */ extern struct sigaction Sigcld; /* SIGCLD */ extern struct sigaction Sigint; /* SIGINT */ extern struct sigaction Sigpoll; /* SIGPOLL */ extern struct sigaction Sigquit; /* SIGQUIT */ extern struct sigaction Sigterm; /* SIGTERM */ #ifdef DEBUG extern struct sigaction Sigusr1; /* SIGUSR1 */ extern struct sigaction Sigusr2; /* SIGUSR2 */ #endif #ifdef DEBUG extern FILE *Debugfp; extern void debug(const char *, ...); #endif extern uid_t Uucp_uid; extern gid_t Tty_gid; extern struct strbuf *peek_ptr; extern int Logmaxsz; extern int Splflag; /* ttymon.c */ extern struct Gdef *get_speed(struct pmtab *); extern void open_device(struct pmtab *); extern void set_softcar(struct pmtab *); extern void setup_PCpipe(void); /* ulockf.c */ extern int fd_cklock(int); extern int fd_mklock(int); #ifdef __cplusplus } #endif #endif /* _TMEXTERN_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #include #include #include #include #include #include #include "tmstruct.h" #include "ttymon.h" /* * global fd and fp */ FILE *Logfp = NULL; /* for log file */ int Lckfd; /* for pid file */ int Sfd, Pfd; /* for sacpipe and pmpipe */ int PCpipe[2]; /* pipe between Parent & Children */ #ifdef DEBUG FILE *Debugfp = NULL; /* for debug file */ #endif char State = PM_STARTING; /* current state */ char *Istate; /* initial state */ char *Tag; /* port monitor tag */ int Maxfiles; /* Max number of open files */ int Maxfds; /* Max no of devices ttymon can monitor */ int Reread_flag = FALSE; /* reread pmtab flag */ int Retry; /* retry open_device flag */ struct pmtab *PMtab = NULL; /* head pointer to pmtab linked list */ int Nentries = 0; /* # of entries in pmtab linked list */ struct Gdef Gdef[MAXDEFS]; /* array to hold entries in /etc/ttydefs */ int Ndefs = 0; /* highest index to Gdef that was used */ long Mtime = 0; /* last modification time of ttydefs */ struct pollfd *Pollp; /* ptr to an array of poll struct */ int Npollfd; /* size of the pollfd array */ struct Gdef DEFAULT = { /* default terminal settings */ "default", "9600", "9600 sane", 0, /* * next label is set to 4800 so we can start searching ttydefs. * if 4800 is not in ttydefs, we will loop back to use DEFAULT */ "4800" }; uid_t Uucp_uid = 5; /* owner's uid for bi-directional ports */ gid_t Tty_gid = GID_TTY; /* group id for all tty devices */ /* * Nlocked - number of ports that are either locked or have active * sessions not under this ttymon. */ int Nlocked = 0; /* original rlimit value */ struct rlimit Rlimit; /* * places to remember original signal dispositions and masks */ sigset_t Origmask; /* original signal mask */ struct sigaction Sigalrm; /* SIGALRM */ struct sigaction Sigcld; /* SIGCLD */ struct sigaction Sigint; /* SIGINT */ struct sigaction Sigpoll; /* SIGPOLL */ struct sigaction Sigquit; /* SIGQUIT */ struct sigaction Sigterm; /* SIGTERM */ #ifdef DEBUG struct sigaction Sigusr1; /* SIGUSR1 */ struct sigaction Sigusr2; /* SIGUSR2 */ #endif struct strbuf *peek_ptr; int Logmaxsz = 1000000; /* Log Max Size */ int Splflag = 0; /* serialize Log file manipulation */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 2018, Joyent, Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "ttymon.h" #include "tmstruct.h" #include "tmextern.h" #include "sac.h" static struct pmtab *find_pid(pid_t); static void kill_subprocesses(void); static struct pmtab *find_fd(int); static void pcsync_close(int *, int *, int, int); /* * fork_tmchild - fork child on the device */ static void fork_tmchild(struct pmtab *pmptr) { pid_t pid; sigset_t cset; sigset_t tset; int pcpipe0[2], pcpipe1[2]; int p0; #ifdef DEBUG debug("in fork_tmchild"); #endif pmptr->p_inservice = FALSE; /* * initialize pipe. * Child has pcpipe[0] pipe fd for reading and writing * and closes pcpipe[1]. Parent has pcpipe[1] pipe fd for * reading and writing and closes pcpipe[0]. * * This way if the child process exits the parent's block * read on pipe will return immediately as the other end of * the pipe has closed. Similarly if the parent process exits * child's blocking read on the pipe will return immediately. */ if (((p0 = pipe(pcpipe0)) == -1) || (pipe(pcpipe1) == -1)) { if (p0 == 0) { (void) close(pcpipe0[0]); (void) close(pcpipe0[1]); } log("pipe() failed: %s", strerror(errno)); pmptr->p_status = VALID; pmptr->p_childpid = 0; Retry = TRUE; } /* protect following region from SIGCLD */ (void) sigprocmask(SIG_SETMASK, NULL, &cset); tset = cset; (void) sigaddset(&tset, SIGCLD); (void) sigprocmask(SIG_SETMASK, &tset, NULL); if ((pid = fork()) == 0) { /* * Close all file descriptors except pmptr->p_fd * Wait for the parent process to close its fd */ pcsync_close(pcpipe0, pcpipe1, pid, pmptr->p_fd); /* The CHILD */ tmchild(pmptr); /* tmchild should never return */ fatal("tmchild for <%s> returns unexpected", pmptr->p_device); } else if (pid < 0) { log("fork failed: %s", strerror(errno)); pmptr->p_status = VALID; pmptr->p_childpid = 0; Retry = TRUE; } else { /* * The PARENT - store pid of child and close the device */ pmptr->p_childpid = pid; } if (pmptr->p_fd > 0) { (void) close(pmptr->p_fd); pmptr->p_fd = 0; } (void) sigprocmask(SIG_SETMASK, &cset, NULL); /* * Wait for child to close file descriptors */ pcsync_close(pcpipe0, pcpipe1, pid, pmptr->p_fd); } /* * got_carrier - carrier is detected on the stream * - depends on the flags, different action is taken * - R_FLAG - wait for data * - C_FLAG - if port is not disabled, fork tmchild * - A_FLAG - wait for data * - otherwise - write out prompt, then wait for data */ void got_carrier(struct pmtab *pmptr) { flush_input(pmptr->p_fd); if (pmptr->p_ttyflags & R_FLAG) { #ifdef DEBUG debug("R_FLAG"); #endif return; } else if ((pmptr->p_ttyflags & (C_FLAG|B_FLAG)) && (State != PM_DISABLED) && (!(pmptr->p_flags & X_FLAG))) { fork_tmchild(pmptr); } else if (pmptr->p_ttyflags & A_FLAG) { #ifdef DEBUG debug("A_FLAG"); #endif return; } else if (pmptr->p_timeout) { fork_tmchild(pmptr); } else if (!(pmptr->p_ttyflags & X_FLAG)) { write_prompt(pmptr->p_fd, pmptr, TRUE, TRUE); } } /* * got_data - data is detected on the stream, fork tmchild */ static void got_data(struct pmtab *pmptr) { struct sigaction sigact; if (tm_checklock(pmptr->p_fd) != 0) { pmptr->p_status = LOCKED; (void) close(pmptr->p_fd); pmptr->p_fd = 0; Nlocked++; if (Nlocked == 1) { sigact.sa_flags = 0; sigact.sa_handler = sigalarm; (void) sigemptyset(&sigact.sa_mask); (void) sigaction(SIGALRM, &sigact, NULL); (void) alarm(ALARMTIME); } } else { fork_tmchild(pmptr); } } /* * got_hup - stream hangup is detected, close the device */ static void got_hup(struct pmtab *pmptr) { #ifdef DEBUG debug("in got hup"); #endif (void) close(pmptr->p_fd); pmptr->p_fd = 0; pmptr->p_inservice = 0; Retry = TRUE; } /* * do_poll - poll device * - if POLLHUP received, close the device * - if POLLIN received, fork tmchild. */ void do_poll(struct pollfd *fdp, int nfds) { int i, n; struct pmtab *pmptr; n = poll(fdp, (unsigned long)nfds, -1); /* blocked poll */ #ifdef DEBUG debug("poll return"); #endif if (n < 0) { if (errno == EINTR) /* interrupt by signal */ return; fatal("do_poll: poll failed: %s", strerror(errno)); } for (i = 0; (i < nfds) && (n != 0); i++, fdp++) { if (fdp->revents != 0) { n--; if ((pmptr = find_fd(fdp->fd)) == NULL) { log("do_poll: cannot find fd %d in pmtab", fdp->fd); continue; } else if (fdp->revents & POLLHUP) { got_hup(pmptr); } else if (fdp->revents & POLLIN) { #ifdef DEBUG debug("got POLLIN"); #endif got_data(pmptr); } else if (fdp->revents & POLLERR) { fatal("ttymon[%d]: do_poll: POLLERR on fd %d", getpid(), fdp->fd); } } } } /* * sigchild - handler for SIGCLD * - find the pid of dead child * - clean utmp if U_FLAG is set */ void sigchild(int n __unused) { struct pmtab *pmptr; siginfo_t info; int status; pid_t pid; int rcode; #ifdef DEBUG debug("in sigchild"); #endif /* find all processes that died */ for (;;) { rcode = waitid(P_ALL, 0, &info, WNOHANG|WEXITED); if (rcode == -1 && errno == EINTR) continue; /* If no more children have exited, just return */ if (rcode == -1 || (pid = info.si_pid) == 0) break; /* construct status as returned from waitid() */ status = info.si_status & 0377; switch (info.si_code) { case CLD_EXITED: status <<= 8; break; case CLD_DUMPED: status |= WCOREFLG; break; case CLD_KILLED: break; } if ((pmptr = find_pid(pid)) == NULL) { #ifdef DEBUG log("cannot find dead child (%ld) in pmtab", pid); #endif /* * This may happen if the entry is deleted from pmtab * before the service exits. * We try to cleanup utmp entry */ cleanut(pid, status); } else { if (pmptr->p_flags & U_FLAG) cleanut(pid, status); pmptr->p_status = VALID; pmptr->p_fd = 0; pmptr->p_childpid = 0; pmptr->p_inservice = 0; Retry = TRUE; } } } /* * sigterm - handler for SIGTERM */ void sigterm(int _s __unused) { fatal("caught SIGTERM"); } /* * state_change - this is called when ttymon changes * its internal state between enabled and disabled */ void state_change(void) { struct pmtab *pmptr; #ifdef DEBUG debug("in state_change"); #endif /* * closing PCpipe will cause attached non-service children * to get SIGPOLL and exit */ (void) close(PCpipe[0]); (void) close(PCpipe[1]); /* reopen PCpipe */ setup_PCpipe(); /* * also close all open ports so ttymon can start over * with new internal state */ for (pmptr = PMtab; pmptr; pmptr = pmptr->p_next) { if ((pmptr->p_fd > 0) && (pmptr->p_childpid == 0)) { (void) close(pmptr->p_fd); pmptr->p_fd = 0; } } Retry = TRUE; } /* * re_read - reread pmtab * - kill tmchild if entry changed */ void re_read(void) { sigset_t cset; sigset_t tset; (void) sigprocmask(SIG_SETMASK, NULL, &cset); tset = cset; (void) sigaddset(&tset, SIGCLD); (void) sigprocmask(SIG_SETMASK, &tset, NULL); if (Nlocked > 0) { (void) alarm(0); Nlocked = 0; } read_pmtab(); kill_subprocesses(); (void) sigprocmask(SIG_SETMASK, &cset, NULL); purge(); if (Nentries > Npollfd) { #ifdef DEBUG debug("Nentries > Npollfd, reallocating pollfds"); #endif /* need to malloc more pollfd structure */ free(Pollp); Npollfd = Nentries + 10; if (Npollfd > Maxfds) Npollfd = Maxfds; Pollp = malloc((unsigned)(Npollfd * sizeof (struct pollfd))); if (Pollp == NULL) fatal("malloc for Pollp failed"); } Retry = TRUE; } /* * find_pid(pid) - find the corresponding pmtab entry for the pid */ static struct pmtab * find_pid(pid_t pid) { struct pmtab *pmptr; for (pmptr = PMtab; pmptr; pmptr = pmptr->p_next) { if (pmptr->p_childpid == pid) { return (pmptr); } } return (NULL); } /* * find_fd(fd) - find the corresponding pmtab entry for the fd */ static struct pmtab * find_fd(int fd) { struct pmtab *pmptr; for (pmptr = PMtab; pmptr; pmptr = pmptr->p_next) { if (pmptr->p_fd == fd) { return (pmptr); } } return (NULL); } /* * kill_subprocesses() - if the pmtab entry has been changed, * kill tmchild if it is not in service. * - close the device if there is no tmchild */ static void kill_subprocesses(void) { struct pmtab *pmptr; for (pmptr = PMtab; pmptr; pmptr = pmptr->p_next) { if (pmptr->p_status == VALID) continue; if ((pmptr->p_fd > 0) && (pmptr->p_childpid == 0)) { (void) close(pmptr->p_fd); pmptr->p_fd = 0; } else if ((pmptr->p_fd == 0) && (pmptr->p_childpid > 0) && (pmptr->p_inservice == FALSE)) { (void) kill(pmptr->p_childpid, SIGTERM); } } } static void mark_service(pid_t pid) { struct pmtab *pmptr; #ifdef DEBUG debug("in mark_service"); #endif if ((pmptr = find_pid(pid)) == NULL) { log("mark_service: cannot find child (%ld) in pmtab", pid); return; } pmptr->p_inservice = TRUE; } /* * read_pid(fd) - read pid info from PCpipe */ static void read_pid(int fd) { int ret; pid_t pid; for (;;) { if ((ret = read(fd, &pid, sizeof (pid))) < 0) { if (errno == EINTR) continue; if (errno == EAGAIN) return; fatal("read PCpipe failed: %s", strerror(errno)); } if (ret == 0) return; if (ret != sizeof (pid)) fatal("read return size incorrect, ret = %d", ret); mark_service(pid); } } /* * sipoll_catch() - signal handle of SIGPOLL for ttymon * - it will check both PCpipe and pmpipe */ void sigpoll_catch(int s __unused) { int ret; struct pollfd pfd[2]; #ifdef DEBUG debug("in sigpoll_catch"); #endif pfd[0].fd = PCpipe[0]; pfd[1].fd = Pfd; pfd[0].events = POLLIN; pfd[1].events = POLLIN; if ((ret = poll(pfd, 2, 0)) < 0) fatal("sigpoll_catch: poll failed: %s", strerror(errno)); if (ret > 0) { if (pfd[0].revents & POLLIN) read_pid(pfd[0].fd); if (pfd[1].revents & POLLIN) sacpoll(); } } void sigalarm(int signo __unused) { struct pmtab *pmptr; struct sigaction sigact; int fd; #ifdef DEBUG debug("in sigalarm, Nlocked = %d", Nlocked); #endif for (pmptr = PMtab; pmptr; pmptr = pmptr->p_next) { if ((pmptr->p_status == LOCKED) && (pmptr->p_fd == 0)) { fd = open(pmptr->p_device, O_RDWR | O_NONBLOCK); if (fd == -1) { log("open (%s) failed: %s", pmptr->p_device, strerror(errno)); pmptr->p_status = VALID; Nlocked--; Retry = TRUE; } else { if (tm_checklock(fd) == 0) { Nlocked--; pmptr->p_fd = fd; Retry = TRUE; } else { (void) close(fd); } } } else if ((pmptr->p_status == SESSION) && (pmptr->p_fd == 0)) { fd = open(pmptr->p_device, O_RDWR | O_NONBLOCK); if (fd == -1) { log("open (%s) failed: %s", pmptr->p_device, strerror(errno)); pmptr->p_status = VALID; Nlocked--; Retry = TRUE; } else { if (check_session(fd) == 0) { Nlocked--; pmptr->p_fd = fd; Retry = TRUE; } else { (void) close(fd); } } } else if ((pmptr->p_status == UNACCESS) && (pmptr->p_fd == 0)) { fd = open(pmptr->p_device, O_RDWR | O_NONBLOCK); if (fd == -1) { log("open (%s) failed: %s", pmptr->p_device, strerror(errno)); pmptr->p_status = VALID; Nlocked--; Retry = TRUE; } else { Nlocked--; pmptr->p_fd = fd; Retry = TRUE; } } } if (Nlocked > 0) { sigact.sa_flags = 0; sigact.sa_handler = sigalarm; (void) sigemptyset(&sigact.sa_mask); (void) sigaction(SIGALRM, &sigact, NULL); (void) alarm(ALARMTIME); } else { sigact.sa_flags = 0; sigact.sa_handler = SIG_IGN; (void) sigemptyset(&sigact.sa_mask); (void) sigaction(SIGALRM, &sigact, NULL); } } /* * pcsync_close - For the child process close all open fd's except * the one that is passed to the routine. Coordinate the reads and * writes to the pipes by the parent and child process to ensure * the parent and child processes have closed all the file descriptors * that are not needed any more. */ static void pcsync_close(int *p0, int *p1, int pid, int fd) { char ch; if (pid == 0) { /* Child */ struct pmtab *tp; for (tp = PMtab; tp; tp = tp->p_next) if ((tp->p_fd > 0) && (tp->p_fd != fd)) (void) close(tp->p_fd); (void) close(p0[1]); (void) close(p1[0]); if (read(p0[0], &ch, 1) == 1) (void) write(p1[1], "a", 1); (void) close(p0[0]); (void) close(p1[1]); } else { /* Parent */ (void) close(p0[0]); (void) close(p1[1]); if (write(p0[1], "a", 1) == 1) (void) read(p1[0], &ch, 1); (void) close(p0[1]); (void) close(p1[0]); } } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #include #include #include #include "tmextern.h" /* -------------------------------------------------------- */ /* the follwing are here so we can use routines in ulockf.c */ int Debug = 0; char *Bnptr; /* dummies for using uucp .o routines */ /*VARARGS*/ /*ARGSUSED*/ void assert(char *s1, char *s2, int i1, char *s3, int i2) { } void cleanup(void) { } void logent(char *s1 __unused, char *s2 __unused) { /* so we can load ulockf() */ } /* * lastname - If the path name starts with "/dev/", * return the rest of the string. * - Otherwise, return the last token of the path name */ char * lastname(char *name) { char *sp, *p; sp = name; if (strncmp(sp, "/dev/", 5) == 0) sp += 5; else while ((p = (char *)strchr(sp, '/')) != NULL) { sp = ++p; } return (sp); } /* * tm_lock(fd) - set advisory lock on the device */ int tm_lock(int fd) { return (fd_mklock(fd)); } /* * tm_checklock - check if advisory lock is on */ int tm_checklock(int fd) { return (fd_cklock(fd)); } /* * check_session(fd) - check if a session established on fd * return 1 if session exists, otherwise, return 0. * */ int check_session(int fd) { pid_t sid; if (ioctl(fd, TIOCGSID, &sid) == -1) return (0); else if (sid == 0) return (0); else return (1); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * error/logging/cleanup functions for ttymon. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ttymon.h" #include "tmstruct.h" #include "tmextern.h" const char *appname = "ttymon"; void openttymonlog(void) { int fd, ret; char logfile[MAXPATHLEN]; /* the log file resides in /var/saf/pmtag/ */ (void) snprintf(logfile, sizeof (logfile), "%s%s/%s", LOGDIR, Tag, LOGFILE); Logfp = NULL; (void) close(0); if ((fd = open(logfile, O_WRONLY | O_CREAT | O_APPEND, 0444)) != -1) if ((ret = fcntl(fd, F_DUPFD, 3)) == 3) { /* set close-on-exec flag */ if (fcntl(ret, F_SETFD, FD_CLOEXEC) == 0) { Logfp = fdopen(ret, "a+"); } } if (!Logfp) { cons_printf("ttymon cannot create log file \"%s\": %s\n", logfile, strerror(errno)); exit(1); } log(" "); log("********** ttymon starting **********"); #ifdef DEBUG log("fd(log)\t = %d", fileno(Logfp)); #endif } static void roll_log(void) { char logf[MAXPATHLEN]; char ologf[MAXPATHLEN]; char tlogf[MAXPATHLEN]; FILE *nlogfp; struct stat buf; (void) fprintf(Logfp, "Restarting log file\n"); (void) snprintf(logf, sizeof (logf), "%s%s/%s", LOGDIR, Tag, LOGFILE); (void) snprintf(ologf, sizeof (ologf), "%s%s/%s", LOGDIR, Tag, OLOGFILE); (void) snprintf(tlogf, sizeof (tlogf), "%s%s/%s", LOGDIR, Tag, TLOGFILE); if (!stat(ologf, &buf) && rename(ologf, tlogf)) { (void) fprintf(Logfp, "rename old to tmp file failed\n"); } else if (!stat(logf, &buf) && rename(logf, ologf)) { (void) fprintf(Logfp, "rename log to old file failed\n"); /* Restore old log file */ if (!stat(tlogf, &buf) && rename(tlogf, ologf)) (void) fprintf(Logfp, "rename tmp to old file failed\n"); } else if ((nlogfp = fopen(logf, "w")) != NULL) { (void) fclose(Logfp); Logfp = nlogfp; /* reset close-on-exec */ (void) fcntl(fileno(Logfp), F_SETFD, 1); } else { (void) fprintf(Logfp, "log file open failed\n"); /* Restore current and old log file */ if (!stat(ologf, &buf) && rename(ologf, logf)) (void) fprintf(Logfp, "rename old to log file failed\n"); else if (!stat(tlogf, &buf) && rename(tlogf, ologf)) (void) fprintf(Logfp, "rename tmp to old file failed\n"); } (void) unlink(tlogf); /* remove any stale tmp logfile */ } /* * vlog(msg) - common message routine. * - if Logfp is NULL, write message to stderr or CONSOLE */ static void vlog(const char *fmt, va_list ap) { char *timestamp; /* current time in readable form */ time_t clock; /* current time in seconds */ int fd; struct stat buf; if (Logfp) { if ((fstat(fileno(Logfp), &buf) != -1) && (buf.st_size >= Logmaxsz) && !Splflag) { Splflag = 1; roll_log(); Splflag = 0; } (void) time(&clock); timestamp = ctime(&clock); *(strchr(timestamp, '\n')) = '\0'; (void) fprintf(Logfp, "%s; %ld; ", timestamp, getpid()); (void) vfprintf(Logfp, fmt, ap); if (fmt[strlen(fmt) - 1] != '\n') (void) fputc('\n', Logfp); (void) fflush(Logfp); } else if (isatty(STDERR_FILENO)) { (void) fprintf(stderr, "%s: ", appname); (void) vfprintf(stderr, fmt, ap); if (fmt[strlen(fmt) - 1] != '\n') (void) fputc('\n', stderr); (void) fflush(stderr); } else if ((fd = open(CONSOLE, O_WRONLY|O_NOCTTY)) != -1) { FILE *f = fdopen(fd, "w"); (void) fprintf(f, "%s: ", appname); (void) vfprintf(f, fmt, ap); if (fmt[strlen(fmt) - 1] != '\n') (void) fputc('\n', f); (void) fclose(f); } else { vsyslog(LOG_CRIT, fmt, ap); } } /* * log(fmt, ...) - put a message into the log file * - if Logfp is NULL, write message to stderr or CONSOLE */ /*PRINTFLIKE1*/ void log(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vlog(fmt, ap); va_end(ap); } /* * fatal(fmt, ...) - put a message into the log file, then exit. */ /*PRINTFLIKE1*/ void fatal(const char *fmt, ...) { if (fmt) { va_list ap; va_start(ap, fmt); vlog(fmt, ap); va_end(ap); } log("********** ttymon exiting ***********"); exit(1); } #ifdef DEBUG /* * opendebug - open debugging file, sets global file pointer Debugfp * arg: getty - if TRUE, ttymon is in getty_mode and use a different * debug file */ void opendebug(int getty_mode) { int fd, ret; char debugfile[BUFSIZ]; if (!getty_mode) { (void) strcpy(debugfile, LOGDIR); (void) strcat(debugfile, Tag); (void) strcat(debugfile, "/"); (void) strcat(debugfile, DBGFILE); if ((Debugfp = fopen(debugfile, "a+")) == NULL) fatal("open debug file failed"); } else { if ((fd = open(EX_DBG, O_WRONLY|O_APPEND|O_CREAT)) < 0) fatal("open %s failed: %s", EX_DBG, errno); if (fd >= 3) { ret = fd; } else { if ((ret = fcntl(fd, F_DUPFD, 3)) < 0) fatal("F_DUPFD fcntl failed: %s", strerror(errno)); } if ((Debugfp = fdopen(ret, "a+")) == NULL) fatal("fdopen failed: %s", strerror(errno)); if (ret != fd) (void) close(fd); } /* set close-on-exec flag */ if (fcntl(fileno(Debugfp), F_SETFD, 1) == -1) fatal("F_SETFD fcntl failed: %s", strerror(errno)); } /* * debug(msg) - put a message into debug file */ void debug(const char *fmt, ...) { va_list ap; char *timestamp; /* current time in readable form */ time_t clock; /* current time in seconds */ (void) time(&clock); timestamp = ctime(&clock); *(strchr(timestamp, '\n')) = '\0'; (void) fprintf(Debugfp, "%s; %ld; ", timestamp, getpid()); va_start(ap, fmt); (void) vfprintf(Debugfp, fmt, ap); va_end(ap); (void) fprintf(Debugfp, "\n"); (void) fflush(Debugfp); } #endif /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #include #include char quoted(char *, int *); /* * getword - extract one token from the string * char *ptr; pointer to the string to be scanned * int *size; *size = number of characters scanned * int getall; if TRUE, get all char until ':' or '\0' * - token delimiter is white space if getall is FALSE * - token delimiter is ':' or '\0' if getall is TRUE */ char * getword(char *ptr, int *size, int getall) { char *optr, c; static char word[BUFSIZ]; int qsize; *size = 0; if (!getall) { /* Skip all white spaces */ while (isspace(*ptr)) { (*size)++; ptr++; } } /* Put all characters from here to next white space or ':' or '\0' */ /* into the word, up to the size of the word. */ for (optr = word, *optr = '\0'; *ptr != '\0' && *ptr != ':'; ptr++, (*size)++) { if (!getall) { if (isspace(*ptr)) break; } /* If the character is quoted, analyze it. */ if (*ptr == '\\') { c = quoted(ptr, &qsize); (*size) += qsize; ptr += qsize; } else c = *ptr; /* If there is room, add this character to the word. */ if (optr < &word[BUFSIZ]) *optr++ = c; } /* skip trailing blanks if any */ while (isspace(*ptr)) { (*size)++; ptr++; } /* Make sure the line is null terminated. */ *optr++ = '\0'; return (word); } /* "quoted" takes a quoted character, starting at the quote */ /* character, and returns a single character plus the size of */ /* the quote string. "quoted" recognizes the following as */ /* special, \n,\r,\v,\t,\b,\f as well as the \nnn notation. */ char quoted(char *ptr, int *qsize) { char c, *rptr; int i; rptr = ptr; switch (*++rptr) { case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 'v': c = '\013'; break; case 'b': c = '\b'; break; case 't': c = '\t'; break; case 'f': c = '\f'; break; case ':': c = ':'; break; default: /* If this is a numeric string, take up to three characters of */ /* it as the value of the quoted character. */ if (*rptr >= '0' && *rptr <= '7') { for (i = 0, c = 0; i < 3; i++) { c = c * 8 + (*rptr - '0'); if (*++rptr < '0' || *rptr > '7') break; } rptr--; /* If the character following the '\\' is a NULL, back up the */ /* ptr so that the NULL won't be missed. The sequence */ /* backslash null is essentually illegal. */ } else if (*rptr == '\0') { c = '\0'; rptr--; /* In all other cases the quoting does nothing. */ } else { c = *rptr; } break; } /* Compute the size of the quoted character. */ (*qsize) = rptr - ptr; return (c); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #include #include #include #include #include #include #include #include #include #include "ttymon.h" #include "tmstruct.h" #include "tmextern.h" #define BRK 1 #define DEL 2 struct strbuf *do_peek(int, int); static int process(int, struct strbuf *); static int interrupt; /* * poll_data - it polls the device, waiting for data * - return BADSPEED it is received * - return the result of process if data is received * - write a newline if is received * - exit if hangup is received */ int poll_data(void) { int j; struct strbuf *ptr; struct pollfd fds[1]; struct sigaction sigact; #ifdef DEBUG debug("in poll_data"); #endif if (peek_ptr != NULL) { for (j = 0; j < peek_ptr->len; j++) peek_ptr->buf[j] &= 0x7F; return (process(0, peek_ptr)); } fds[0].fd = 0; fds[0].events = POLLIN; sigact.sa_flags = 0; sigact.sa_handler = sigint; (void) sigemptyset(&sigact.sa_mask); (void) sigaddset(&sigact.sa_mask, SIGINT); (void) sigaction(SIGINT, &sigact, NULL); for (;;) { interrupt = 0; if ((j = poll(fds, 1, -1)) == -1) { if (interrupt == BRK) { return (BADSPEED); } if (interrupt == DEL) { /* XXX revisit kmd */ return (BADSPEED); } } else if (j > 0) { if (fds[0].revents & POLLHUP) { log("POLLHUP received, about to exit"); exit(1); } if (fds[0].revents & POLLIN) { ptr = do_peek(fds[0].fd, 255); if (ptr != NULL) { return (process(fds[0].fd, ptr)); } } } } } /* * process - process the data * - return GOODNAME if it is a non-empty line * - return NONAME if a is received * - return BADNAME if zero byte is detected * - except the case of GOODNAME, data will be pulled out * of the stream */ static int process( int fd, /* fd to read data from if necessary */ struct strbuf *ptr) /* ptr that holds data in ptr->buf */ { unsigned i; char junk[BUFSIZ]; for (i = 0; i < ptr->len; i++) { if (ptr->buf[i] == '\0') { (void) read(fd, junk, i+1); return (BADSPEED); } else if ((ptr->buf[i] == '\n') || (ptr->buf[i] == '\r')) { if (i == 0) { (void) read(fd, junk, ptr->len); return (NONAME); } else return (GOODNAME); } } /* end for loop */ /* end of input is encountered */ #ifdef DEBUG debug("in process: EOF encountered"); #endif exit(1); /*NOTREACHED*/ } /* * do_peek - peek at the stream to get the data * int fd; fd to do the ioctl on * int n; maxlen of data to peek at * - this only called when POLLIN is detected, * - so there should always be something there * - return a ptr to the buf that contains the data * - return NULL if nothing to peek at */ struct strbuf * do_peek(int fd, int n) { int ret; static struct strpeek peek; struct strpeek *peekp; static char buf[BUFSIZ]; #ifdef DEBUG debug("in do_peek"); #endif peekp = &peek; peekp->flags = 0; /* need to ask for ctl info to avoid bug in I_PEEK code */ peekp->ctlbuf.maxlen = 1; peekp->ctlbuf.buf = buf; peekp->databuf.maxlen = n; peekp->databuf.buf = buf; ret = ioctl(fd, I_PEEK, &peek); if (ret == -1) { log("do_peek: I_PEEK failed: %s", errno); exit(1); } if (ret == 0) { return (NULL); } return (&(peekp->databuf)); } /* * sigint - this is called when SIGINT is caught */ void sigint(int s __unused) { struct strbuf *ptr; char junk[2]; #ifdef DEBUG debug("in sigint"); #endif ptr = do_peek(0, 1); if (ptr == NULL) { /* somebody type */ interrupt = DEL; } else { if (ptr->buf[0] == '\0') { /* somebody type or frame error */ (void) read(0, junk, 1); interrupt = BRK; } } } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #include #include #include #include #include #include #include #include #include "ttymon.h" #include "tmstruct.h" #include "tmextern.h" static int get_flags(char *, long *); static int get_ttyflags(char *, long *); static int same_entry(struct pmtab *, struct pmtab *); static int check_pmtab(struct pmtab *); static void insert_pmtab(struct pmtab *); static void free_pmtab(struct pmtab *); static char *expand(char *, char *); int check_identity(struct pmtab *); /* * read_pmtab() * - read and parse pmtab * - store table in linked list pointed by global variable "PMtab" * - exit if file does not exist or error detected. */ void read_pmtab(void) { struct pmtab *gptr; char *ptr, *wptr; FILE *fp; int input, state, size, rawc, field, linenum; char oldc; char line[BUFSIZ]; char wbuf[BUFSIZ]; static char *states[] = { "", "tag", "flags", "identity", "reserved1", "reserved2", "reserved3", "device", "ttyflags", "count", "service", "timeout", "ttylabel", "modules", "prompt", "disable msg", "terminal type", "soft-carrier" }; #ifdef DEBUG debug("in read_pmtab"); #endif if ((fp = fopen(PMTABFILE, "r")) == NULL) { fatal("open pmtab (%s) failed", PMTABFILE); } Nentries = 0; if (check_version(PMTAB_VERS, PMTABFILE) != 0) fatal("check pmtab version failed"); for (gptr = PMtab; gptr; gptr = gptr->p_next) { if ((gptr->p_status == SESSION) || (gptr->p_status == LOCKED) || (gptr->p_status == UNACCESS)) { if (gptr->p_fd > 0) { (void) close(gptr->p_fd); gptr->p_fd = 0; } gptr->p_inservice = gptr->p_status; } gptr->p_status = NOTVALID; } wptr = wbuf; input = ACTIVE; linenum = 0; field = FAILURE; do { linenum++; line[0] = '\0'; for (ptr = line, oldc = '\0'; ptr < &line[sizeof (line) - 1] && (rawc = getc(fp)) != '\n' && rawc != EOF; ptr++, oldc = (char)rawc) { if ((rawc == '#') && (oldc != '\\')) break; *ptr = (char)rawc; } *ptr = '\0'; /* skip rest of the line */ if (rawc != EOF && rawc != '\n') { if (rawc != '#') log("Entry too long.\n"); while ((rawc = getc(fp)) != EOF && rawc != '\n') ; } if (rawc == EOF) { if (ptr == line) break; else input = FINISHED; } /* if empty line, skip */ for (ptr = line; *ptr != '\0' && isspace(*ptr); ptr++) ; if (*ptr == '\0') continue; #ifdef DEBUG debug("**** Next Entry ****\n%s", line); #endif log("Processing pmtab line #%d", linenum); /* Now we have the complete line */ if ((gptr = ALLOC_PMTAB) == NULL) fatal("memory allocation failed"); /* set hangup flag, this is the default */ gptr->p_ttyflags |= H_FLAG; /* * For compatibility reasons, we cannot rely on these * having values assigned from pmtab. */ gptr->p_termtype = ""; gptr->p_softcar = ""; for (state = P_TAG, ptr = line; state != FAILURE && state != SUCCESS;) { switch (state) { case P_TAG: gptr->p_tag = strsave(getword(ptr, &size, 0)); break; case P_FLAGS: (void) strcpy(wptr, getword(ptr, &size, 0)); if ((get_flags(wptr, &gptr->p_flags)) != 0) { field = state; state = FAILURE; } break; case P_IDENTITY: gptr->p_identity = strsave( getword(ptr, &size, 0)); break; case P_RES1: gptr->p_res1 = strsave(getword(ptr, &size, 0)); break; case P_RES2: gptr->p_res2 = strsave(getword(ptr, &size, 0)); break; case P_RES3: gptr->p_res3 = strsave(getword(ptr, &size, 0)); break; case P_DEVICE: gptr->p_device = strsave( getword(ptr, &size, 0)); break; case P_TTYFLAGS: (void) strcpy(wptr, getword(ptr, &size, 0)); if (get_ttyflags(wptr, &gptr->p_ttyflags) != 0) { field = state; state = FAILURE; } break; case P_COUNT: (void) strcpy(wptr, getword(ptr, &size, 0)); if (strcheck(wptr, NUM) != 0) { log("wait_read count must be a " "positive number"); field = state; state = FAILURE; } else { gptr->p_count = atoi(wptr); } break; case P_SERVER: gptr->p_server = strsave(expand(getword(ptr, &size, 1), gptr->p_device)); break; case P_TIMEOUT: (void) strcpy(wptr, getword(ptr, &size, 0)); if (strcheck(wptr, NUM) != 0) { log("timeout value must be a positive " "number"); field = state; state = FAILURE; } else { gptr->p_timeout = atoi(wptr); } break; case P_TTYLABEL: gptr->p_ttylabel = strsave(getword(ptr, &size, 0)); break; case P_MODULES: gptr->p_modules = strsave(getword(ptr, &size, 0)); if (vml(gptr->p_modules) != 0) { field = state; state = FAILURE; } break; case P_PROMPT: gptr->p_prompt = strsave(getword(ptr, &size, TRUE)); break; case P_DMSG: gptr->p_dmsg = strsave(getword(ptr, &size, TRUE)); break; case P_TERMTYPE: gptr->p_termtype = strsave(getword(ptr, &size, TRUE)); break; case P_SOFTCAR: gptr->p_softcar = strsave(getword(ptr, &size, TRUE)); break; } /* end switch */ ptr += size; if (state == FAILURE) break; if (*ptr == ':') { ptr++; /* Skip the ':' */ state++; } else if (*ptr != '\0') { field = state; state = FAILURE; } if (*ptr == '\0') { /* * Maintain compatibility with older ttymon * pmtab files. If Sun-added fields are * missing, this should not be an error. */ if (state > P_DMSG) { state = SUCCESS; } else { field = state; state = FAILURE; } } } /* end for loop */ if (state == SUCCESS) { if (check_pmtab(gptr) == 0) { if (Nentries < Maxfds) { insert_pmtab(gptr); } else { log("can't add more entries to " "pmtab, Maxfds = %d", Maxfds); free_pmtab(gptr); (void) fclose(fp); return; } } else { log("Parsing failure for entry: \n%s", line); log("----------------------------------------" "---"); free_pmtab(gptr); } } else { *++ptr = '\0'; log("Parsing failure in the \"%s\" field,\n%s" "<--error detected here", states[field], line); log("-------------------------------------------"); free_pmtab(gptr); } } while (input == ACTIVE); (void) fclose(fp); } /* * get_flags - scan flags field to set U_FLAG and X_FLAG */ static int get_flags(char *wptr, long *flags) { char *p; for (p = wptr; *p; p++) { switch (*p) { case 'x': *flags |= X_FLAG; break; case 'u': *flags |= U_FLAG; break; default: log("Invalid flag -- %c", *p); return (-1); } } return (0); } /* * get_ttyflags - scan ttyflags field to set corresponding flags * char *wptr pointer to the input string * long *ttyflags pointer to the flag to be set */ static int get_ttyflags(char *wptr, long *ttyflags) { char *p; for (p = wptr; *p; p++) { switch (*p) { case 'c': *ttyflags |= C_FLAG; break; case 'h': /* h means don't hangup */ *ttyflags &= ~H_FLAG; break; case 'b': *ttyflags |= B_FLAG; break; case 'r': *ttyflags |= R_FLAG; break; case 'I': *ttyflags |= I_FLAG; break; default: log("Invalid ttyflag -- %c", *p); return (-1); } } return (0); } #ifdef DEBUG /* * pflags - put service flags into intelligible form for output * long flags - binary representation of the flags */ char * pflags(long flags) { int i; /* scratch counter */ static char buf[BUFSIZ]; /* formatted flags */ if (flags == 0) return ("-"); i = 0; if (flags & U_FLAG) { buf[i++] = 'u'; flags &= ~U_FLAG; } if (flags & X_FLAG) { buf[i++] = 'x'; flags &= ~X_FLAG; } if (flags) log("Internal error in pflags"); buf[i] = '\0'; return (buf); } /* * pttyflags - put ttyflags into intelligible form for output * long flags - binary representation of ttyflags */ char * pttyflags(long flags) { int i; /* scratch counter */ static char buf[BUFSIZ]; /* formatted flags */ if (flags == 0) return ("h"); i = 0; if (flags & C_FLAG) { buf[i++] = 'c'; flags &= ~C_FLAG; } if (flags & H_FLAG) flags &= ~H_FLAG; else buf[i++] = 'h'; if (flags & B_FLAG) { buf[i++] = 'b'; flags &= ~B_FLAG; } if (flags & R_FLAG) { buf[i++] = 'r'; flags &= ~B_FLAG; } if (flags & I_FLAG) { buf[i++] = 'I'; flags &= ~I_FLAG; } if (flags) log("Internal error in p_ttyflags"); buf[i] = '\0'; return (buf); } void dump_pmtab(void) { struct pmtab *gptr; debug("in dump_pmtab"); log("********** dumping pmtab **********"); log(" "); for (gptr = PMtab; gptr != NULL; gptr = gptr->p_next) { log("-------------------------------------------"); log("tag:\t\t%s", gptr->p_tag); log("flags:\t\t%s", pflags(gptr->p_flags)); log("identity:\t%s", gptr->p_identity); log("reserved1:\t%s", gptr->p_res1); log("reserved2:\t%s", gptr->p_res2); log("reserved3:\t%s", gptr->p_res3); log("device:\t%s", gptr->p_device); log("ttyflags:\t%s", pttyflags(gptr->p_ttyflags)); log("count:\t\t%d", gptr->p_count); log("server:\t%s", gptr->p_server); log("timeout:\t%d", gptr->p_timeout); log("ttylabel:\t%s", gptr->p_ttylabel); log("modules:\t%s", gptr->p_modules); log("prompt:\t%s", gptr->p_prompt); log("disable msg:\t%s", gptr->p_dmsg); log("terminal type:\t%s", gptr->p_termtype); log("soft-carrier:\t%s", gptr->p_softcar); log("status:\t\t%d", gptr->p_status); log("inservice:\t%d", gptr->p_inservice); log("fd:\t\t%d", gptr->p_fd); log("pid:\t\t%ld", gptr->p_childpid); log("uid:\t\t%ld", gptr->p_uid); log("gid:\t\t%ld", gptr->p_gid); log("dir:\t%s", gptr->p_dir); log(" "); } log("********** end dumping pmtab **********"); } #endif /* * same_entry(e1,e2) - compare 2 entries of pmtab * if the fields are different, copy e2 to e1 * return 1 if same, return 0 if different */ static int same_entry(struct pmtab *e1, struct pmtab *e2) { if (strcmp(e1->p_identity, e2->p_identity) != 0) return (0); if (strcmp(e1->p_res1, e2->p_res1) != 0) return (0); if (strcmp(e1->p_res2, e2->p_res2) != 0) return (0); if (strcmp(e1->p_res3, e2->p_res3) != 0) return (0); if (strcmp(e1->p_device, e2->p_device) != 0) return (0); if (strcmp(e1->p_server, e2->p_server) != 0) return (0); if (strcmp(e1->p_ttylabel, e2->p_ttylabel) != 0) return (0); if (strcmp(e1->p_modules, e2->p_modules) != 0) return (0); if (strcmp(e1->p_prompt, e2->p_prompt) != 0) return (0); if (strcmp(e1->p_dmsg, e2->p_dmsg) != 0) return (0); if (strcmp(e1->p_termtype, e2->p_termtype) != 0) return (0); if (strcmp(e1->p_softcar, e2->p_softcar) != 0) return (0); if (e1->p_flags != e2->p_flags) return (0); /* * compare lowest 4 bits only, * because A_FLAG is not part of original ttyflags */ if ((e1->p_ttyflags & ~A_FLAG) != (e2->p_ttyflags & ~A_FLAG)) return (0); if (e1->p_count != e2->p_count) return (0); if (e1->p_timeout != e2->p_timeout) return (0); if (e1->p_uid != e2->p_uid) return (0); if (e1->p_gid != e2->p_gid) return (0); if (strcmp(e1->p_dir, e2->p_dir) != 0) return (0); return (1); } /* * insert_pmtab - insert a pmtab entry into the linked list */ static void insert_pmtab(struct pmtab *sp) { struct pmtab *tsp, *savtsp; /* scratch pointers */ int ret; /* strcmp return value */ #ifdef DEBUG debug("in insert_pmtab"); #endif savtsp = tsp = PMtab; /* * find the correct place to insert this element */ while (tsp) { ret = strcmp(sp->p_tag, tsp->p_tag); if (ret > 0) { /* keep on looking */ savtsp = tsp; tsp = tsp->p_next; continue; } else if (ret == 0) { if (tsp->p_status) { /* this is a duplicate entry, ignore it */ log("Ignoring duplicate entry for <%s>", tsp->p_tag); } else { if (same_entry(tsp, sp)) { /* same entry */ tsp->p_status = VALID; } else { /* entry changed */ if ((sp->p_flags & X_FLAG) && ((sp->p_dmsg == NULL) || (*(sp->p_dmsg) == '\0'))) { /* disabled entry */ tsp->p_status = NOTVALID; } else { #ifdef DEBUG debug("replacing <%s>", sp->p_tag); #endif /* replace old entry */ sp->p_next = tsp->p_next; if (tsp == PMtab) { PMtab = sp; } else { savtsp->p_next = sp; } sp->p_status = CHANGED; sp->p_fd = tsp->p_fd; sp->p_childpid = tsp->p_childpid; sp->p_inservice = tsp->p_inservice; sp = tsp; } } Nentries++; } free_pmtab(sp); return; } else { if ((sp->p_flags & X_FLAG) && ((sp->p_dmsg == NULL) || (*(sp->p_dmsg) == '\0'))) { /* disabled entry */ free_pmtab(sp); return; } /* * Set the state of soft-carrier. * Since this is a one-time only operation, * we do it when this service is added to * the enabled list. */ if (*sp->p_softcar != '\0') set_softcar(sp); /* insert it here */ if (tsp == PMtab) { sp->p_next = PMtab; PMtab = sp; } else { sp->p_next = savtsp->p_next; savtsp->p_next = sp; } #ifdef DEBUG debug("adding <%s>", sp->p_tag); #endif Nentries++; /* this entry is "current" */ sp->p_status = VALID; return; } } /* * either an empty list or should put element at end of list */ if ((sp->p_flags & X_FLAG) && ((sp->p_dmsg == NULL) || (*(sp->p_dmsg) == '\0'))) { /* disabled entry */ free_pmtab(sp); /* do not poll this entry */ return; } /* * Set the state of soft-carrier. * Since this is a one-time only operation, * we do it when this service is added to * the enabled list. */ if (*sp->p_softcar != '\0') set_softcar(sp); sp->p_next = NULL; if (PMtab == NULL) PMtab = sp; else savtsp->p_next = sp; #ifdef DEBUG debug("adding <%s>", sp->p_tag); #endif ++Nentries; /* this entry is "current" */ sp->p_status = VALID; } /* * purge - purge linked list of "old" entries */ void purge(void) { struct pmtab *sp; /* working pointer */ struct pmtab *savesp, *tsp; /* scratch pointers */ #ifdef DEBUG debug("in purge"); #endif sp = savesp = PMtab; while (sp) { if (sp->p_status) { #ifdef DEBUG debug("p_status not 0"); #endif savesp = sp; sp = sp->p_next; } else { tsp = sp; if (tsp == PMtab) { PMtab = sp->p_next; savesp = PMtab; } else { savesp->p_next = sp->p_next; } #ifdef DEBUG debug("purging <%s>", sp->p_tag); #endif sp = sp->p_next; free_pmtab(tsp); } } } /* * free_pmtab - free one pmtab entry */ static void free_pmtab(struct pmtab *p) { #ifdef DEBUG debug("in free_pmtab"); #endif free(p->p_tag); free(p->p_identity); free(p->p_res1); free(p->p_res2); free(p->p_res3); free(p->p_device); free(p->p_server); free(p->p_ttylabel); free(p->p_modules); free(p->p_prompt); free(p->p_dmsg); free(p->p_termtype); free(p->p_softcar); free(p->p_dir); free(p); } /* * check_pmtab - check the fields to make sure things are correct * - return 0 if everything is ok * - return -1 if something is wrong */ static int check_pmtab(struct pmtab *p) { if (p == NULL) { log("pmtab ptr is NULL"); return (-1); } /* check service tag */ if ((p->p_tag == NULL) || (*(p->p_tag) == '\0')) { log("port/service tag is missing"); return (-1); } if (strlen(p->p_tag) > (size_t)(MAXID - 1)) { log("port/service tag <%s> is longer than %d", p->p_tag, MAXID-1); return (-1); } if (strcheck(p->p_tag, ALNUM) != 0) { log("port/service tag <%s> is not alphanumeric", p->p_tag); return (-1); } if (check_identity(p) != 0) { return (-1); } if (check_device(p->p_device) != 0) return (-1); if (check_cmd(p->p_server) != 0) return (-1); return (0); } /* * check_identity - check to see if the identity is a valid user * - log name in the passwd file, * - and if its group id is a valid one * - return 0 if everything is ok. Otherwise, return -1 */ int check_identity(struct pmtab *p) { struct passwd *pwdp; if ((p->p_identity == NULL) || (*(p->p_identity) == '\0')) { log("identity field is missing"); return (-1); } if ((pwdp = getpwnam(p->p_identity)) == NULL) { log("missing or bad passwd entry for <%s>", p->p_identity); endpwent(); return (-1); } if (getgrgid(pwdp->pw_gid) == NULL) { log("no group entry for %ld", pwdp->pw_gid); endgrent(); endpwent(); return (-1); } p->p_uid = pwdp->pw_uid; p->p_gid = pwdp->pw_gid; p->p_dir = strsave(pwdp->pw_dir); endgrent(); endpwent(); return (0); } /* * expand(cmdp, devp) - expand %d to device name and %% to %, * - any other characters are untouched. * - return the expanded string */ static char * expand(char *cmdp, char *devp) { char *cp, *dp, *np; static char buf[BUFSIZ]; cp = cmdp; np = buf; dp = devp; while (*cp) { if (*cp != '%') { *np++ = *cp++; continue; } switch (*++cp) { case 'd': while (*dp) { *np++ = *dp++; } cp++; break; case '%': *np++ = *cp++; break; default: *np++ = *cp++; break; } } *np = '\0'; return (buf); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #include #include #include #include #include #include #include #include "ttymon.h" #include "tmextern.h" #include "sac.h" /* * openpid - open the pid and put ttymon's pid in it * - put an advisory lock on the file * - to prevent another instance of ttymon in same directory * - SAC also makes use of the lock * - fd 0 is reserved for pid file */ void openpid(void) { char lockbuf[16]; /* large enough for a PID string */ (void) close(0); /* open for read first, otherwise, may delete the pid already there */ if ((Lckfd = open(PIDFILE, O_RDONLY)) != -1) { if (lockf(Lckfd, F_TEST, 0L) == -1) fatal("pid file is locked. ttymon may already be " "running!"); (void) close(Lckfd); } if ((Lckfd = open(PIDFILE, O_WRONLY|O_CREAT|O_TRUNC, 0644)) != 0) fatal("open pid file failed: %s", strerror(errno)); if (lockf(Lckfd, F_LOCK, 0L) == -1) fatal("lock pid file failed: %s", strerror(errno)); (void) snprintf(lockbuf, sizeof (lockbuf), "%ld", getpid()); (void) write(Lckfd, lockbuf, strlen(lockbuf) + 1); #ifdef DEBUG log("fd(pid)\t = %d", Lckfd); #endif } /* * openpipes() -- open pmpipe and sacpipe to communicate with SAC * -- Pfd, Sfd are global file descriptors for pmpipe, sacpipe */ void openpipes(void) { Sfd = open(SACPIPE, O_WRONLY); if (Sfd < 0) fatal("open sacpipe failed: %s", strerror(errno)); Pfd = open(PMPIPE, O_RDWR|O_NONBLOCK); if (Pfd < 0) fatal("open pmpipe failed: %s", strerror(errno)); #ifdef DEBUG log("fd(sacpipe)\t = %d", Sfd); log("fd(pmpipe)\t = %d", Pfd); #endif } /* * remove_env(env) - remove an environment variable from the environment */ static void remove_env(char *env) { char **p; char **rp = NULL; p = environ; if (p == NULL) return; while (*p) { if (strncmp(*p, env, strlen(env)) == 0) rp = p; p++; } if (rp) { *rp = *--p; *p = NULL; } } /* * get_environ() -- get env variables PMTAG, ISTATE * -- set global variables Tag, State */ void get_environ(void) { if ((Tag = getenv("PMTAG")) == NULL) fatal("PMTAG is missing"); if ((Istate = getenv("ISTATE")) == NULL) fatal("ISTATE is missing"); State = (strcmp(Istate, "enabled") == 0) ? PM_ENABLED : PM_DISABLED; /* * remove the environment variables so they will not * be passed to the children */ remove_env("ISTATE"); remove_env("PMTAG"); } /* * sacpoll - the event handler when sac event is posted */ void sacpoll(void) { int ret; char oldState; struct sacmsg sacmsg; struct pmmsg pmmsg; sigset_t cset; sigset_t tset; #ifdef DEBUG debug("in sacpoll"); #endif /* we don't want to be interrupted by sigchild now */ (void) sigprocmask(SIG_SETMASK, NULL, &cset); tset = cset; (void) sigaddset(&tset, SIGCLD); (void) sigprocmask(SIG_SETMASK, &tset, NULL); /* * read sac messages, one at a time until no message * is left on the pipe. * the pipe is open with O_NONBLOCK, read will return -1 * and errno = EAGAIN if nothing is on the pipe */ for (;;) { ret = read(Pfd, &sacmsg, sizeof (sacmsg)); if (ret < 0) { switch (errno) { case EAGAIN: /* no more data on the pipe */ (void) sigprocmask(SIG_SETMASK, &cset, NULL); return; case EINTR: break; default: fatal("pmpipe read failed: %s", strerror(errno)); break; /*NOTREACHED*/ } } else if (ret == 0) { /* no more data on the pipe */ (void) sigprocmask(SIG_SETMASK, &cset, NULL); return; } else { pmmsg.pm_size = 0; (void) strcpy(pmmsg.pm_tag, Tag); pmmsg.pm_maxclass = TM_MAXCLASS; pmmsg.pm_type = PM_STATUS; switch (sacmsg.sc_type) { case SC_STATUS: break; case SC_ENABLE: log("Got SC_ENABLE message"); oldState = State; State = PM_ENABLED; if (State != oldState) { #ifdef DEBUG debug("state changed to ENABLED"); #endif state_change(); } break; case SC_DISABLE: log("Got SC_DISABLE message"); oldState = State; State = PM_DISABLED; if (State != oldState) { #ifdef DEBUG debug("state changed to DISABLED"); #endif state_change(); } break; case SC_READDB: log("Got SC_READDB message"); Reread_flag = 1; break; default: log("Got unknown message %d", sacmsg.sc_type); pmmsg.pm_type = PM_UNKNOWN; break; } /* end switch */ pmmsg.pm_state = State; while (write(Sfd, &pmmsg, sizeof (pmmsg)) != sizeof (pmmsg)) { if (errno == EINTR) continue; log("sanity response to SAC failed: %s", strerror(errno)); break; } } } /* end for loop */ } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #include #include #include "tmextern.h" /* * catch_signals: * ttymon catch some signals and ignore the rest. * * SIGTERM - killed by somebody * SIGPOLL - got message on pmpipe, probably from sac * or on PCpipe * SIGCLD - tmchild died */ void catch_signals(void) { sigset_t cset; struct sigaction sigact; #ifdef DEBUG debug("in catch_signals"); #endif cset = Origmask; (void) sigdelset(&cset, SIGTERM); (void) sigdelset(&cset, SIGCLD); (void) sigdelset(&cset, SIGPOLL); #ifdef DEBUG (void) sigdelset(&cset, SIGUSR1); (void) sigdelset(&cset, SIGUSR2); #endif (void) sigprocmask(SIG_SETMASK, &cset, NULL); sigact.sa_flags = 0; sigact.sa_handler = sigterm; (void) sigemptyset(&sigact.sa_mask); (void) sigaddset(&sigact.sa_mask, SIGTERM); (void) sigaction(SIGTERM, &sigact, NULL); sigact.sa_flags = 0; sigact.sa_handler = sigchild; (void) sigemptyset(&sigact.sa_mask); (void) sigaction(SIGCLD, &sigact, NULL); sigact.sa_flags = 0; sigact.sa_handler = sigpoll_catch; (void) sigemptyset(&sigact.sa_mask); (void) sigaddset(&sigact.sa_mask, SIGPOLL); (void) sigaction(SIGPOLL, &sigact, NULL); #ifdef DEBUG sigact.sa_flags = 0; sigact.sa_handler = dump_pmtab; (void) sigemptyset(&sigact.sa_mask); (void) sigaddset(&sigact.sa_mask, SIGUSR1); (void) sigaction(SIGUSR1, &sigact, NULL); sigact.sa_flags = 0; sigact.sa_handler = dump_ttydefs; (void) sigemptyset(&sigact.sa_mask); (void) sigaddset(&sigact.sa_mask, SIGUSR2); (void) sigaction(SIGUSR2, &sigact, NULL); #endif } /* * child_sigcatch() - tmchild inherits some signal_catch from parent * and need to reset them */ void child_sigcatch(void) { struct sigaction sigact; sigset_t cset; cset = Origmask; (void) sigdelset(&cset, SIGINT); (void) sigdelset(&cset, SIGPOLL); (void) sigprocmask(SIG_SETMASK, &cset, NULL); sigact.sa_flags = 0; sigact.sa_handler = sigpoll; (void) sigemptyset(&sigact.sa_mask); (void) sigaddset(&sigact.sa_mask, SIGPOLL); (void) sigaction(SIGPOLL, &sigact, NULL); sigact.sa_flags = 0; sigact.sa_handler = sigint; (void) sigemptyset(&sigact.sa_mask); (void) sigaddset(&sigact.sa_mask, SIGINT); (void) sigaction(SIGINT, &sigact, NULL); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #ifndef _TMSTRUCT_H #define _TMSTRUCT_H /* * /etc/ttydefs structure */ struct Gdef { char *g_id; /* id for modes & speeds */ char *g_iflags; /* initial terminal flags */ char *g_fflags; /* final terminal flags */ short g_autobaud; /* autobaud indicator */ char *g_nextid; /* next id if this speed is wrong */ }; /* * pmtab structure + internal data for ttymon */ struct pmtab { /* the following fields are from pmtab */ char *p_tag; /* port/service tag */ long p_flags; /* flags */ char *p_identity; /* id for service to run as */ char *p_res1; /* reserved field */ char *p_res2; /* reserved field */ char *p_res3; /* reserved field */ char *p_device; /* full path name of device */ long p_ttyflags; /* ttyflags */ int p_count; /* wait_read count */ char *p_server; /* full service cmd line */ int p_timeout; /* timeout for input */ char *p_ttylabel; /* ttylabel in /etc/ttydefs */ char *p_modules; /* modules to push */ char *p_prompt; /* prompt message */ char *p_dmsg; /* disable message */ char *p_termtype; /* terminal type */ char *p_softcar; /* use softcarrier */ /* the following fields are for ttymon internal use */ int p_status; /* status of entry */ int p_fd; /* fd for the open device */ pid_t p_childpid; /* pid of child on the device */ int p_inservice; /* service invoked */ int p_respawn; /* respawn count in this series */ long p_time; /* start time of a series */ uid_t p_uid; /* uid of p_identity */ gid_t p_gid; /* gid of p_identity */ char *p_dir; /* home dir of p_identity */ char *p_ttymode; /* mode line for serial device */ struct pmtab *p_next; }; /* * valid flags for p_flags field of pmtab */ #define X_FLAG 0x1 /* port/service disabled */ #define U_FLAG 0x2 /* create utmp entry for the service */ /* * valid flags for p_ttyflags field of pmtab */ #define C_FLAG 0x1 /* invoke service on carrier */ #define H_FLAG 0x2 /* hangup the line */ #define B_FLAG 0x4 /* bi-directional line */ #define R_FLAG 0x8 /* do wait_read */ #define I_FLAG 0x10 /* initialize only */ /* * autobaud enabled flag */ #define A_FLAG 0x20 /* autobaud flag */ /* * values for p_status field of pmtab */ #define NOTVALID 0 /* entry is not valid */ #define VALID 1 /* entry is valid */ #define CHANGED 2 /* entry is valid but changed */ #define GETTY 3 /* entry is for ttymon express */ #define ALLOC_PMTAB \ ((struct pmtab *)calloc((unsigned)1, \ (unsigned)sizeof (struct pmtab))) #endif /* _TMSTRUCT_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #include #include #include #include #include #include #include #include #include #include #include "sys/stropts.h" #include "sys/signal.h" #include "ttymon.h" #include "tmstruct.h" #include "tmextern.h" /* * set_termio - set termio on device * fd - fd for the device * options - stty termio options * aspeed - autobaud speed * clear - if TRUE, current flags will be set to some defaults * before applying the options * - if FALSE, current flags will not be cleared * mode - terminal mode, CANON, RAW */ int set_termio(int fd, char *options, char *aspeed, int clear, long mode) { struct termio termio; struct termios termios; struct stio stermio; struct termiox termiox; struct winsize winsize; struct winsize owinsize; int term; int cnt = 1; char *uarg; char *argvp[MAXARGS]; /* stty args */ static char *binstty = "/usr/bin/stty"; static char buf[BUFSIZ]; #ifdef DEBUG debug("in set_termio"); #endif if ((term = get_ttymode(fd, &termio, &termios, &stermio, &termiox, &winsize)) < 0) { log("set_termio: get_ttymode failed: %s", strerror(errno)); return (-1); } owinsize = winsize; if (clear) { if (mode & CANON) { /* could have removed these too - rely on defaults */ termios.c_cc[VEOF] = CEOF; termios.c_cc[VEOL] = CNUL; } else { termios.c_lflag &= ECHO; termios.c_cc[VMIN] = 1; termios.c_cc[VTIME] = 0; } } if (options != NULL && *options != '\0') { /* just a place holder to make it look like invoking stty */ argvp[0] = binstty; (void) strcpy(buf, options); mkargv(buf, &argvp[1], &cnt, MAXARGS - 1); if (aspeed != NULL && *aspeed != '\0') { argvp[cnt++] = aspeed; } argvp[cnt] = (char *)0; if ((uarg = sttyparse(cnt, argvp, term, &termio, &termios, &termiox, &winsize)) != NULL) { log("sttyparse unknown mode: %s", uarg); return (-1); } } if (set_ttymode(fd, term, &termio, &termios, &stermio, &termiox, &winsize, &owinsize) != 0) { log("set_termio: set_ttymode failed", strerror(errno)); return (-1); } return (0); } #ifdef NOT_USE /* * turnon_canon - turn on canonical processing * - return 0 if succeeds, -1 if fails */ turnon_canon(int fd) { struct termio termio; #ifdef DEBUG debug("in turnon_canon"); #endif if (ioctl(fd, TCGETA, &termio) != 0) { log("turnon_canon: TCGETA failed, fd = %d: %s", fd, strerror(errno)); return (-1); } termio.c_lflag |= (ISIG|ICANON|ECHO|ECHOE|ECHOK); termio.c_cc[VEOF] = CEOF; termio.c_cc[VEOL] = CNUL; if (ioctl(fd, TCSETA, &termio) != 0) { log("turnon_canon: TCSETA failed, fd = %d: %s", fd, strerror(errno)); return (-1); } return (0); } #endif /* * flush_input - flush the input queue */ void flush_input(int fd) { if (ioctl(fd, I_FLUSH, FLUSHR) == -1) log("flush_input failed, fd = %d: %s", fd, strerror(errno)); if (ioctl(fd, TCSBRK, 1) == -1) log("drain of ouput failed, fd = %d: %s", fd, strerror(errno)); } /* * push_linedisc - if modules is not NULL, pop everything * - then push modules specified by "modules" */ int push_linedisc( int fd, /* fd to push modules on */ char *modules, /* ptr to a list of comma separated module names */ char *device) /* device name for printing msg */ { char *p, *tp; char buf[BUFSIZ]; #ifdef DEBUG debug("in push_linedisc"); #endif /* * copy modules into buf so we won't mess up the original buffer * because strtok will chop the string */ p = strcpy(buf, modules); while (ioctl(fd, I_POP, 0) >= 0) /* pop everything */ ; for (p = strtok(p, ","); p != NULL; p = strtok(NULL, ",")) { for (tp = p + strlen(p) - 1; tp >= p && isspace(*tp); --tp) *tp = '\0'; if (ioctl(fd, I_PUSH, p) == -1) { log("push (%s) on %s failed: %s", p, device, strerror(errno)); return (-1); } } return (0); } /* * hang_up_line - set speed to B0. This will drop DTR */ int hang_up_line(int fd) { struct termio termio; struct termios termios; #ifdef DEBUG debug("in hang_up_line"); #endif if (ioctl(fd, TCGETS, &termios) < 0) { if (ioctl(fd, TCGETA, &termio) < 0) { log("hang_up_line: TCGETA failed: %s", strerror(errno)); return (-1); } termio.c_cflag &= ~CBAUD; termio.c_cflag |= B0; if (ioctl(fd, TCSETA, &termio) < 0) { log("hang_up_line: TCSETA failed: %s", strerror(errno)); return (-1); } } else { (void) cfsetospeed(&termios, B0); if (ioctl(fd, TCSETS, &termios) < 0) { log("hang_up_line: TCSETS failed: %s", strerror(errno)); return (-1); } } return (0); } /* * initial_termio - set initial termios * - return 0 if successful, -1 if failed. */ int initial_termio(int fd, struct pmtab *pmptr) { int ret; struct Gdef *speedef; speedef = get_speed(pmptr); if (speedef->g_autobaud & A_FLAG) { pmptr->p_ttyflags |= A_FLAG; if (auto_termio(fd) == -1) { (void) close(fd); return (-1); } } else { if (pmptr->p_ttyflags & R_FLAG) ret = set_termio(fd, speedef->g_iflags, NULL, TRUE, (long)RAW); else ret = set_termio(fd, speedef->g_iflags, NULL, TRUE, (long)CANON); if (ret == -1) { log("initial termio on (%s) failed", pmptr->p_device); (void) close(fd); return (-1); } } return (0); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #include #include #include #include #include #include #include #include #include #include #include "ttymon.h" #include "tmstruct.h" #include "tmextern.h" #include "stty.h" static void insert_def(struct Gdef *); static void zero(char *, int); /* * read_ttydefs - read in the /etc/ttydefs and store in Gdef array * - if id is not NULL, only get entry with that id * - if check is TRUE, print out the entries */ void read_ttydefs(const char *id, int check) { FILE *fp; static struct Gdef def; struct Gdef *gptr; static char line[BUFSIZ]; static char dbuf[BUFSIZ]; char *ptr; int len; int input, state, size, rawc, field; char oldc; static char d_id[MAXID+1], d_nextid[MAXID+1], d_autobaud[MAXID+1], d_if[BUFSIZ], d_ff[BUFSIZ]; static char *states[] = { "", "tty label", "Initial flags", "Final flags", "Autobaud", "Next label" }; if ((fp = fopen(TTYDEFS, "r")) == NULL) { log("can't open \"%s\".\n", TTYDEFS); return; } if (check) { for (len = 0; len < (size_t)(BUFSIZ - 1); len++) dbuf[len] = '-'; dbuf[len] = '\0'; } /* Start searching for the line with the proper "id". */ input = ACTIVE; field = FAILURE; do { line[0] = '\0'; for (ptr = line, oldc = '\0'; ptr < &line[sizeof (line) - 1] && (rawc = getc(fp)) != '\n' && rawc != EOF; ptr++, oldc = (char)rawc) { if ((rawc == '#') && (oldc != '\\')) break; *ptr = (char)rawc; } *ptr = '\0'; /* skip rest of the line */ if (rawc != EOF && rawc != '\n') { if (check && rawc != '#') log("Entry too long."); while ((rawc = getc(fp)) != EOF && rawc != '\n') ; } if (rawc == EOF) { if (ptr == line) break; else input = FINISHED; } /* if empty line, skip */ for (ptr = line; *ptr != '\0' && isspace(*ptr); ptr++) ; if (*ptr == '\0') continue; /* Now we have the complete line */ /* Initialize "def" and "gptr". */ gptr = &def; zero((char *)gptr, sizeof (struct Gdef)); ptr = line; state = T_TTYLABEL; (void) strncpy(d_id, getword(ptr, &size, 0), MAXID); gptr->g_id = d_id; ptr += size; if (*ptr != ':') { field = state; state = FAILURE; } else { ptr++; /* Skip the ':' */ state++; } /* If "id" != NULL, and it does not match, go to next entry */ if ((id != NULL) && (strcmp(id, gptr->g_id) != 0)) continue; if (check) { len = strlen(line); dbuf[len] = '\0'; log("\n%s", dbuf); log("%s", line); log("%s\n", dbuf); dbuf[len] = '-'; } for (; state != FAILURE && state != SUCCESS; ) { switch (state) { case T_IFLAGS: (void) strncpy(d_if, getword(ptr, &size, 1), BUFSIZ); gptr->g_iflags = d_if; ptr += size; if ((*ptr != ':') || (check_flags(d_if) != 0)) { field = state; state = FAILURE; } else { ptr++; state++; } break; case T_FFLAGS: (void) strncpy(d_ff, getword(ptr, &size, 1), BUFSIZ); gptr->g_fflags = d_ff; ptr += size; if ((*ptr != ':') || (check_flags(d_ff) != 0)) { field = state; state = FAILURE; } else { ptr++; state++; } break; case T_AUTOBAUD: (void) strncpy(d_autobaud, getword(ptr, &size, 0), MAXID); if (size > 1) { ptr += size; field = state; state = FAILURE; break; } if (size == 1) { if (*d_autobaud == 'A') { gptr->g_autobaud |= A_FLAG; } else { ptr += size; field = state; state = FAILURE; break; } } ptr += size; if (*ptr != ':') { field = state; state = FAILURE; } else { ptr++; /* Skip the ':' */ state++; } break; case T_NEXTLABEL: (void) strncpy(d_nextid, getword(ptr, &size, 0), MAXID); gptr->g_nextid = d_nextid; ptr += size; if (*ptr != '\0') { field = state; state = FAILURE; } else { state = SUCCESS; } break; } /* end switch */ } /* end for loop */ if (state == SUCCESS) { if (check) { log("ttylabel:\t%s", gptr->g_id); log("initial flags:\t%s", gptr->g_iflags); log("final flags:\t%s", gptr->g_fflags); if (gptr->g_autobaud & A_FLAG) log("autobaud:\tyes"); else log("autobaud:\tno"); log("nextlabel:\t%s", gptr->g_nextid); } if (Ndefs < MAXDEFS) { insert_def(gptr); } else { log("can't add more entries to ttydefs table, " " Maximum entries = %d", MAXDEFS); (void) fclose(fp); return; } if (id != NULL) { return; } } else { *++ptr = '\0'; log("Parsing failure in the \"%s\" field\n" "%s<--error detected here\n", states[field], line); } } while (input == ACTIVE); (void) fclose(fp); } /* * zero - zero out the buffer */ static void zero(char *adr, int size) { if (adr != NULL) { while (size--) *adr++ = '\0'; } } /* * find_def(ttylabel) * - scan Gdef table for an entry with requested "ttylabel". * - return a Gdef ptr if entry with "ttylabel" is found * - return NULL if no entry with matching "ttylabel" */ struct Gdef * find_def(char *ttylabel) { int i; struct Gdef *tp; tp = &Gdef[0]; for (i = 0; i < Ndefs; i++, tp++) { if (strcmp(ttylabel, tp->g_id) == 0) { return (tp); } } return (NULL); } /* * check_flags - check to see if the flags contains options that are * recognizable by stty * - return 0 if no error. Otherwise return -1 */ int check_flags(char *flags) { struct termio termio; struct termios termios; struct termiox termiox; struct winsize winsize; int term; int cnt = 1; char *argvp[MAXARGS]; /* stty args */ static char *binstty = "/usr/bin/stty"; static char buf[BUFSIZ]; char *s_arg; /* this will point to invalid option */ /* put flags into buf, because strtok will break up buffer */ (void) strcpy(buf, flags); argvp[0] = binstty; /* just a place holder */ mkargv(buf, &argvp[1], &cnt, MAXARGS - 1); argvp[cnt] = NULL; /* * because we don't know what type of terminal we have now, * just set term = everything, so all possible stty options * are accepted */ term = ASYNC|TERMIOS|FLOW; if ((s_arg = sttyparse(cnt, argvp, term, &termio, &termios, &termiox, &winsize)) != NULL) { log("invalid mode: %s", s_arg); return (-1); } return (0); } /* * insert_def - insert one entry into Gdef table */ static void insert_def(struct Gdef *gptr) { struct Gdef *tp; if (find_def(gptr->g_id) != NULL) { log("Warning -- duplicate entry <%s>, ignored", gptr->g_id); return; } tp = &Gdef[Ndefs]; tp->g_id = strsave(gptr->g_id); tp->g_iflags = strsave(gptr->g_iflags); tp->g_fflags = strsave(gptr->g_fflags); tp->g_autobaud = gptr->g_autobaud; tp->g_nextid = strsave(gptr->g_nextid); Ndefs++; } /* * mkargv - parse the string into args, starting from args[cnt] */ void mkargv(char *string, char **args, int *cnt, int maxargs) { char *ptrin, *ptrout; int i; int qsize; for (i = 0; i < maxargs; i++) args[i] = NULL; ptrin = ptrout = string; for (i = 0; *ptrin != '\0' && i < maxargs; i++) { /* Skip excess white spaces between arguments. */ while (*ptrin == ' ' || *ptrin == '\t') { ptrin++; ptrout++; } /* Save the address of argument if there is something there. */ if (*ptrin == '\0') break; else args[i] = ptrout; /* Span the argument itself. The '\' character causes quoting */ /* of the next character to take place (except for '\0'). */ while (*ptrin != '\0') { if (*ptrin == '\\') { *ptrout++ = quoted(ptrin, &qsize); ptrin += qsize; /* Is this the end of the argument? If so quit loop. */ } else if (*ptrin == ' ' || *ptrin == '\t') { ptrin++; break; /* If this is a normal letter of the argument, save it, advancing */ /* the pointers at the same time. */ } else *ptrout++ = *ptrin++; } /* Null terminate the string. */ *ptrout++ = '\0'; } (*cnt) += i; } #ifdef DEBUG /* * dump_ttydefs - dump Gdef table to log file */ void dump_ttydefs(void) { int i; struct Gdef *gptr; gptr = &Gdef[0]; log("********** dumping ttydefs table **********"); log("Ndefs = %d", Ndefs); log(" "); for (i = 0; i < Ndefs; i++, gptr++) { log("----------------------------------------"); log("ttylabel:\t%s", gptr->g_id); log("initial flags:\t%s", gptr->g_iflags); log("final flags:\t%s", gptr->g_fflags); if (gptr->g_autobaud & A_FLAG) log("autobaud:\tyes"); else log("Autobaud:\tno"); log("nextlabel:\t%s", gptr->g_nextid); log(" "); } log("********** end dumping ttydefs table **********"); } #endif /* * this is copies from uucp/strsave.c * and is modified that if malloc fails, it will exit */ char * strsave(char *str) { char *rval; if (str == NULL) { if ((rval = malloc(1)) == NULL) { log("strsave: malloc failed"); exit(1); } *rval = '\0'; } else { if ((rval = malloc(strlen(str) + 1)) == NULL) { log("strsave: malloc failed"); exit(1); } (void) strcpy(rval, str); } return (rval); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #include #include #include #include #include #include #include #include #include #include #include #include #include "ttymon.h" #include "tmstruct.h" #include "tmextern.h" #define NSTRPUSH 9 /* should agree with the tunable in */ /* /etc/master.d/kernel */ /* * check_device - check to see if the device exists, * - and if it is a character device * - return 0 if everything is ok. Otherwise, return -1 */ int check_device(char *device) { struct stat statbuf; if ((device == NULL) || (*device == '\0')) { log("error -- device field is missing"); return (-1); } if (*device != '/') { log("error -- must specify full path name for \"%s\".", device); return (-1); } if (access(device, 0) == 0) { if (stat(device, &statbuf) < 0) { log("stat(%s) failed: %s", device, strerror(errno)); return (-1); } if ((statbuf.st_mode & S_IFMT) != S_IFCHR) { log("error -- \"%s\" not character special device", device); return (-1); } } else { log("error -- device \"%s\" does not exist", device); return (-1); } return (0); } /* * check_cmd - check to see if the cmd file exists, * - and if it is executable * - return 0 if everything is ok. Otherwise, return -1 */ int check_cmd(char *cmd) { struct stat statbuf; char tbuf[BUFSIZ]; char *tp = tbuf; if ((cmd == NULL) || (*cmd == '\0')) { log("error -- server command is missing"); return (-1); } (void) strcpy(tp, cmd); (void) strtok(tp, " \t"); if (*tp != '/') { log("error -- must specify full path name for \"%s\".", tp); return (-1); } if (access(tp, 0) == 0) { if (stat(tp, &statbuf) < 0) { log("stat(%s) failed.", tp); return (-1); } if (!(statbuf.st_mode & 0111)) { log("error -- \"%s\" not executable\n", tp); return (-1); } if ((statbuf.st_mode & S_IFMT) != S_IFREG) { log("error -- \"%s\" not a regular file", tp); return (-1); } } else { log("error -- \"%s\" does not exist", tp); return (-1); } return (0); } /* * strcheck(sp, flag) - check string * - if flag == ALNUM, all char. are expected to * be alphanumeric * - if flag == NUM, all char. are expected to * be digits and the number must be >= 0 * - return 0 if successful, -1 if failed. */ int strcheck(char *sp, int flag) { char *cp; if (flag == NUM) { for (cp = sp; *cp; cp++) { if (!isdigit(*cp)) { return (-1); } } } else { /* (flag == ALNUM) */ for (cp = sp; *cp; cp++) { if (!isalnum(*cp)) { return (-1); } } } return (0); } /* * vml(modules) - validate a list of modules * - return 0 if successful, -1 if failed */ int vml(char *modules) { char *modp, *svmodp; int i, fd; struct str_mlist newmods[NSTRPUSH]; /* modlist for newlist */ struct str_list newlist; /* modules to be pushed */ if ((modules == NULL) || (*modules == '\0')) return (0); newlist.sl_modlist = newmods; newlist.sl_nmods = NSTRPUSH; if ((modp = malloc(strlen(modules) + 1)) == NULL) { log("vml: malloc failed"); return (-1); }; svmodp = modp; (void) strcpy(modp, modules); /* * pull mod names out of comma-separated list */ for (i = 0, modp = strtok(modp, ","); modp != NULL; i++, modp = strtok(NULL, ",")) { if (i >= NSTRPUSH) { log("too many modules in <%s>", modules); i = -1; break; } (void) strncpy(newlist.sl_modlist[i].l_name, modp, FMNAMESZ); newlist.sl_modlist[i].l_name[FMNAMESZ] = '\0'; } free(svmodp); if (i == -1) return (-1); newlist.sl_nmods = i; /* * Is it a valid list of modules? */ if ((fd = open(USERDEV, O_RDWR)) == -1) { if (errno == EBUSY) { log("Warning - can't validate module list, " "/dev/sad/user busy"); return (0); } log("open /dev/sad/user failed: %s", strerror(errno)); return (-1); } if ((i = ioctl(fd, SAD_VML, &newlist)) < 0) { log("Validate modules ioctl failed, modules = <%s>: %s", modules, strerror(errno)); (void) close(fd); return (-1); } if (i != 0) { log("Error - invalid STREAMS module list <%s>.", modules); (void) close(fd); return (-1); } (void) close(fd); return (0); } /* * copystr(s1, s2) - copy string s2 to string s1 * - also put '\' in front of ':' */ void copystr(char *s1, char *s2) { while (*s2) { if (*s2 == ':') { *s1++ = '\\'; } *s1++ = *s2++; } *s1 = '\0'; } /*PRINTFLIKE1*/ void cons_printf(const char *fmt, ...) { char buf[MAXPATHLEN * 2]; /* enough space for msg including a path */ int fd; va_list ap; va_start(ap, fmt); (void) vsnprintf(buf, sizeof (buf), fmt, ap); va_end(ap); if ((fd = open(CONSOLE, O_WRONLY|O_NOCTTY)) != -1) (void) write(fd, buf, strlen(buf) + 1); (void) close(fd); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ #include #include #include #include #include #include #include #include #include #include #include "sac.h" #include "tmextern.h" /* * account - create a utmpx record for service * */ int account(char *line) { struct utmpx utmpx; /* prototype utmpx entry */ struct utmpx *up = &utmpx; /* and a pointer to it */ (void) memset(up, '\0', sizeof (utmpx)); up->ut_user[0] = '.'; (void) strncpy(&up->ut_user[1], Tag, sizeof (up->ut_user)-1); (void) strncpy(up->ut_line, lastname(line), sizeof (up->ut_line)); up->ut_pid = getpid(); up->ut_type = USER_PROCESS; up->ut_id[0] = 't'; up->ut_id[1] = 'm'; up->ut_id[2] = SC_WILDC; up->ut_id[3] = SC_WILDC; up->ut_exit.e_termination = 0; up->ut_exit.e_exit = 0; (void) time(&up->ut_tv.tv_sec); if (makeutx(up) == NULL) { log("makeutx for pid %d failed", up->ut_pid); return (-1); } return (0); } /* * checkut_line - check if a login is active on the requested device */ int checkut_line(char *line) { struct utmpx *u; char buf[33], ttyn[33]; int rvalue = 0; pid_t ownpid = getpid(); (void) strncpy(buf, lastname(line), sizeof (u->ut_line)); buf[sizeof (u->ut_line)] = '\0'; setutxent(); while ((u = getutxent()) != NULL) { if (u->ut_pid == ownpid) { if (u->ut_type == USER_PROCESS) { (void) strncpy(ttyn, u->ut_line, sizeof (u->ut_line)); ttyn[sizeof (u->ut_line)] = '\0'; if (strcmp(buf, ttyn) == 0) { rvalue = 1; break; } } } } endutxent(); return (rvalue); } void cleanut(pid_t pid, int status) { pam_handle_t *pamh; struct utmpx *up; char user[33], ttyn[33], rhost[258]; setutxent(); while ((up = getutxent())) { if (up->ut_pid == pid) { if (up->ut_type == DEAD_PROCESS) { /* Cleaned up elsewhere. */ break; } (void) strncpy(user, up->ut_user, sizeof (up->ut_user)); user[sizeof (up->ut_user)] = '\0'; (void) strncpy(ttyn, up->ut_line, sizeof (up->ut_line)); ttyn[sizeof (up->ut_line)] = '\0'; (void) strncpy(rhost, up->ut_host, sizeof (up->ut_host)); rhost[sizeof (up->ut_host)] = '\0'; if (pam_start("ttymon", user, NULL, &pamh) == PAM_SUCCESS) { (void) pam_set_item(pamh, PAM_TTY, ttyn); (void) pam_set_item(pamh, PAM_RHOST, rhost); (void) pam_close_session(pamh, 0); (void) pam_end(pamh, PAM_SUCCESS); } up->ut_type = DEAD_PROCESS; up->ut_exit.e_termination = WTERMSIG(status); up->ut_exit.e_exit = WEXITSTATUS(status); (void) time(&up->ut_tv.tv_sec); if (modutx(up) == NULL) { /* * Since modutx failed we'll * write out the new entry * ourselves. */ (void) pututxline(up); updwtmpx("wtmpx", up); } break; } } endutxent(); } /* * getty_account - This is a copy of old getty account routine. * - This is only called if ttymon is invoked as getty. * - It tries to find its own INIT_PROCESS entry in utmpx * - and change it to LOGIN_PROCESS */ void getty_account(char *line) { pid_t ownpid; struct utmpx *u; ownpid = getpid(); setutxent(); while ((u = getutxent()) != NULL) { if (u->ut_type == INIT_PROCESS && u->ut_pid == ownpid) { (void) strncpy(u->ut_line, lastname(line), sizeof (u->ut_line)); (void) strncpy(u->ut_user, "LOGIN", sizeof (u->ut_user)); u->ut_type = LOGIN_PROCESS; /* Write out the updated entry. */ (void) pututxline(u); break; } } /* create wtmpx entry also */ if (u != NULL) updwtmpx("/etc/wtmpx", u); endutxent(); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #include #include #include #include #include #include #include #include #include "tmstruct.h" #include "tmextern.h" #include "ttymon.h" /* * ttyadm - format ttymon specific information and * print it to stdout * * Usage: ttyadm [options] -d device -s service -l ttylabel * ttyadm -V * * valid options are: * -c * -h * -b * -I * -S y|n * -T term * -r count * -t timeout * -p prompt * -m modules * -i msg */ static void usage(void); static int check_label(char *); int main(int argc, char *argv[]) { int c; /* option letter */ int errflg = 0; /* error indicator */ struct pmtab *ptr; char *timeout = ""; char *count = ""; char prompt[BUFSIZ]; char dmsg[BUFSIZ]; char ttyflags[BUFSIZ], *tf; int dflag = 0; /* -d seen */ int sflag = 0; /* -s seen */ int lflag = 0; /* -l seen */ int mflag = 0; /* -m seen */ if (argc == 1) usage(); if ((ptr = ALLOC_PMTAB) == NULL) { (void) fprintf(stderr, "calloc failed\n"); exit(1); } ptr->p_modules = ""; ptr->p_dmsg = ""; ptr->p_termtype = ""; ptr->p_softcar = ""; ptr->p_prompt = "login\\: "; ttyflags[0] = '\0'; tf = ttyflags; while ((c = getopt(argc, argv, "IT:S:Vd:s:chbr:t:l:m:p:i:")) != -1) { switch (c) { case 'V': if ((argc > 2) || (optind < argc)) usage(); (void) fprintf(stdout, "%d\n", PMTAB_VERS); exit(0); break; /*NOTREACHED*/ case 'd': ptr->p_device = optarg; dflag = 1; break; case 'c': tf = strcat(tf, "c"); break; case 'h': tf = strcat(tf, "h"); break; case 'b': tf = strcat(tf, "b"); break; case 'I': tf = strcat(tf, "I"); break; case 'r': tf = strcat(tf, "r"); count = optarg; if (strcheck(optarg, NUM) != 0) { (void) fprintf(stderr, "Invalid argument for \"-r\" -- positive number expected.\n"); usage(); } break; case 'T': ptr->p_termtype = optarg; break; case 'S': switch (*optarg) { case 'Y': case 'y': ptr->p_softcar = "y"; break; case 'N': case 'n': ptr->p_softcar = "n"; break; default: usage(); } break; case 's': ptr->p_server = optarg; sflag = 1; break; case 't': timeout = optarg; if (strcheck(optarg, NUM) != 0) { (void) fprintf(stderr, "Invalid argument for \"-t\" -- positive number expected.\n"); usage(); } break; case 'l': ptr->p_ttylabel = optarg; lflag = 1; break; case 'm': ptr->p_modules = optarg; mflag = 1; break; case 'p': ptr->p_prompt = prompt; copystr(ptr->p_prompt, optarg); break; case 'i': ptr->p_dmsg = dmsg; copystr(ptr->p_dmsg, optarg); break; case '?': usage(); break; } } if (optind < argc) usage(); if ((!dflag) || (!sflag) || (!lflag)) usage(); if (check_device(ptr->p_device) != 0) errflg++; if (check_cmd(ptr->p_server) != 0) errflg++; if (check_label(ptr->p_ttylabel) != 0) errflg++; if (mflag && (vml(ptr->p_modules) != 0)) errflg++; if (errflg) exit(1); (void) fprintf(stdout, "%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:", ptr->p_device, ttyflags, count, ptr->p_server, timeout, ptr->p_ttylabel, ptr->p_modules, ptr->p_prompt, ptr->p_dmsg, ptr->p_termtype, ptr->p_softcar); return (0); } /* * usage - print out a usage message */ static void usage(void) { (void) fprintf(stderr, "Usage:\tttyadm [ options ] -d device -s service -l ttylabel\n"); (void) fprintf(stderr, "\tttyadm -V\n"); (void) fprintf(stderr, "\n\tValid options are:\n"); (void) fprintf(stderr, "\t-c\n"); (void) fprintf(stderr, "\t-h\n"); (void) fprintf(stderr, "\t-b\n"); (void) fprintf(stderr, "\t-I\n"); (void) fprintf(stderr, "\t-S y|n\n"); (void) fprintf(stderr, "\t-T term\n"); (void) fprintf(stderr, "\t-r count\n"); (void) fprintf(stderr, "\t-t timeout\n"); (void) fprintf(stderr, "\t-p prompt\n"); (void) fprintf(stderr, "\t-m modules\n"); (void) fprintf(stderr, "\t-i msg\n"); exit(1); } /* * check_label - if ttylabel exists in /etc/ttydefs, return 0 * - otherwise, return -1 */ static int check_label(char *ttylabel) { FILE *fp; if ((ttylabel == NULL) || (*ttylabel == '\0')) { (void) fprintf(stderr, "error -- ttylabel is missing"); return (-1); } if ((fp = fopen(TTYDEFS, "r")) == NULL) { (void) fprintf(stderr, "error -- \"%s\" does not exist, " "can't verify ttylabel <%s>\n", TTYDEFS, ttylabel); return (-1); } if (find_label(fp, ttylabel)) { (void) fclose(fp); return (0); } (void) fclose(fp); (void) fprintf(stderr, "error -- can't find ttylabel <%s> in \"%s\"\n", ttylabel, TTYDEFS); return (-1); } /* * log - print a message to stderr */ void log(const char *msg, ...) { va_list ap; va_start(ap, msg); (void) vfprintf(stderr, msg, ap); va_end(ap); (void) fprintf(stderr, "\n"); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sac.h" #include "ttymon.h" #include "tmstruct.h" #include "tmextern.h" static int Initialized; static void initialize(void); static void open_all(void); static int set_poll(struct pollfd *); static int check_spawnlimit(struct pmtab *); static int mod_ttydefs(void); void open_device(struct pmtab *); void set_softcar(struct pmtab *); /* * ttymon - a port monitor under SAC * - monitor ports, set terminal modes, baud rate * and line discipline for the port * - invoke service on port if connection request received * - Usage: ttymon * ttymon -g [options] * Valid options are * -h * -d device * -l ttylabel * -t timeout * -m modules * -p prompt * * - ttymon without args is invoked by SAC * - ttymon -g is invoked by process that needs to * have login service on the fly */ int main(int argc, char *argv[]) { int nfds; /* * Only the superuser should execute this command. */ if (getuid() != 0) return (1); /* remember original signal mask and dispositions */ (void) sigprocmask(SIG_SETMASK, NULL, &Origmask); (void) sigaction(SIGINT, NULL, &Sigint); (void) sigaction(SIGALRM, NULL, &Sigalrm); (void) sigaction(SIGPOLL, NULL, &Sigpoll); (void) sigaction(SIGQUIT, NULL, &Sigquit); (void) sigaction(SIGCLD, NULL, &Sigcld); (void) sigaction(SIGTERM, NULL, &Sigterm); #ifdef DEBUG (void) sigaction(SIGUSR1, NULL, &Sigusr1); (void) sigaction(SIGUSR2, NULL, &Sigusr2); #endif /* * SIGQUIT needs to be ignored. Otherwise, hitting ^\ from * console kills ttymon. */ (void) signal(SIGQUIT, SIG_IGN); if ((argc > 1) || (strcmp(lastname(argv[0]), "getty") == 0)) { ttymon_express(argc, argv); return (1); /*NOTREACHED*/ } initialize(); for (;;) { nfds = set_poll(Pollp); if (!Reread_flag) { if (nfds > 0) do_poll(Pollp, nfds); else (void) pause(); } /* * READDB messages may arrive during poll or pause. * So the flag needs to be checked again. */ if (Reread_flag) { Reread_flag = FALSE; re_read(); } while (Retry) { Retry = FALSE; open_all(); } } } static void initialize(void) { struct pmtab *tp; struct passwd *pwdp; struct group *gp; struct rlimit rlimit; Initialized = FALSE; /* * get_environ() must be called first, * otherwise we don't know where the log file is */ get_environ(); openttymonlog(); openpid(); openpipes(); setup_PCpipe(); log("PMTAG: %s", Tag); log("Starting state: %s", (State == PM_ENABLED) ? "enabled" : "disabled"); #ifdef DEBUG opendebug(FALSE); debug("***** ttymon in initialize *****"); log("debug mode is \t on"); #endif catch_signals(); /* register to receive SIGPOLL when data comes to pmpipe */ if (ioctl(Pfd, I_SETSIG, S_INPUT) < 0) fatal("I_SETSIG on pmpipe failed: %s", strerror(errno)); sacpoll(); /* this is needed because there may be data already */ Maxfiles = (int)ulimit(4, 0L); /* get max number of open files */ if (Maxfiles < 0) fatal("ulimit(4,0L) failed: %s", strerror(errno)); if (getrlimit(RLIMIT_NOFILE, &Rlimit) == -1) fatal("getrlimit failed: %s", strerror(errno)); rlimit.rlim_cur = rlimit.rlim_max = Rlimit.rlim_max; if (setrlimit(RLIMIT_NOFILE, &rlimit) == -1) fatal("setrlimit failed: %s", strerror(errno)); (void) enable_extended_FILE_stdio(-1, -1); Maxfiles = rlimit.rlim_cur; Maxfds = Maxfiles - FILE_RESERVED; log("max open files = %d", Maxfiles); log("max ports ttymon can monitor = %d", Maxfds); read_pmtab(); /* * setup poll array * - we allocate 10 extra pollfd so that * we do not have to re-malloc when there is * minor fluctuation in Nentries */ Npollfd = Nentries + 10; if (Npollfd > Maxfds) Npollfd = Maxfds; if ((Pollp = (struct pollfd *) malloc((unsigned)(Npollfd * sizeof (struct pollfd)))) == (struct pollfd *)NULL) fatal("malloc for Pollp failed"); (void) mod_ttydefs(); /* just to initialize Mtime */ if (check_version(TTYDEFS_VERS, TTYDEFS) != 0) fatal("check /etc/ttydefs version failed"); read_ttydefs(NULL, FALSE); /* initialize global variables, Uucp_uid & Tty_gid */ if ((pwdp = getpwnam(UUCP)) != NULL) Uucp_uid = pwdp->pw_uid; if ((gp = getgrnam(TTY)) == NULL) log("no group entry for , default is used"); else Tty_gid = gp->gr_gid; endgrent(); endpwent(); #ifdef DEBUG debug("Uucp_uid = %u, Tty_gid = %u", Uucp_uid, Tty_gid); #endif log("Initialization Completed"); /* open the devices ttymon monitors */ Retry = TRUE; while (Retry) { Retry = FALSE; for (tp = PMtab; tp; tp = tp->p_next) { if ((tp->p_status > 0) && (tp->p_fd == 0) && (tp->p_childpid == 0) && !(tp->p_ttyflags & I_FLAG) && (!((State == PM_DISABLED) && ((tp->p_dmsg == NULL)||(*(tp->p_dmsg) == '\0'))))) { open_device(tp); if (tp->p_fd > 0) got_carrier(tp); } } } Initialized = TRUE; } static void free_defs(void); /* * open_all - open devices in pmtab if the entry is * - valid, fd = 0, and pid = 0 */ static void open_all(void) { struct pmtab *tp; int check_modtime; sigset_t cset; sigset_t tset; #ifdef DEBUG debug("in open_all"); #endif check_modtime = TRUE; for (tp = PMtab; tp; tp = tp->p_next) { if ((tp->p_status > 0) && (tp->p_fd == 0) && (tp->p_childpid == 0) && !(tp->p_ttyflags & I_FLAG) && (!((State == PM_DISABLED) && ((tp->p_dmsg == NULL)||(*(tp->p_dmsg) == '\0'))))) { /* * if we have not check modification time and * /etc/ttydefs was modified, need to re-read it */ if (check_modtime && mod_ttydefs()) { check_modtime = FALSE; (void) sigprocmask(SIG_SETMASK, NULL, &cset); tset = cset; (void) sigaddset(&tset, SIGCLD); (void) sigprocmask(SIG_SETMASK, &tset, NULL); free_defs(); #ifdef DEBUG debug("/etc/ttydefs is modified, re-read it"); #endif read_ttydefs(NULL, FALSE); (void) sigprocmask(SIG_SETMASK, &cset, NULL); } open_device(tp); if (tp->p_fd > 0) got_carrier(tp); } else if (((tp->p_status == LOCKED) || (tp->p_status == SESSION) || (tp->p_status == UNACCESS)) && (tp->p_fd > 0) && (!((State == PM_DISABLED) && ((tp->p_dmsg == NULL)||(*(tp->p_dmsg) == '\0'))))) { if (check_modtime && mod_ttydefs()) { check_modtime = FALSE; (void) sigprocmask(SIG_SETMASK, NULL, &cset); tset = cset; (void) sigaddset(&tset, SIGCLD); (void) sigprocmask(SIG_SETMASK, &tset, NULL); free_defs(); #ifdef DEBUG debug("/etc/ttydefs is modified, re-read it"); #endif read_ttydefs(NULL, FALSE); (void) sigprocmask(SIG_SETMASK, &cset, NULL); } tp->p_status = VALID; open_device(tp); if (tp->p_fd > 0) got_carrier(tp); } } } void set_softcar(struct pmtab *pmptr) { int fd, val = 0; #ifdef DEBUG debug("in set_softcar"); #endif /* * If soft carrier is not set one way or * the other, leave it alone. */ if (*pmptr->p_softcar == '\0') return; if (*pmptr->p_softcar == 'y') val = 1; if ((fd = open(pmptr->p_device, O_RDONLY|O_NONBLOCK|O_NOCTTY)) < 0) { log("open (%s) failed: %s", pmptr->p_device, strerror(errno)); return; } if (ioctl(fd, TIOCSSOFTCAR, &val) < 0) log("set soft-carrier (%s) failed: %s", pmptr->p_device, strerror(errno)); (void) close(fd); } /* * open_device(pmptr) - open the device * - check device lock * - change owner of device * - push line disciplines * - set termio */ void open_device(struct pmtab *pmptr) { int fd, tmpfd; struct sigaction sigact; #ifdef DEBUG debug("in open_device"); #endif if (pmptr->p_status == GETTY) { revokedevaccess(pmptr->p_device, 0, 0, 0); if ((fd = open(pmptr->p_device, O_RDWR)) == -1) fatal("open (%s) failed: %s", pmptr->p_device, strerror(errno)); } else { if (check_spawnlimit(pmptr) == -1) { pmptr->p_status = NOTVALID; log("service <%s> is respawning too rapidly", pmptr->p_tag); return; } if (pmptr->p_fd > 0) { /* file already open */ fd = pmptr->p_fd; pmptr->p_fd = 0; } else if ((fd = open(pmptr->p_device, O_RDWR|O_NONBLOCK)) == -1) { log("open (%s) failed: %s", pmptr->p_device, strerror(errno)); if ((errno == ENODEV) || (errno == EBUSY)) { pmptr->p_status = UNACCESS; Nlocked++; if (Nlocked == 1) { sigact.sa_flags = 0; sigact.sa_handler = sigalarm; (void) sigemptyset(&sigact.sa_mask); (void) sigaction(SIGALRM, &sigact, NULL); (void) alarm(ALARMTIME); } } else Retry = TRUE; return; } /* set close-on-exec flag */ if (fcntl(fd, F_SETFD, 1) == -1) fatal("F_SETFD fcntl failed: %s", strerror(errno)); if (tm_checklock(fd) != 0) { pmptr->p_status = LOCKED; (void) close(fd); Nlocked++; if (Nlocked == 1) { sigact.sa_flags = 0; sigact.sa_handler = sigalarm; (void) sigemptyset(&sigact.sa_mask); (void) sigaction(SIGALRM, &sigact, NULL); (void) alarm(ALARMTIME); } return; } if (check_session(fd) != 0) { if ((Initialized) && (pmptr->p_inservice != SESSION)) { log("Warning -- active session exists on <%s>", pmptr->p_device); } else { /* * this may happen if a service is running * and ttymon dies and is restarted, * or another process is running on the * port. */ pmptr->p_status = SESSION; pmptr->p_inservice = 0; (void) close(fd); Nlocked++; if (Nlocked == 1) { sigact.sa_flags = 0; sigact.sa_handler = sigalarm; (void) sigemptyset(&sigact.sa_mask); (void) sigaction(SIGALRM, &sigact, NULL); (void) alarm(ALARMTIME); } return; } } pmptr->p_inservice = 0; } if (pmptr->p_ttyflags & H_FLAG) { /* drop DTR */ (void) hang_up_line(fd); /* * After hang_up_line, the stream is in STRHUP state. * We need to do another open to reinitialize streams * then we can close one fd */ if ((tmpfd = open(pmptr->p_device, O_RDWR|O_NONBLOCK)) == -1) { log("open (%s) failed: %s", pmptr->p_device, strerror(errno)); Retry = TRUE; (void) close(fd); return; } (void) close(tmpfd); } #ifdef DEBUG debug("open_device (%s), fd = %d", pmptr->p_device, fd); #endif /* Change ownership of the tty line to root/uucp and */ /* set protections to only allow root/uucp to read the line. */ if (pmptr->p_ttyflags & (B_FLAG|C_FLAG)) (void) fchown(fd, Uucp_uid, Tty_gid); else (void) fchown(fd, ROOTUID, Tty_gid); (void) fchmod(fd, 0620); if ((pmptr->p_modules != NULL)&&(*(pmptr->p_modules) != '\0')) { if (push_linedisc(fd, pmptr->p_modules, pmptr->p_device) == -1) { Retry = TRUE; (void) close(fd); return; } } if (initial_termio(fd, pmptr) == -1) { Retry = TRUE; (void) close(fd); return; } (void) di_devperm_logout((const char *)pmptr->p_device); pmptr->p_fd = fd; } /* * set_poll(fdp) - put all fd's in a pollfd array * - set poll event to POLLIN and POLLMSG * - return number of fd to be polled */ static int set_poll(struct pollfd *fdp) { struct pmtab *tp; int nfd = 0; for (tp = PMtab; tp; tp = tp->p_next) { if (tp->p_fd > 0) { fdp->fd = tp->p_fd; fdp->events = POLLIN; fdp++; nfd++; } } return (nfd); } /* * check_spawnlimit - return 0 if spawnlimit is not reached * - otherwise return -1 */ static int check_spawnlimit(struct pmtab *pmptr) { time_t now; (void) time(&now); if (pmptr->p_time == 0L) pmptr->p_time = now; if (pmptr->p_respawn >= SPAWN_LIMIT) { if ((now - pmptr->p_time) < SPAWN_INTERVAL) { pmptr->p_time = now; pmptr->p_respawn = 0; return (-1); } pmptr->p_time = now; pmptr->p_respawn = 0; } pmptr->p_respawn++; return (0); } /* * mod_ttydefs - to check if /etc/ttydefs has been modified * - return TRUE if file modified * - otherwise, return FALSE */ static int mod_ttydefs(void) { struct stat statbuf; if (stat(TTYDEFS, &statbuf) == -1) { /* if stat failed, don't bother reread ttydefs */ return (FALSE); } if ((long)statbuf.st_mtime != Mtime) { Mtime = (long)statbuf.st_mtime; return (TRUE); } return (FALSE); } /* * free_defs - free the Gdef table */ static void free_defs(void) { int i; struct Gdef *tp; tp = &Gdef[0]; for (i = 0; i < Ndefs; i++, tp++) { free(tp->g_id); free(tp->g_iflags); free(tp->g_fflags); free(tp->g_nextid); tp->g_id = NULL; tp->g_iflags = NULL; tp->g_fflags = NULL; tp->g_nextid = NULL; } Ndefs = 0; } /* * rebuild flags entry using speed from ttymode. */ static char * merge_flags(char *src, char *ttymode) { char *data, *ptr, *flags; /* copy speed entry */ data = strsave(src); flags = strsave(ttymode); ptr = strchr(flags, ','); if (ptr == NULL) { /* ttymode is corrupted */ free(flags); return (data); } *ptr = '\0'; ptr = flags; flags = strsave(flags); free(ptr); /* * The flags line is supposed to have stty keywords separated by space. * We need to split up the keywords, replace the speed and * reconstruct the flags line. */ ptr = strtok(data, " \t"); if (ptr == NULL) { free(data); return (flags); } do { char *tmp; /* skip speed */ if (isdigit(*ptr)) continue; if (asprintf(&tmp, "%s %s", flags, ptr) <= 0) { /* should we complain? */ break; } free(flags); flags = tmp; } while ((ptr = strtok(NULL, " \t")) != NULL); free(data); return (flags); } /* * struct Gdef *get_speed(ttylabel) * - search "/etc/ttydefs" for speed and term. specification * using "ttylabel". If "ttylabel" is NULL, default * to DEFAULT * - for /dev/console, if we are in fact using serial console, * use ttyX-mode value to get speed. This allows us to use * the value set for serial console either from firmware (or BMC sol), * or boot loader default. * arg: ttylabel - label/id of speed settings. */ struct Gdef * get_speed(struct pmtab *pmptr) { static struct Gdef serial = { 0 }; struct Gdef *sp; char *ttylabel = pmptr->p_ttylabel; if ((ttylabel != NULL) && (*ttylabel != '\0')) { if ((sp = find_def(ttylabel)) == NULL) { log("unable to find <%s> in \"%s\"", ttylabel, TTYDEFS); sp = &DEFAULT; /* use default */ } } else { sp = &DEFAULT; /* use default */ } /* * if this is not /dev/console or /dev/console is not using serial, * we are done. */ if (pmptr->p_ttymode == NULL || strcmp(pmptr->p_device, "/dev/console") != 0) return (sp); /* is entry for serial set up? */ if (serial.g_id == NULL) { /* * Copy data from sp, except we need to update inital and * final flags. */ serial.g_id = strsave(sp->g_id); serial.g_iflags = merge_flags(sp->g_iflags, pmptr->p_ttymode); serial.g_fflags = merge_flags(sp->g_fflags, pmptr->p_ttymode); serial.g_autobaud = sp->g_autobaud; serial.g_nextid = strsave(sp->g_nextid); } return (&serial); } /* * setup_PCpipe() - setup the pipe between Parent and Children * - the pipe is used for a tmchild to send its * pid to inform ttymon that it is about to * invoke service * - the pipe also serves as a mean for tmchild * to detect failure of ttymon */ void setup_PCpipe(void) { int flag = 0; if (pipe(PCpipe) == -1) fatal("pipe() failed: %s", strerror(errno)); /* set close-on-exec flag */ if (fcntl(PCpipe[0], F_SETFD, 1) == -1) fatal("F_SETFD fcntl failed: %s", strerror(errno)); if (fcntl(PCpipe[1], F_SETFD, 1) == -1) fatal("F_SETFD fcntl failed: %s", strerror(errno)); /* set O_NONBLOCK flag */ if (fcntl(PCpipe[0], F_GETFL, flag) == -1) fatal("F_GETFL failed: %s", strerror(errno)); flag |= O_NONBLOCK; if (fcntl(PCpipe[0], F_SETFL, flag) == -1) fatal("F_SETFL failed: %s", strerror(errno)); /* set message discard mode */ if (ioctl(PCpipe[0], I_SRDOPT, RMSGD) == -1) fatal("I_SRDOPT RMSGD failed: %s", strerror(errno)); /* register to receive SIGPOLL when data come */ if (ioctl(PCpipe[0], I_SETSIG, S_INPUT) == -1) fatal("I_SETSIG S_INPUT failed: %s", strerror(errno)); #ifdef DEBUG log("PCpipe[0]\t = %d", PCpipe[0]); log("PCpipe[1]\t = %d", PCpipe[1]); #endif } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.7 */ #define FALSE 0 #define TRUE 1 #define SUCCESS 0 #define FAILURE -1 /* initialize device failed */ #define LOCKED -2 /* device is locked by others */ #define SESSION -3 /* device has active session */ #define UNACCESS -4 /* device not accessible */ #define ACTIVE 1 #define FINISHED 0 /* * flags to indicate the field of /etc/ttydefs * Note: order is important because it corresponds to * the order of fields in the file */ #define T_TTYLABEL 1 #define T_IFLAGS 2 #define T_FFLAGS 3 #define T_AUTOBAUD 4 #define T_NEXTLABEL 5 /* * flags to indicate the field of pmtab * Note: order is important because it corresponds to * the order of fields in the file */ #define P_TAG 1 #define P_FLAGS 2 #define P_IDENTITY 3 #define P_RES1 4 #define P_RES2 5 #define P_RES3 6 #define P_DEVICE 7 #define P_TTYFLAGS 8 #define P_COUNT 9 #define P_SERVER 10 #define P_TIMEOUT 11 #define P_TTYLABEL 12 #define P_MODULES 13 #define P_PROMPT 14 #define P_DMSG 15 #define P_TERMTYPE 16 #define P_SOFTCAR 17 /* * termio mode */ #define RAW 0x1 /* raw mode */ #define CANON 0x2 /* canonical mode */ /* * return value for peeking input data */ #define GOODNAME 1 #define NONAME 0 #define BADSPEED -1 #define MAXID 15 /* Maximum length the "g_id" and "g_nextid" \ * strings can take. Longer ones will be \ * truncated. \ */ #define MAXARGS 64 /* Maximum number of arguments that can be \ * passed to "login" \ */ #define SPAWN_LIMIT 15 /* respawn allowed within SPAWN_INTERVAL */ #define SPAWN_INTERVAL (2*60) #define UUCP "uucp" /* owner of bi-directional devices */ #define TTY "tty" /* group name of all devices */ #define ROOTUID 0 /* root uid */ #define LOGDIR "/var/saf/" /* home dir of all saf log */ #define LOGFILE "log" /* log file */ #define OLOGFILE "o.log" /* saved log file */ #define TLOGFILE "t.log" /* temp log file */ #define PIDFILE "_pid" /* pid file */ #define PMTABFILE "_pmtab" /* pmtab file */ #define PMPIPE "_pmpipe" /* pmpipe */ #define SACPIPE "../_sacpipe" /* sacpipe */ #define TTYDEFS "/etc/ttydefs" /* ttydefs file */ #define CONSOLE "/dev/syscon" /* /dev/console */ #ifdef DEBUG #define DBGFILE "debug" /* debug file */ #define EX_DBG "/var/saf/tm_debug" /* debug file for ttymon express*/ #endif #ifdef SYS_NAME #define ISSUEFILE "/etc/issue" /*file to print before prompt */ #endif #define PMTAB_VERS 1 /* pmtab version number */ #define TTYDEFS_VERS 1 /* /etc/ttydefs version number */ #define MAXDEFS 100 /* max entries Gdef table can have */ /* * - ttymon reserves 7 fd for the following use: * - pid, log, pmpipe, sacpipe, pmtab, PCpipe[0], PCpipe[1]. * - if DEBUG is on, reserve one more for debug file * - fd for each file * pid 0 * sacpipe 1 * pmpipe 2 * log 3 * PCpipe[0] 4 * PCpipe[1] 5 * debug 6 * pmtab floating, any fd will do */ #ifdef DEBUG #define FILE_RESERVED 8 #else #define FILE_RESERVED 7 #endif #define TM_MAXCLASS 1 /* maxclass of SAC msg ttymon understands */ /* * flag value for strcheck() */ #define NUM 0 #define ALNUM 1 #define ALARMTIME 60 /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include "uucp.h" #include /* #include */ /* #include */ static struct stat _st_buf; static char lockname[BUFSIZ]; static void stlock(char *); static int onelock(char *, char *, char *); /* * make a lock file with given 'name' * If one already exists, send a signal 0 to the process--if * it fails, then unlink it and make a new one. * * input: * name - name of the lock file to make * * return: * 0 -> success * FAIL -> failure */ GLOBAL int mklock(char *name) { static char pid[SIZEOFPID+2] = { '\0' }; /* +2 for '\n' and NULL */ static char tempfile[MAXNAMESIZE]; #ifdef V8 char *cp; #endif if (pid[0] == '\0') { (void) sprintf(pid, "%*ld\n", SIZEOFPID, (long)getpid()); (void) sprintf(tempfile, "%s/LTMP.%ld", X_LOCKDIR, (long)getpid()); } #ifdef V8 /* this wouldn't be a problem if we used lock directories */ /* some day the truncation of system names will bite us */ cp = rindex(name, '/'); if (cp++ != CNULL) if (strlen(cp) > MAXBASENAME) *(cp+MAXBASENAME) = NULLCHAR; #endif /* V8 */ if (onelock(pid, tempfile, name) == -1) { (void) unlink(tempfile); if (cklock(name)) { return (FAIL); } else { if (onelock(pid, tempfile, name)) { (void) unlink(tempfile); DEBUG(4, "ulockf failed in onelock()\n%s", ""); return (FAIL); } } } stlock(name); return (0); } /* * check to see if the lock file exists and is still active * - use kill(pid,0) * * return: * 0 -> success (lock file removed - no longer active * FAIL -> lock file still active */ GLOBAL int cklock(char *name) { int ret; pid_t lpid = -1; char alpid[SIZEOFPID+2]; /* +2 for '\n' and NULL */ int fd; fd = open(name, O_RDONLY); DEBUG(4, "ulockf name %s\n", name); if (fd == -1) { if (errno == ENOENT) /* file does not exist -- OK */ return (0); DEBUG(4, "Lock File--can't read (errno %d) --remove it!\n", errno); goto unlk; } ret = read(fd, (char *)alpid, SIZEOFPID + 1); /* +1 for '\n' */ (void) close(fd); if (ret != (SIZEOFPID+1)) { DEBUG(4, "Lock File--bad format--remove it!\n%s", ""); goto unlk; } lpid = (pid_t)strtol(alpid, NULL, 10); if ((ret = kill(lpid, 0)) == 0 || errno == EPERM) { DEBUG(4, "Lock File--process still active--not removed\n%s", ""); return (FAIL); } else { /* process no longer active */ DEBUG(4, "kill pid (%ld), ", (long)lpid); DEBUG(4, "returned %d", ret); DEBUG(4, "--ok to remove lock file (%s)\n", name); } unlk: if (unlink(name) != 0) { DEBUG(4, "ulockf failed in unlink()\n%s", ""); return (FAIL); } return (0); } #define MAXLOCKS 10 /* maximum number of lock files */ static char *Lockfile[MAXLOCKS]; GLOBAL int Nlocks = 0; /* * put name in list of lock files * return: * none */ static void stlock(char *name) { int i; char *p; for (i = 0; i < Nlocks; i++) { if (Lockfile[i] == NULL) break; } ASSERT(i < MAXLOCKS, "TOO MANY LOCKS", "", i); if (i >= Nlocks) i = Nlocks++; p = calloc((unsigned)strlen(name) + 1, sizeof (char)); ASSERT(p != NULL, "CAN NOT ALLOCATE FOR", name, 0); (void) strcpy(p, name); Lockfile[i] = p; } /* * remove the named lock. If named lock is NULL, * then remove all locks currently in list. * return: * none */ GLOBAL void rmlock(char *name) { int i; #ifdef V8 char *cp; cp = rindex(name, '/'); if (cp++ != CNULL) if (strlen(cp) > MAXBASENAME) *(cp+MAXBASENAME) = NULLCHAR; #endif /* V8 */ for (i = 0; i < Nlocks; i++) { if (Lockfile[i] == NULL) continue; if (name == NULL || EQUALS(name, Lockfile[i])) { (void) unlink(Lockfile[i]); free(Lockfile[i]); Lockfile[i] = NULL; } } } /* * remove a lock file * * Parameters: * pre - Path and first part of file name of the lock file to be * removed. * s - The suffix part of the lock file. The name of the lock file * will be derrived by concatenating pre, a period, and s. * * return: * none */ GLOBAL void delock(char *pre, char *s) { char ln[MAXNAMESIZE]; (void) sprintf(ln, "%s.%s", pre, s); BASENAME(ln, '/')[MAXBASENAME] = '\0'; rmlock(ln); } /* * create lock file * * Parameters: * pre - Path and first part of file name of the lock file to be * created. * name - The suffix part of the lock file. The name of the lock file * will be derrived by concatenating pre, a period, and name. * * return: * 0 -> success * FAIL -> failure */ GLOBAL int mlock(char *pre, char *name) { char lname[MAXNAMESIZE]; /* * if name has a '/' in it, then it's a device name and it's * not in /dev (i.e., it's a remotely-mounted device or it's * in a subdirectory of /dev). in either case, creating our normal * lockfile (/var/spool/locks/LCK..) is going to bomb if * is "/remote/dev/term/14" or "/dev/net/foo/clone", so never * mind. since we're using advisory filelocks on the devices * themselves, it'll be safe. * * of course, programs and people who are used to looking at the * lockfiles to find out what's going on are going to be a trifle * misled. we really need to re-consider the lockfile naming structure * to accomodate devices in directories other than /dev ... maybe in * the next release. */ if (strchr(name, '/') != NULL) return (0); (void) sprintf(lname, "%s.%s", pre, BASENAME(name, '/')); BASENAME(lname, '/')[MAXBASENAME] = '\0'; return (mklock(lname)); } /* * makes a lock on behalf of pid. * input: * pid - process id * tempfile - name of a temporary in the same file system * name - lock file name (full path name) * return: * -1 - failed * 0 - lock made successfully */ static int onelock(char *pid, char *tempfile, char *name) { int fd; char cb[100]; fd = creat(tempfile, (mode_t)0444); if (fd < 0) { (void) sprintf(cb, "%s %s %d", tempfile, name, errno); logent("ULOCKC", cb); if ((errno == EMFILE) || (errno == ENFILE)) (void) unlink(tempfile); return (-1); } /* +1 for '\n' */ if (write(fd, pid, SIZEOFPID + 1) != (SIZEOFPID + 1)) { (void) sprintf(cb, "%s %s %d", tempfile, name, errno); logent("ULOCKW", cb); (void) unlink(tempfile); return (-1); } (void) chmod(tempfile, (mode_t)0444); (void) chown(tempfile, UUCPUID, UUCPGID); (void) close(fd); if (link(tempfile, name) < 0) { DEBUG(4, "%s: ", strerror(errno)); DEBUG(4, "link(%s, ", tempfile); DEBUG(4, "%s)\n", name); if (unlink(tempfile) < 0) { (void) sprintf(cb, "ULK err %s %d", tempfile, errno); logent("ULOCKLNK", cb); } return (-1); } if (unlink(tempfile) < 0) { (void) sprintf(cb, "%s %d", tempfile, errno); logent("ULOCKF", cb); } return (0); } /* * fd_mklock(fd) - lock the device indicated by fd is possible * * return - * SUCCESS - this process now has the fd locked * FAIL - this process was not able to lock the fd */ GLOBAL int fd_mklock(int fd) { int tries = 0; if (fstat(fd, &_st_buf) != 0) return (FAIL); (void) sprintf(lockname, "%s.%3.3lu.%3.3lu.%3.3lu", L_LOCK, (unsigned long) major(_st_buf.st_dev), (unsigned long) major(_st_buf.st_rdev), (unsigned long) minor(_st_buf.st_rdev)); if (mklock(lockname) == FAIL) return (FAIL); while (lockf(fd, F_TLOCK, 0L) != 0) { DEBUG(7, "fd_mklock: lockf returns %d\n", errno); if ((++tries >= MAX_LOCKTRY) || (errno != EAGAIN)) { rmlock(lockname); logent("fd_mklock", "lockf failed"); return (FAIL); } (void) sleep(2); } DEBUG(7, "fd_mklock: ok\n%s", ""); return (SUCCESS); } /* * fn_cklock(name) - determine if the device indicated by name is locked * * return - * SUCCESS - the name is not locked * FAIL - the name is locked by another process */ GLOBAL int fn_cklock(char *name) { /* we temporarily use lockname to hold full path name */ (void) sprintf(lockname, "%s%s", (*name == '/' ? "" : "/dev/"), name); if (stat(lockname, &_st_buf) != 0) return (FAIL); (void) sprintf(lockname, "%s.%3.3lu.%3.3lu.%3.3lu", L_LOCK, (unsigned long) major(_st_buf.st_dev), (unsigned long) major(_st_buf.st_rdev), (unsigned long) minor(_st_buf.st_rdev)); return (cklock(lockname)); } /* * fd_cklock(fd) - determine if the device indicated by fd is locked * * return - * SUCCESS - the fd is not locked * FAIL - the fd is locked by another process */ GLOBAL int fd_cklock(int fd) { if (fstat(fd, &_st_buf) != 0) return (FAIL); (void) sprintf(lockname, "%s.%3.3lu.%3.3lu.%3.3lu", L_LOCK, (unsigned long) major(_st_buf.st_dev), (unsigned long) major(_st_buf.st_rdev), (unsigned long) minor(_st_buf.st_rdev)); if (cklock(lockname) == FAIL) return (FAIL); else return (lockf(fd, F_TEST, 0L)); } /* * remove the locks associated with the device file descriptor * * return - * SUCCESS - both BNU lock file and advisory locks removed * FAIL - */ GLOBAL void fd_rmlock(int fd) { if (fstat(fd, &_st_buf) == 0) { (void) sprintf(lockname, "%s.%3.3lu.%3.3lu.%3.3lu", L_LOCK, (unsigned long) major(_st_buf.st_dev), (unsigned long) major(_st_buf.st_rdev), (unsigned long) minor(_st_buf.st_rdev)); rmlock(lockname); } (void) lockf(fd, F_ULOCK, 0L); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _UUCP_H #define _UUCP_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include "parms.h" #ifdef DIAL #define EXTERN static #define GLOBAL static #else #define EXTERN extern #define GLOBAL #endif #ifdef BSD4_2 #define V7 #undef NONAP #undef FASTTIMER #endif /* BSD4_2 */ #ifdef FASTTIMER #undef NONAP #endif #ifdef V8 #define V7 #endif /* V8 */ #include #include #include #include /* * param.h includes types.h and signal.h in 4bsd */ #ifdef V7 #include #include #else /* !V7 */ #include #include #include #include #endif #include #include #include #ifdef BSD4_2 #include #else /* !BSD4_2 */ #include #endif #include #include #ifdef ATTSV #include #endif /* ATTSV */ #ifdef RT #include "rt/types.h" #include "rt/unix/param.h" #include "rt/stat.h" #include #endif /* RT */ /* what mode should user files be allowed to have upon creation? */ /* NOTE: This does not allow setuid or execute bits on transfer. */ #define LEGALMODE (mode_t)0666 /* what mode should public files have upon creation? */ #define PUB_FILEMODE (mode_t)0666 /* what mode should log files have upon creation? */ #define LOGFILEMODE (mode_t)0644 /* what mode should C. files have upon creation? */ #define CFILEMODE (mode_t)0644 /* what mode should D. files have upon creation? */ #define DFILEMODE (mode_t)0600 /* define the value of PUBMASK, used for creating "public" directories */ #define PUBMASK (mode_t)0000 /* what mode should public directories have upon creation? */ #define PUB_DIRMODE (mode_t)0777 /* define the value of DIRMASK, used for creating "system" subdirectories */ #define DIRMASK (mode_t)0022 #define MAXSTART 300 /* how long to wait on startup */ /* define the last characters for ACU (used for 801/212 dialers) */ #define ACULAST "<" /* * caution - the following names are also in Makefile * any changes here have to also be made there * * it's a good idea to make directories .foo, since this ensures * that they'll be ignored by processes that search subdirectories in SPOOL * * XQTDIR=/var/uucp/.Xqtdir * CORRUPT=/var/uucp/.Corrupt * LOGDIR=/var/uucp/.Log * SEQDIR=/var/uucp/.Sequence * STATDIR=/var/uucp/.Status * */ /* where to put the STST. files? */ #define STATDIR "/var/uucp/.Status" /* where should logfiles be kept? */ #define LOGUUX "/var/uucp/.Log/uux" #define LOGUUXQT "/var/uucp/.Log/uuxqt" #define LOGUUCP "/var/uucp/.Log/uucp" #define LOGCICO "/var/uucp/.Log/uucico" #define CORRUPTDIR "/var/uucp/.Corrupt" /* some sites use /var/uucp/.XQTDIR here */ /* use caution since things are linked into there */ #define XQTDIR "/var/uucp/.Xqtdir" /* how much of a system name can we print in a [CX]. file? */ /* MAXBASENAME - 1 (pre) - 1 ('.') - 1 (grade) - 4 (sequence number) */ #define SYSNSIZE (MAXBASENAME - 7) #ifdef USRSPOOLLOCKS #define LOCKPRE "/var/spool/locks/LCK." #else #define LOCKPRE "/var/spool/uucp/LCK." #endif /* USRSPOOLLOCKS */ #define SQFILE "/etc/uucp/SQFILE" #define SQTMP "/etc/uucp/SQTMP" #define SLCKTIME 5400 /* system/device timeout (LCK.. files) */ #define DIALCODES "/etc/uucp/Dialcodes" #define PERMISSIONS "/etc/uucp/Permissions" #define SPOOL "/var/spool/uucp" #define SEQDIR "/var/uucp/.Sequence" #define X_LOCKTIME 3600 #ifdef USRSPOOLLOCKS #define SEQLOCK "/var/spool/locks/LCK.SQ." #define SQLOCK "/var/spool/locks/LCK.SQ" #define X_LOCK "/var/spool/locks/LCK.X" #define S_LOCK "/var/spool/locks/LCK.S" #define L_LOCK "/var/spool/locks/LK" #define X_LOCKDIR "/var/spool/locks" /* must be dir part of above */ #else #define SEQLOCK "/var/spool/uucp/LCK.SQ." #define SQLOCK "/var/spool/uucp/LCK.SQ" #define X_LOCK "/var/spool/uucp/LCK.X" #define S_LOCK "/var/spool/uucp/LCK.S" #define L_LOCK "/var/spool/uucp/LK" #define X_LOCKDIR "/var/spool/uucp" /* must be dir part of above */ #endif /* USRSPOOLLOCKS */ #define X_LOCKPRE "LCK.X" /* must be last part of above */ #define PUBDIR "/var/spool/uucppublic" #define ADMIN "/var/uucp/.Admin" #define ERRLOG "/var/uucp/.Admin/errors" #define SYSLOG "/var/uucp/.Admin/xferstats" #define RMTDEBUG "/var/uucp/.Admin/audit" #define CLEANUPLOGFILE "/var/uucp/.Admin/uucleanup" #define CMDLOG "/var/uucp/.Admin/command" #define PERFLOG "/var/uucp/.Admin/perflog" #define ACCOUNT "/var/uucp/.Admin/account" #define SECURITY "/var/uucp/.Admin/security" #define WORKSPACE "/var/uucp/.Workspace" #define SQTIME 60 #define TRYCALLS 2 /* number of tries to dial call */ #define MINULIMIT (1L<<11) /* minimum reasonable ulimit */ #define MAX_LOCKTRY 5 /* number of attempts to lock device */ /* * CDEBUG is for communication line debugging * DEBUG is for program debugging * #define SMALL to compile without the DEBUG code */ #ifndef DIAL #define CDEBUG(l, f, s) if (Debug >= l) (void)fprintf(stderr, f, s) #else #define CDEBUG(l, f, s) #define SMALL #endif #ifndef SMALL #define DEBUG(l, f, s) if (Debug >= l) (void)fprintf(stderr, f, s) #else #define DEBUG(l, f, s) #endif /* SMALL */ /* * VERBOSE is used by cu and ct to inform the user of progress * In other programs, the Value of Verbose is always 0. */ #define VERBOSE(f, s) { if (Verbose > 0) (void)fprintf(stderr, f, s); } #define PREFIX(pre, str) (strncmp((pre), (str), strlen(pre)) == SAME) #define BASENAME(str, c) ((Bnptr = strrchr((str), c)) ? (Bnptr + 1) : (str)) #define EQUALS(a, b) \ ((a != CNULL) && (b != CNULL) && (strcmp((a), (b)) == SAME)) #define EQUALSN(a, b, n) \ ((a != CNULL) && (b != CNULL) && (strncmp((a), (b), (n)) == SAME)) #define LASTCHAR(s) (s + strlen(s) - 1) #define SAME 0 #define ANYREAD 04 #define ANYWRITE 02 #define FAIL -1 #define SUCCESS 0 #define NULLCHAR '\0' #define CNULL NULL #define STBNULL NULL #define MASTER 1 #define SLAVE 0 #define MAXBASENAME 14 /* should be DIRSIZ but that is now fs dependent */ #define MAXFULLNAME BUFSIZ #define MAXNAMESIZE 64 /* /var/spool/uucp/<14 chars>/<14 chars>+slop */ #define CONNECTTIME 30 #define EXPECTTIME 45 #define MSGTIME 60 #define NAMESIZE MAXBASENAME+1 #define SIZEOFPID 10 /* maximum number of digits in a pid */ #define EOTMSG "\004\n\004\n" #define CALLBACK 1 /* manifests for sysfiles.c's sysaccess() */ /* check file access for REAL user id */ #define ACCESS_SYSTEMS 1 #define ACCESS_DEVICES 2 #define ACCESS_DIALERS 3 /* check file access for EFFECTIVE user id */ #define EACCESS_SYSTEMS 4 #define EACCESS_DEVICES 5 #define EACCESS_DIALERS 6 /* manifest for chkpth flag */ #define CK_READ 0 #define CK_WRITE 1 /* * commands */ #define SHELL "/usr/bin/sh" #define MAIL "mail" #define UUCICO "/usr/lib/uucp/uucico" #define UUXQT "/usr/lib/uucp/uuxqt" #define UUX "/usr/bin/uux" #define UUCP "/usr/bin/uucp" /* system status stuff */ #define SS_OK 0 #define SS_NO_DEVICE 1 #define SS_TIME_WRONG 2 #define SS_INPROGRESS 3 #define SS_CONVERSATION 4 #define SS_SEQBAD 5 #define SS_LOGIN_FAILED 6 #define SS_DIAL_FAILED 7 #define SS_BAD_LOG_MCH 8 #define SS_LOCKED_DEVICE 9 #define SS_ASSERT_ERROR 10 #define SS_BADSYSTEM 11 #define SS_CANT_ACCESS_DEVICE 12 #define SS_DEVICE_FAILED 13 /* used for interface failure */ #define SS_WRONG_MCH 14 #define SS_CALLBACK 15 #define SS_RLOCKED 16 #define SS_RUNKNOWN 17 #define SS_RLOGIN 18 #define SS_UNKNOWN_RESPONSE 19 #define SS_STARTUP 20 #define SS_CHAT_FAILED 21 #define SS_CALLBACK_LOOP 22 #define MAXPH 60 /* maximum phone string size */ #define MAXC BUFSIZ #define TRUE 1 #define FALSE 0 #define NAMEBUF 32 /* The call structure is used by ct.c, cu.c, and dial.c. */ struct call { char *speed; /* transmission baud rate */ char *line; /* device name for outgoing line */ char *telno; /* ptr to tel-no digit string */ char *type; /* type of device to use for call. */ }; /* structure of an Systems file line */ #define F_MAX 50 /* max number of fields in Systems file line */ #define F_NAME 0 #define F_TIME 1 #define F_TYPE 2 #define F_CLASS 3 /* an optional prefix and the speed */ #define F_PHONE 4 #define F_LOGIN 5 /* structure of an Devices file line */ #define D_TYPE 0 #define D_LINE 1 #define D_CALLDEV 2 #define D_CLASS 3 #define D_CALLER 4 #define D_ARG 5 #define D_MAX 50 /* max number of fields in Devices file line */ #define D_ACU 1 #define D_DIRECT 2 #define D_PROT 4 #define GRADES "/etc/uucp/Grades" #define D_QUEUE 'Z' /* default queue */ /* past here, local changes are not recommended */ #define CMDPRE 'C' #define DATAPRE 'D' #define XQTPRE 'X' /* * stuff for command execution */ #define X_RQDFILE 'F' #define X_STDIN 'I' #define X_STDOUT 'O' #define X_STDERR 'E' #define X_CMD 'C' #define X_USER 'U' #define X_BRINGBACK 'B' #define X_MAILF 'M' #define X_RETADDR 'R' #define X_COMMENT '#' #define X_NONZERO 'Z' #define X_SENDNOTHING 'N' #define X_SENDZERO 'n' /* This structure describes call routines */ struct caller { char *CA_type; int (*CA_caller)(); }; /* structure for a saved C file */ struct cs_struct { char file[NAMESIZE]; char sys[NAMESIZE+5]; char sgrade[NAMESIZE]; char grade; long jsize; }; /* This structure describes dialing routines */ struct dialer { char *DI_type; int (*DI_dialer)(); }; struct nstat { pid_t t_pid; /* process id */ time_t t_start; /* start time */ time_t t_scall; /* start call to system */ time_t t_ecall; /* end call to system */ time_t t_tacu; /* acu time */ time_t t_tlog; /* login time */ time_t t_sftp; /* start file transfer protocol */ time_t t_sxf; /* start xfer */ time_t t_exf; /* end xfer */ time_t t_eftp; /* end file transfer protocol */ time_t t_qtime; /* time file queued */ int t_ndial; /* # of dials */ int t_nlogs; /* # of login trys */ struct tms t_tbb; /* start execution times */ struct tms t_txfs; /* xfer start times */ struct tms t_txfe; /* xfer end times */ struct tms t_tga; /* garbage execution times */ }; /* This structure describes the values from Limits file */ struct limits { int totalmax; /* overall limit */ int sitemax; /* limit per site */ char mode[64]; /* uucico mode */ }; /* external declarations */ EXTERN int (*Read)(), (*Write)(); EXTERN int (*Ioctl)(int, int, ...); EXTERN int Ifn, Ofn; EXTERN int Debug, Verbose; EXTERN uid_t Uid, Euid; /* user-id and effective-uid */ EXTERN long Ulimit; EXTERN mode_t Dev_mode; /* save device mode here */ EXTERN char Wrkdir[]; EXTERN long Retrytime; EXTERN char **Env; EXTERN char Uucp[]; EXTERN char Pchar; EXTERN struct nstat Nstat; EXTERN char Dc[]; /* line name */ EXTERN int Seqn; /* sequence # */ EXTERN int Role; EXTERN int Sgrades; /* flag for administrator defined service grades */ EXTERN char Grade; EXTERN char Logfile[]; EXTERN char Rmtname[]; EXTERN char JobGrade[]; EXTERN char User[]; EXTERN char Loginuser[]; EXTERN char *Spool; EXTERN char *Pubdir; EXTERN char Myname[]; EXTERN char Progname[]; EXTERN char RemSpool[]; EXTERN char *Bnptr; /* used when BASENAME macro is expanded */ EXTERN int SizeCheck; /* ulimit check supported flag */ EXTERN long RemUlimit; /* remote ulimit if supported */ EXTERN int Restart; /* checkpoint restart supported flag */ EXTERN char Jobid[]; /* Jobid of current C. file */ EXTERN int Uerror; /* global error code */ EXTERN char *UerrorText[]; /* text for error code */ #define UERRORTEXT UerrorText[Uerror] #define UTEXT(x) UerrorText[x] /* things get kind of tricky beyond this point -- please stay out */ #ifdef ATTSV #define index strchr #define rindex strrchr #define vfork fork #define ATTSVKILL #define UNAME #else #define strchr index #define strrchr rindex #endif EXTERN struct stat __s_; #define READANY(f) \ ((stat((f), &__s_) == 0) && ((__s_.st_mode & (0004)) != 0)) #define READSOME(f) \ ((stat((f), &__s_) == 0) && ((__s_.st_mode & (0444)) != 0)) #define WRITEANY(f) \ ((stat((f), &__s_) == 0) && ((__s_.st_mode & (0002)) != 0)) #define DIRECTORY(f) \ ((stat((f), &__s_) == 0) && ((__s_.st_mode & (S_IFMT)) == S_IFDIR)) #define NOTEMPTY(f) ((stat((f), &__s_) == 0) && (__s_.st_size != 0)) /* uucp functions and subroutine */ EXTERN void (*genbrk)(); extern int iswrk(), gtwvec(); /* anlwrk.c */ extern void findgrade(); /* grades.c */ extern void chremdir(), mkremdir(); /* chremdir.c */ extern void toCorrupt(); /* cpmv.c */ extern int xmv(); /* cpmv.c */ EXTERN int getargs(); /* getargs.c */ EXTERN void bsfix(); /* getargs.c */ extern char *getprm(); /* getprm.c */ extern char *next_token(); /* permission.c */ extern char *nextarg(); /* permission.c */ extern int getuline(); /* permission.c */ EXTERN void logent(), syslog(), closelog(); /* logent.c */ extern void commandlog(); /* logent.c */ extern time_t millitick(); /* logent.c */ extern unsigned long getfilesize(); /* statlog.c */ extern void putfilesize(); /* statlog.c */ EXTERN char *protoString(); /* permission.c */ extern int logFind(), mchFind(); /* permission.c */ extern int chkperm(), chkpth(); /* permission.c */ extern int cmdOK(), switchRole(); /* permission.c */ extern int callBack(), requestOK(); /* permission.c */ extern int noSpool(); /* permission.c */ extern void myName(); /* permission.c */ extern int mkdirs(); /* expfile.c */ extern int scanlimit(); /* limits.c */ extern void systat(); /* systat.c */ EXTERN int fd_mklock(), fd_cklock(); /* ulockf.c */ EXTERN int fn_cklock(); /* ulockf.c */ EXTERN int mklock(), cklock(), mlock(); /* ulockf.c */ EXTERN void fd_rmlock(), delock(), rmlock(); /* ulockf.c */ extern char *timeStamp(); /* utility.c */ EXTERN void assert(), errent(); /* utility.c */ extern void uucpname(); /* uucpname.c */ extern int versys(); /* versys.c */ extern void xuuxqt(), xuucico(); /* xqt.c */ EXTERN void cleanup(); /* misc main.c */ #define ASSERT(e, s1, s2, i1) if (!(e)) {\ assert(s1, s2, i1, __FILE__, __LINE__);\ cleanup(FAIL); }; #ifdef ATTSV unsigned sleep(); void exit(), setbuf(); long ulimit(); #else /* !ATTSV */ int sleep(), exit(), setbuf(), ftime(); #endif #ifdef UNAME #include #endif /* UNAME */ #ifndef NOUSTAT #ifdef V7USTAT struct ustat { daddr_t f_tfree; /* total free */ ino_t f_tinode; /* total inodes free */ }; #else /* !NOUSTAT && !V7USTAT */ #include #endif /* V7USTAT */ #endif /* NOUSTAT */ #ifdef BSD4_2 char *gethostname(); #endif /* BSD4_2 */ /* messages */ EXTERN char *Ct_OPEN; EXTERN char *Ct_WRITE; EXTERN char *Ct_READ; EXTERN char *Ct_CREATE; EXTERN char *Ct_ALLOCATE; EXTERN char *Ct_LOCK; EXTERN char *Ct_STAT; EXTERN char *Ct_CHOWN; EXTERN char *Ct_CHMOD; EXTERN char *Ct_LINK; EXTERN char *Ct_CHDIR; EXTERN char *Ct_UNLINK; EXTERN char *Wr_ROLE; EXTERN char *Ct_CORRUPT; EXTERN char *Ct_FORK; EXTERN char *Ct_CLOSE; EXTERN char *Ct_BADOWN; EXTERN char *Fl_EXISTS; #ifdef __cplusplus } #endif #endif /* _UUCP_H */