/* ***************************************************************************** * * Copyright 1993 by Xylogics, Inc. ALL RIGHTS RESERVED. * * ALL RIGHTS RESERVED. Licensed Material - Property of Xylogics, Inc. * This software is made available solely pursuant to the terms of a * software license agreement which governs its use. * Unauthorized duplication, distribution or sale are strictly prohibited. * * Module Description: * * Annex reverse-telnet daemon machine-dependent code for IBM AIX * version 3.2 (tested on RISC System/6000 model 350). Based upon * Based upon @(#)telnetd.c 4.26 (Berkeley) 83/08/06 and * machdep.sun version 1.5. * * Original Author: James Carlson Created on: 17MAR93 * * Module Reviewers: * lint, carlson * * Revision Control Information: * $Id: machdep.aix,v 1.5 1995/07/26 11:24:18 carlson Exp $ * * This file created by RCS from * $Source: /annex/common/src/./newrtelnet/RCS/machdep.aix,v $ * * Revision History: * $Log: machdep.aix,v $ * Revision 1.5 1995/07/26 11:24:18 carlson * SPR 4419 -- support version 4.1 AIX. * * Revision 1.4 1994/09/23 10:07:19 carlson * SPR 3557 -- added a bit of debug for pty mode setting and * changed echo-mode not to touch CRMOD flag. * * Revision 1.3 1994/01/07 14:12:14 carlson * SPR 2260 -- AIX 3.2.5 changed everything about the ptys; we now * have to poll the master if we think the slave is closed. * * Revision 1.2 93/07/20 15:25:28 carlson * SPR 1857 -- added sys/select.h and reimplemented hold_open (-o) flag. * * Revision 1.1 93/04/16 16:37:22 carlson * Initial revision * * * This file is currently under revision by: $Locker: $ * ***************************************************************************** */ #include "../inc/config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rtelnet.h" #include "../inc/erpc/netadmp.h" char machrev[] = "$Revision: 1.5 $"; char machsrc[] = "$Source: /annex/common/src/./newrtelnet/RCS/machdep.aix,v $"; static char slave[64]; #define MPXMASTER "/dev/ptc" int process_id; extern int hangup, symbolic, tcp_port, hold_open, never_open, onthefly, transparent, show_pid, cbreakmode, port_num, renametarget, alternate_ptys, drop, binary, noioctls; #ifndef NO_DEBUG extern int so_debug,debug,force_fork; #endif extern char *myname; extern int errno; static char *new_node; /* User's name for pty */ static int progress = 0; /* Internal flags (for cleanup) */ static int erpc_port = 121; /* Port for na communication */ static int using_log_file = 0; /* Flag and file descriptor */ static int pty_was_selected = 0,net_was_selected = 0; static int pty_is_open = 0; static struct sgttyb glob; /* Global tty flags */ static int globchanged = 0; static int slave_pty = -1; static struct sockaddr_in sin = { AF_INET }; static struct sockaddr_in sin2 = { AF_INET }; /* Bits defined in "progress" state variable. */ #define LINKED_TTY 1 #define FORKED_DAEMON 4 extern void cleanup(), show_rtelnet_statistics(); extern int telnet_halt_network(); void pty_close(); int flag_check() { if (alternate_ptys) { (void)fprintf(stderr, "%s: -a flag is not available on this system.\n", myname); return 1; } if (renametarget) { (void)fprintf(stderr, "%s: -R flag is not available on this system.\n", myname); return 1; } if (symbolic) { (void)fprintf(stderr, "%s: -s flag is redundant on this system.\n", myname); return 1; } if (cbreakmode && never_open) { (void)fprintf(stderr, "%s: -c flag is incompatible with -n flag.\n", myname); return 1; } return 0; } void i_perror(str) char *str; { #ifdef NO_DEBUG perror(str); #else extern int sys_nerr; extern char *sys_errlist[]; if (errno < 0 || errno >= sys_nerr) DBG((0,D_ERR,"%s: error %d",str,errno)); else DBG((0,D_ERR,"%s: %s",str,sys_errlist[errno])); #endif } void use_log_file(name) char *name; { int fd; if (using_log_file) { (void)fprintf(stderr, "Duplicate log file name \"%s\" ignored.\n", name); return; } fd = open(name,O_RDWR|O_APPEND|O_CREAT|O_NOCTTY,0666); if (fd < 0) { perror(name); return; } using_log_file = fd; } void start_using_log() { process_id = getpid(); if (using_log_file == 0) return; if (dup2(using_log_file,2) < 0) { using_log_file = 0; perror("dup2 log file"); return; } (void)close(using_log_file); using_log_file = -1; } int name_to_unit(port) char *port; { int num; struct servent *servp; if (tcp_port) { /* User requested interpretation of port number as TCP port */ if (isdigit(*port)) num = atoi(port); else if (servp = getservbyname(port,"tcp")) num = ntohs((u_short)servp->s_port); else { (void)fprintf(stderr, "%s: tcp/%s: unknown service.\n", myname,port); return -1; } if (num <= 0 || num >= 65536) { (void)fprintf(stderr, "%s: Illegal TCP port number -- %d.\n", myname,num); return -1; } } else { /* Interpret port number as Annex serial port number */ num = atoi(port); if (num <= 0 || num > MAX_PORT ) { (void)fprintf(stderr, "%s: Illegal serial port specifier -- \"%s\".\n", myname,port); return -1; } num += PORT_MAP_BASE; } return num; } void resolve_annex(name) char *name; { register struct hostent *host; struct servent *servp; sin.sin_addr.s_addr = inet_addr(name); if (sin.sin_addr.s_addr != -1) sin.sin_family = AF_INET; else { host = gethostbyname(name); if (host) { sin.sin_family = host->h_addrtype; bcopy(host->h_addr, (caddr_t)&sin.sin_addr, host->h_length); } else { (void)fprintf(stderr, "%s: %s: unknown host\n", myname,name); exit(1); } } if (hangup) { servp = getservbyname("erpc", "udp"); if (servp == 0) (void)fprintf(stderr, "%s: udp/erpc: unknown service, using %d.\n", myname,erpc_port); else erpc_port = ntohs((u_short)servp->s_port); } } void set_file_mode(fmode) char *fmode; { (void)fprintf(stderr, "%s: -M flag is useless on this system.\n",myname); exit(1); } void set_user_name(uname) char *uname; { (void)fprintf(stderr, "%s: -u flag is useless on this system.\n",myname); exit(1); } void startup_cleaning() { } static void control_c(dummy) { DBG((1,D_INFO,"control_c interrupt")); cleanup(); } static void increase_debugging(dummy) int dummy; { (void)signal(SIGUSR1,increase_debugging); #ifndef NO_DEBUG if (debug < 5) debug++; DBG((debug,D_INFO,"Setting debug to level %d.",debug)); show_rtelnet_statistics(debug); #endif } static void stop_debugging(dummy) int dummy; { (void)signal(SIGUSR2,stop_debugging); (void)signal(SIGXFSZ,stop_debugging); #ifndef NO_DEBUG DBG((0,D_INFO,"Turning off debugging.")); debug = 0; #endif } void become_daemon() { int fd; #ifndef NO_DEBUG if (!debug || force_fork) #endif { if (fd = fork()) { if (fd < 0) { perror("fork"); exit(1); } if (show_pid) (void)printf("%d\n",fd); exit(0); } progress |= FORKED_DAEMON; fd = getpid(); DBG((1,D_INIT,"Forked off child process %d",fd)); process_id = fd; #ifndef NEWAIX /* Remove association with control terminal */ fd = open("/dev/tty",O_RDWR); if (fd >= 0) { (void)ioctl(fd,(int)TIOCNOTTY,(char *)0); (void)close(fd); } #endif /* So we don't keep this file system busy, go to root. */ #ifndef NO_DEBUG if (!debug) #endif if (chdir("/") < 0) DBG((1,D_WARN,"chdir / failed -- %d",errno)); (void)close(0); (void)close(1); if (!using_log_file) (void)close(2); (void)setsid(); } (void)signal(SIGINT,control_c); (void)signal(SIGTERM,control_c); (void)signal(SIGUSR1,increase_debugging); (void)signal(SIGUSR2,stop_debugging); (void)signal(SIGXFSZ,stop_debugging); (void)signal(SIGHUP,SIG_IGN); (void)signal(SIGPIPE,SIG_IGN); (void)signal(SIGTTIN,SIG_IGN); (void)signal(SIGTTOU,SIG_IGN); (void)signal(SIGTSTP,SIG_IGN); } int set_io_block(s,flag) int s,flag; { int cur; if ((cur = fcntl(s,(int)F_GETFL,0)) < 0) return -1; if (flag) cur &= ~FNONBLOCK; else cur |= FNONBLOCK; return fcntl(s,(int)F_SETFL,cur); } int make_connection() { int s,on=1; struct linger linger; s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) { i_perror("socket"); return -1; } #ifndef NO_DEBUG if (so_debug && setsockopt(s,SOL_SOCKET,SO_DEBUG,(void*)&on,sizeof(on)) < 0) i_perror("setsockopt SO_DEBUG"); #endif if (setsockopt(s,SOL_SOCKET,SO_KEEPALIVE,(void *)&on,sizeof(on))<0) i_perror("setsockopt SO_KEEPALIVE"); if (setsockopt(s,SOL_SOCKET,SO_OOBINLINE,(void *)&on,sizeof(on))<0) i_perror("setsockopt SO_OOBINLINE"); linger.l_onoff = 1; linger.l_linger = 120; if (setsockopt(s,SOL_SOCKET,SO_LINGER,(void *)&linger, sizeof(linger)) < 0) i_perror("setsockopt SO_LINGER"); if (bind(s,(struct sockaddr *)&sin2,sizeof(struct sockaddr))<0) i_perror("bind"); sin.sin_port = htons((u_short)tcp_port); if (connect(s, (struct sockaddr *)&sin, sizeof (sin)) < 0) { i_perror("connect"); (void)close(s); return -1; } /* Make I/O non-blocking */ if (set_io_block(s,0) < 0) i_perror("set_io_block"); return s; } #ifndef NO_DEBUG static char * cvtchar(chr) char chr; { static char buff[5]; char *bp = buff; if (chr & 0x80) { *bp++ = 'M'; *bp++ = '-'; chr = toascii(chr); } if (isgraph(chr)) *bp++ = chr; else if (chr == ' ') { *bp++ = 'S'; *bp++ = 'P'; } else { *bp++ = '^'; *bp++ = (chr == 0x7F) ? '?' : (chr + '@'); } *bp = '\0'; return buff; } #endif static void set_minor_flags(newpty) int newpty; { static int flag = 0; if (noioctls) return; /* Special meaning here -- never_open means we never set flags either */ if (never_open) { DBG((3,D_INFO,"Not setting pty flags; -n specified.")); return; } if (flag == pty_is_open && !globchanged) { DBG((3,D_INFO,"Not setting pty flags; flags unchanged.")); return; } flag = pty_is_open; if (!flag) { DBG((3,D_INFO,"Not setting pty flags; pty closed.")); return; } /* * Note: TIOCSETN will work on the master pty only when the slave is * open. */ if (ioctl(newpty,(int)TIOCSETN,(char *)&glob) < 0) i_perror("pty setn"); else { #ifndef NO_DEBUG static int speedtab[] = { 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400 }; DBG((4,D_INFO,"Set new pty flags.")); if (glob.sg_ispeed >= sizeof(speedtab)/sizeof(*speedtab)) DBG((5,D_INFO,"\tInput bit rate unknown.")); else DBG((5,D_INFO,"\tInput bit rate %d.",speedtab[glob.sg_ispeed])); if (glob.sg_ospeed >= sizeof(speedtab)/sizeof(*speedtab)) DBG((5,D_INFO,"\tOutput bit rate unknown.")); else DBG((5,D_INFO,"\tOutput bit rate %d.",speedtab[glob.sg_ospeed])); DBG((5,D_INFO,"\tErase character %02X (%s).",glob.sg_erase,cvtchar(glob.sg_erase))); DBG((5,D_INFO,"\tKill character %02X (%s).",glob.sg_kill,cvtchar(glob.sg_kill))); if (glob.sg_flags == 0) DBG((5,D_INFO,"\tNo flags set.")); if (glob.sg_flags & TANDEM) DBG((5,D_INFO,"\tTANDEM - send stopc.")); if (glob.sg_flags & CBREAK) DBG((5,D_INFO,"\tCBREAK - half-cooked.")); if (glob.sg_flags & LCASE) DBG((5,D_INFO,"\tLCASE - simulate lower case.")); if (glob.sg_flags & ECHO) DBG((5,D_INFO,"\tECHO - echo input.")); if (glob.sg_flags & CRMOD) DBG((5,D_INFO,"\tCRMOD - output CR as CRLF.")); if (glob.sg_flags & RAW) DBG((5,D_INFO,"\tRAW - no processing.")); switch (glob.sg_flags & ANYP) { case ODDP: DBG((5,D_INFO,"\tODDP - odd parity.")); break; case EVENP: DBG((5,D_INFO,"\tEVENP - even parity.")); break; case ANYP: DBG((5,D_INFO,"\tANYP - any parity.")); break; default: DBG((5,D_INFO,"\t~ANYP - no parity.")); } switch (glob.sg_flags & CRDELAY) { case CR0: DBG((5,D_INFO,"\tCR0 - no CR delay.")); break; case CR1: DBG((5,D_INFO,"\tCR1 - tn 300.")); break; case CR2: DBG((5,D_INFO,"\tCR2 - tty 37.")); break; case CR3: DBG((5,D_INFO,"\tCR3 - concept 100.")); } switch (glob.sg_flags & TBDELAY) { case TAB0: DBG((5,D_INFO,"\tTAB0 - no TAB delay.")); break; case TAB1: DBG((5,D_INFO,"\tTAB1 - tty 37.")); break; case TAB2: DBG((5,D_INFO,"\tTAB2 - TAB delay.")); break; case XTABS: DBG((5,D_INFO,"\tXTABS - expand TABs.")); } if (glob.sg_flags & BS1) DBG((5,D_INFO,"\tBS1 - BS delay.")); if (glob.sg_flags & FF1) DBG((5,D_INFO,"\tFF1 - FF delay (tty 37).")); switch (glob.sg_flags & NLDELAY) { case NL0: DBG((5,D_INFO,"\tNL0 - no NL delay.")); break; case NL1: DBG((5,D_INFO,"\tNL1 - tty 37.")); break; case NL2: DBG((5,D_INFO,"\tNL2 - vt05.")); break; case NL3: DBG((5,D_INFO,"\tNL3 - NL delay.")); } if (glob.sg_flags & TOSTOP) DBG((5,D_INFO,"\tTOSTOP - SIGSTOP on output.")); if (glob.sg_flags & PRTERA) DBG((5,D_INFO,"\tPRTERA - printing term erase.")); if (glob.sg_flags & CRTERA) DBG((5,D_INFO,"\tCRTERA - CRT style erase.")); if (glob.sg_flags & TILDE) DBG((5,D_INFO,"\tTILDE - Hazeltine memorial.")); if (glob.sg_flags & FLUSHO) DBG((5,D_INFO,"\tFLUSHO - flush output.")); if (glob.sg_flags & LITOUT) DBG((5,D_INFO,"\tLITOUT - literal output.")); if (glob.sg_flags & CRTBS) DBG((5,D_INFO,"\tCRTBS - CRT backspace.")); if (glob.sg_flags & MDMBUF) DBG((5,D_INFO,"\tMDMBUF - DTR pacing.")); if (glob.sg_flags & NOHANG) DBG((5,D_INFO,"\tNOHANG - no SIGHUP.")); if (glob.sg_flags & CRTKIL) DBG((5,D_INFO,"\tCRTKIL - erase with BS.")); if (glob.sg_flags & PASS8) DBG((5,D_INFO,"\tPASS8 - 8 bit data path.")); if (glob.sg_flags & CTLECH) DBG((5,D_INFO,"\tCTLECH - show controls.")); if (glob.sg_flags & PENDIN) DBG((5,D_INFO,"\tPENDIN - pending input.")); if (glob.sg_flags & DECCTQ) DBG((5,D_INFO,"\tDECCTQ - use XON to start.")); if (glob.sg_flags & NOFLUSH) DBG((5,D_INFO,"\tNOFLUSH - no output flush on signal.")); #endif globchanged = 0; } } /* * timet is in milliseconds. */ int wait_for_io(from,dev_pty,dev_net,timet) int from,dev_pty,dev_net,timet; { int n_found,towait; fd_set ibits,obits,ebits; struct timeval timev,*timevp; #ifndef NO_DEBUG char temp[64]; /* 58 bytes used */ #endif DBG((4,D_INFO,"wait_for_io: from %02X pty %d net %d timet %d.", from,dev_pty,dev_net,timet)); towait = dev_pty+1; if (dev_net >= towait) towait = dev_net+1; try_waiting_again: for (;;) { FD_ZERO(&ibits); FD_ZERO(&obits); FD_ZERO(&ebits); if (dev_pty >= 0) { #ifdef NEWAIX FD_SET(dev_pty, &ebits); #else if (!pty_is_open) { from &= ~ALL_PTY; if (timet <= 0) timet = 2000; } #endif if (from & FROM_PTY) FD_SET(dev_pty, &ibits); if (from & TO_PTY) FD_SET(dev_pty, &obits); } if (dev_net >= 0) { if (from & FROM_NET) FD_SET(dev_net, &ibits); if (from & TO_NET) FD_SET(dev_net, &obits); } if (timet > 0) { timevp = &timev; timev.tv_usec = (timet%1000)*1000; timev.tv_sec = timet/1000; } else timevp = (struct timeval *)NULL; #define FDB(x) ((x)->fds_bits[0]) #define IOB FDB(&ibits),FDB(&obits),FDB(&ebits) DBG((3,D_INFO,"selecting: i %02X o %02X e %02X.",IOB)); errno = 0; n_found = select(towait,&ibits,&obits,&ebits,timevp); if (n_found < 0) { if (errno == EINTR) { DBG((3,D_INFO,"interrupted -- trying again.")); continue; } i_perror("select"); cleanup(); } else break; } DBG((3,D_INFO,"%d found: i %02X o %02X e %02X.",n_found,IOB)); if (n_found == 0) from = WFIO_TIMEOUT; else from = 0; #ifndef NO_DEBUG (void)strcpy(temp,"\t"); #define ADDS(s) (void)strcat(temp,s) #else #define ADDS(s) #endif if (dev_pty >= 0) { ADDS("(pty"); if (FD_ISSET(dev_pty, &ibits)) { from |= FROM_PTY; ADDS(" input"); } if (FD_ISSET(dev_pty, &obits)) { from |= TO_PTY; ADDS(" output"); } #ifdef NEWAIX if (FD_ISSET(dev_pty, &ebits)) { /* * If we have any exceptional condition then it * means that the slave side of the pty might have * been opened. So giving it benefit of doubt, say * that we have something from pty and let pty_read() * read some data and figure out what the real * condition is. */ from |= FROM_PTY; ADDS(" exception"); } #else if (!pty_is_open) if (onthefly) { char dummy[1]; if (read(dev_pty,dummy,1) < 0) { if (errno != EIO) i_perror("pty test"); goto try_waiting_again; } from |= FROM_PTY; ADDS(" open"); DBG((3,D_INFO,"Dummy byte is %02X",dummy[0])); pty_is_open = 1; set_minor_flags(dev_pty); } else { from |= FROM_PTY | TO_PTY; ADDS(" polling"); } #endif ADDS((from&ALL_PTY) ? ")" : " none)"); } if (dev_net >= 0) { ADDS("(net"); if (FD_ISSET(dev_net, &ibits)) { from |= FROM_NET; ADDS(" input"); } if (FD_ISSET(dev_net, &obits)) { from |= TO_NET; ADDS(" output"); } ADDS((from&ALL_NET) ? ")" : " none)"); } DBG((3,D_INFO,temp)); return from; } static void set_pty_flags(newpty) int newpty; { int on; on = 1; if (ioctl(newpty,(int)TIOCPKT,(char *)&on) < 0) { i_perror("pty pkt"); return; } if (set_io_block(newpty,0)) i_perror("pty nbio"); } static void unlink_pty() { pty_close(-1); if (progress & LINKED_TTY) { (void)unlink(new_node); progress &= ~LINKED_TTY; DBG((2,D_INFO,"Removed link between %s and %s.",new_node,slave)); } } /* * AIX style ptys -- open the multiplexed master pty to request the next * available master/slave pair and then figure out names for ptys. */ int openmaster(name) char *name; { int newpty,i; char *sname; unlink_pty(); DBG((2,D_INFO,"Attempting to find pty for \"%s\".",name)); newpty = open(MPXMASTER,O_RDWR|O_NOCTTY); if (newpty < 0) { i_perror(MPXMASTER); return -1; } DBG((3,D_INFO,"Got master file descriptor %d.",newpty)); if ((sname = ttyname(newpty)) == NULL) DBG((1,D_WARN,"%s: %s not a tty?",myname,MPXMASTER)); else { strcpy(slave,sname); DBG((1,D_INFO,"Using slave %s.",slave)); set_pty_flags(newpty); i = symlink(slave,name); if (i < 0) { i_perror(name); DBG((0,D_ERR,"Unable to link slave pty.")); } else { if (hold_open) { slave_pty=open(sname,O_RDONLY|O_NOCTTY); DBG((3,D_INFO,"Got slave file descriptor %d.",slave_pty)); } new_node = name; progress |= LINKED_TTY; glob.sg_ispeed = glob.sg_ospeed = B9600; glob.sg_kill = '\025'; glob.sg_erase = '\b'; if (transparent) { glob.sg_flags = RAW | LITOUT; glob.sg_kill = glob.sg_erase = '\0'; } else if (cbreakmode) glob.sg_flags = CBREAK | CRMOD | XTABS | ANYP; else glob.sg_flags = CRMOD | XTABS | ANYP; if (binary) glob.sg_flags &= ~CRMOD & ~ XTABS; globchanged = 1; return newpty; } } (void)close(newpty); return -1; } void machdep_cleanup() { unlink_pty(); } /* * AIX style ptys don't need the BSD master-alias garbage or the * System V replacement-master garbage. Yay! */ int reopen_pty(dev_pty) int dev_pty; { pty_close(dev_pty); return -1; } /* * This routine is called when the first real data packet is read from * the pty. This is not useful here. */ void first_pty_data() { } /* * If we're not in RAW or CBREAK mode, then we can experience trouble * with the pty if we give it more than 191 bytes between line feeds. * (I have no idea why this is!) In any event, we have to compensate * for this bit of weirdness. */ /*ARGSUSED*/ int fix_cooked_mode_bug(columns,pty) int columns,pty; { #if 0 struct termio b; if (columns == 191) { if (!transparent && !cbreakmode) { if (slave_pty < 0) return 1; if (ioctl(slave_pty,(int)TCGETA,(char *)&b) < 0) return 1; if (b.c_lflag & ICANON) return 1; } } #endif return 0; } /*ARGSUSED*/ int get_interrupt_char(s) int s; { struct tchars b; if (ioctl(s,(int)TIOCGETC,(char *)&b) < 0) return '\003'; return b.t_intrc; } /* * If flag is zero, return character-erase, if non-zero return line- * erase. */ /*ARGSUSED*/ int get_erase_char(s,flag) int s,flag; { if (flag) return glob.sg_kill; return glob.sg_erase; } /* * flag is 0 to clear, 1 to set, and option is 0 for raw mode, 1 for * echo/crmod. */ /*ARGSUSED*/ int mode(s,flag,option) int s,flag,option; { option = (option == MODEF_RAW) ? RAW : ECHO; if (flag) { if ((glob.sg_flags&option) != option) { glob.sg_flags |= option; globchanged = 1; } } else { if ((glob.sg_flags&option) != 0) { glob.sg_flags &= ~option; globchanged = 1; } } set_minor_flags(s); return 0; } void reset_serial_line() { sin.sin_port = htons((u_short)erpc_port); (void)reset_line((struct sockaddr_in *)&sin,(u_short)SERIAL_DEV, (u_short)port_num); } int force_send(fd,buff,len,flag) int fd,flag,len; char *buff; { errno = 0; net_was_selected = 0; return send(fd,buff,len,flag ? MSG_OOB : 0); } int network_read(fd,buffp,siz) int fd,siz; char **buffp; { int cc; errno = 0; cc = read(fd,*buffp,siz); if (cc > 0) return cc; if (cc == 0) return MDIO_CLOSED; if (errno == EWOULDBLOCK || errno == EINTR) return MDIO_DEFER; return MDIO_ERROR; } int network_write(fd,buff,siz) int fd,siz; char *buff; { int cc,nws = net_was_selected; net_was_selected = 0; errno = 0; cc = write(fd,buff,siz); if (cc >= 0) return cc; if (errno == EWOULDBLOCK || errno == EINTR) if (nws) return MDIO_UNSELECT; else return MDIO_DEFER; return MDIO_ERROR; } void network_close(fd) int fd; { if (fd >= 0) { (void)shutdown(fd,2); if (close(fd) < 0) i_perror("net close"); } } int pty_read(fd,buffp,siz) int fd,siz; char **buffp; { int done,cc; char *cp = *buffp; static int flag = 0; if (flag == 0) { errno = 0; cc = read(fd,cp,siz); if (cc == 0) { errno = EIO; return MDIO_ERROR; } if (cc < 0) if (errno == EIO) if (pty_is_open) { pty_is_open = 0; set_minor_flags(fd); DBG((3,D_ERR,"I/O Error on open pty.")); return MDIO_ERROR; } else return MDIO_DEFER; else if (errno == EWOULDBLOCK || errno == EINTR) return MDIO_DEFER; else return MDIO_ERROR; pty_is_open = 1; set_minor_flags(fd); if (*cp == 0 && cc > 1) { *buffp = cp+1; return cc - 1; } flag = *cp & (TIOCPKT_FLUSHREAD | TIOCPKT_FLUSHWRITE | TIOCPKT_STOP | TIOCPKT_START | TIOCPKT_DOSTOP | TIOCPKT_NOSTOP); } DBG((flag!=0?2:3,D_INFO,"Processing pty packet flags %02X",flag)); done = 0; /* * The order of flush write/read is important, due to the way the * telnet protocol handles it. */ if (flag & (TIOCPKT_STOP | TIOCPKT_START)) { done |= flag & (TIOCPKT_STOP | TIOCPKT_START); telnet_halt_network((flag&TIOCPKT_STOP)?1:0); } if (flag & TIOCPKT_FLUSHWRITE) { if (cancel_network_output()) done |= TIOCPKT_FLUSHWRITE; telnet_halt_network(0); } if (flag & TIOCPKT_FLUSHREAD) { if (cancel_network_input()) done |= TIOCPKT_FLUSHREAD; } if (flag & TIOCPKT_DOSTOP) { if (telnet_ship_lflow(1)) done |= TIOCPKT_DOSTOP; } else if (flag & TIOCPKT_NOSTOP) { if (telnet_ship_lflow(0)) done |= TIOCPKT_NOSTOP; } if (done != 0) DBG((3,D_INFO,"Processed pty packet flags %02X.",done)); flag &= ~done; return MDIO_DEFER; } int pty_write(fd,buff,siz) int fd,siz; char *buff; { int cc,pws = pty_was_selected; pty_was_selected = 0; errno = 0; cc = write(fd,buff,siz); if (cc >= 0) { pty_is_open = 1; set_minor_flags(fd); return cc; } if (errno == EWOULDBLOCK || errno == EINTR) if (pws) return MDIO_UNSELECT; else return MDIO_DEFER; if (errno == EIO) { pty_is_open = 0; set_minor_flags(fd); DBG((1,D_WARN,"Slave not open - discarding %d bytes.",siz)); return siz; } return MDIO_ERROR; } void pty_close(fd) int fd; { if (slave_pty >= 0) { (void)close(slave_pty); slave_pty = -1; } if (fd >= 0) (void)close(fd); set_minor_flags(-1); pty_is_open = 0; }