#include "ckcsym.h" /* emulate most compilers -DSYMBOL feature */ char *ckxv = "Stratus VOS Communications support 6.1.007, 15 January 1998"; /* C K L T I O */ /* C-Kermit interrupt, terminal control & i/o functions for STRATUS VOS */ /* Author: Frank da Cruz (fdc@columbia.edu), The Kermit Project, Columbia University Adapted to Stratus VOS by David R. Lane, SoftCom Systems, Inc., Nov 93. Copyright (C) 1985, 1999, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. */ /* Includes */ #include "ckcdeb.h" /* This moved to here. */ #include /* System error numbers */ #include "ckcnet.h" /* Symbols for network types. */ #include /* Signals */ #include #include #include #include #include #include #include #include #include #include /* In releases prior to 13, get_port_info is a struct tag. In 13, it becomes a typedef. No, it's not in the SRB, it's just an incompatible change in a system include file. (YECH!) The cklmak.cm macro tries to set this define on releases before 13. YMMV, unfortunately. */ #ifdef GET_PORT_INFO_IS_STRUCT typedef struct get_port_info get_port_info; #endif typedef struct { unsigned filler : 8; unsigned baud : 8; unsigned stop : 8; unsigned parity : 8; } term_configure; typedef struct { short version; unsigned short flags; } DATA_SET_STATUS_INFO; #define DSSI_CLEAR_TO_SEND 0x8000 #define DSSI_CARRIER_DETECT 0x4000 #define DSSI_DATA_SET_READY 0x2000 /* We know these are set here. MUST unset them before the definitions. */ #define signal vsignal #define alarm valarm SIGTYP (*vsignal(int type, SIGTYP (*func)(int)))(int); int valarm(int interval); /* Maximum length for the name of a tty device */ #ifndef DEVNAMLEN #define DEVNAMLEN 66 #endif #ifdef NETCONN #undef DEVNAMLEN #define DEVNAMLEN 132 /* longer field for host:service */ #endif /* NETCONN */ #include "ckuver.h" /* Version herald */ char *ckxsys = HERALD; /* Variables available to outside world: dftty -- Pointer to default tty name string, like "/dev/tty". dfloc -- 0 if dftty is console, 1 if external line. dfprty -- Default parity dfflow -- Default flow control ckxech -- Flag for who echoes console typein: 1 - The program (system echo is turned off) 0 - The system (or front end, or terminal). functions that want to do their own echoing should check this flag before doing so. backgrd -- Flag indicating program executing in background, such as as a batch server. Used to ignore INT and QUIT signals. myttystr -- name of terminal tty Functions for assigned communication line (either external or console tty): sysinit() -- System dependent program initialization syscleanup() -- System dependent program shutdown ttopen(ttname,local,mdmtyp,timo) -- Open the named tty for exclusive access. ttclos() -- Close & reset the tty, releasing any access lock. ttsspd(cps) -- Set the transmission speed of the tty. ttgspd() -- Get (read) the the transmission speed of the tty. ttpkt(speed,flow,parity) -- Put the tty in packet mode and set the speed. ttvt(speed,flow) -- Put the tty in virtual terminal mode. or in DIALING or CONNECTED modem control state. ttres() -- Restore original tty modes. ttscarr(carrier) -- Set carrier control mode, on/off/auto. ttinl(dest,max,timo) -- Timed read line from the tty. ttinc(timo) -- Timed read character from tty. ttchk() -- See how many characters in tty input buffer. ttxin(n,buf) -- Read n characters from tty (untimed). ttol(string,length) -- Write a string to the tty. ttoc(c) -- Write a character to the tty. ttflui() -- Flush tty input buffer. ttsndb() -- Send BREAK signal. ttsndlb() -- Send Long BREAK signal. */ /* Functions for console terminal: congm() -- Get console terminal modes. concb(esc) -- Put the console in single-character wakeup mode with no echo. conbin(esc) -- Put the console in binary (raw) mode. conres() -- Restore the console to mode obtained by congm(). conoc(c) -- Unbuffered output, one character to console. conol(s) -- Unbuffered output, null-terminated string to the console. conola(s) -- Unbuffered output, array of strings to the console. conxo(n,s) -- Unbuffered output, n characters to the console. conchk() -- Check if characters available at console. coninc(timo) -- Timed get a character from the console. congks(timo) -- Timed get keyboard scan code. conint() -- Enable terminal interrupts on the console if not background. connoi() -- Disable terminal interrupts on the console if not background. Time functions msleep(m) -- Millisecond sleep ztime(&s) -- Return pointer to date/time string rtimer() -- Reset timer gtimer() -- Get elapsed time since last call to rtimer() */ /* Declarations */ /* dftty is the device name of the default device for file transfer */ /* dfloc is 0 if dftty is the user's console terminal, 1 if an external line */ #ifndef DFTTY char *dftty = "(terminal_name)"; char *dfmdm = "none"; int dfloc = 0; #else char *dftty = DFTTY; /* Default location specified on */ char *dfmdm = "none"; /* command line. */ int dfloc = 0; /* controlling terminal name. */ #endif /* DFTTY */ int dfprty = 0; /* Default parity (0 = none) */ int ttprty = 0; /* The parity that is in use. */ int ttpflg = 0; /* Parity not sensed yet. */ static int ttpmsk = 0377; /* Parity stripping mask. */ int ttmdm = 0; /* Modem in use. */ int ttcarr = CAR_AUT; /* Carrier handling mode. */ int dfflow = FLO_XONX; /* Default is Xon/Xoff */ int backgrd = 0; /* Assume in foreground (no '&' ) */ int fdflag = 0; /* Flag for redirected stdio */ int ttfdflg = 0; /* Open File descriptor was given */ int tvtflg = 0; /* Flag that ttvt has been called */ long ttspeed = -1; /* For saving speed */ long comspd = -1; /* speed of console device */ int ttflow = -9; /* For saving flow */ int ttld = -1; /* Line discipline */ extern int ttnproto; /* Defined in ckcnet.c */ extern int ttnet; /* Defined in ckcnet.c */ extern int xfrcan, xfrchr, xfrnum; /* Defined in ckcmai.c */ int ckxech = 0; /* 0 if system normally echoes console characters, else 1 */ /* Declarations of variables global within this module */ static time_t tcount; /* Elapsed time counter */ static SIGTYP (*saval)() = NULL; /* For saving alarm() handler */ static long vosbaud[] = {0, 50, 75, 110, 134, 150, 300, 600, 1200, 1800, 2400, 3600, 4800, 7200, 9600, 19200, 38400, 76800, 56000, 64000}; #define NUMBAUDS ((sizeof vosbaud) / (sizeof vosbaud[0])) /* BREAKNULS is defined for systems that simulate sending a BREAK signal by sending a bunch of NUL characters at low speed. This may be all we can do, if we don't have support for TERM_SEND_BREAK. */ static char /* A string of nulls */ *brnuls = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; static jmp_buf sjbuf; /* Longjump buffers */ static jmp_buf jjbuf; /* Longjump buffers */ /* these are for CONNECT processing */ static long eventids[2]; /* 0 is con, 1 is tty, same as src argument */ static long eventcnts[2]; static int no_wait = 0; static char cinbuff[256]; static int cincnt; static int cinbp; char myttystr[DEVNAMLEN+1]; /* name of console device */ /* static */ /* (Not static any more) */ int ttyfd = -1; /* TTY file descriptor */ int telnetfd = 0; /* File descriptor is for telnet */ int x25fd = 0; /* File descriptor is for X.25 */ static int lkf = 0, /* Line lock flag */ cgmf = 0, /* Flag that console modes saved */ xlocal = 0, /* Flag for tty local or remote */ curcarr = 0; /* Carrier mode: require/ignore. */ static int netconn = 0; /* 1 if network connection active */ static char escchr; /* Escape or attn character */ /* this is for our incredible ALARM hack. it sucks wind, but it works. */ /* if Stratus would add alarm() to the runtime, we wouldn't have to do this. */ /* at least SIGALRM is defined, and kill() works... */ static time_t deadline = 0; #define chkalrm() ((deadline) ? chkalarm () : 0) _PROTOTYP(chkalarm, (void)); /* these are various terminal info modes, when filled in */ static long ttold, ttraw, tttvt, ttcur, ccold, ccraw, cccbrk; static int plines = 0; static int conraw = 0; /* is console in a raw mode? 0=no, 1=cb, 2=bin */ static int ttyraw = 0; /* ditto for the tty */ static int conesc = 0; /* set to 1 if esc char (^\) typed */ static char ttnmsv[DEVNAMLEN]; /* Copy of open path for tthang */ static char ttnmful[DEVNAMLEN]; /* full path name of device */ /* ANSI-style prototypes for internal functions. */ /* Functions used outside this module are prototyped in ckcker.h. */ _PROTOTYP( VOID cancio, (void) ); _PROTOTYP( SIGTYP timerh, (int) ); _PROTOTYP( SIGTYP cctrap, (int) ); _PROTOTYP( SIGTYP esctrp, (int) ); _PROTOTYP( int do_open, (char *) ); _PROTOTYP( VOID conbgt, (int) ); #ifdef ACUCNTRL _PROTOTYP( VOID acucntrl, (char *, char *) ); #endif /* ACUCNTRL */ _PROTOTYP( int netwait, (int, int, long *, long *) ); _PROTOTYP( int carrctl, (long *, int) ); _PROTOTYP( static int conout, (char *, int) ); /* same as isatty, but uses port ids instead of file descriptors. * file descriptors are an artifact of the c-runtime on VOS, so we use * ports to get some reasonable performance. open/close/read/write use * file descriptors, and the s$ calls use ports. The control operations * involved in changing from binary to normal modes are not in the c * runtime (ioctl and its kin), so we need to use ports for that, and the * c runtime loses its mind if you change the modes behind its back. */ _PROTOTYP( int vostty, (int) ); _PROTOTYP( static char *connam, (void) ); /* roll our own ttyname() */ _PROTOTYP( long congspd, (void) ); /* returns speed of console device */ /* prototypes for VOS calls */ _PROTOTYP( extern VOID s$attach_port, (CV(32) *port_name, CV(256)* path, short *hold, short *port_id, short *status) ); _PROTOTYP( extern VOID s$close, (short *port_id, short *status) ); _PROTOTYP( extern VOID s$control, (short *port_id, short *opcode, VOID *control, short *status) ); _PROTOTYP( extern VOID s$detach_port, (short *port_id, short *status) ); _PROTOTYP( extern VOID s$get_data_set_status, (CV(66) *channel, VOID *info, short *status) ); _PROTOTYP( extern VOID s$get_lockers, (CV(256) *path, short *maxlock, short *nbrlock, long *pids, short *locktyp, short *status) ); _PROTOTYP (extern VOID s$get_port_info, (short *port, get_port_info *pinfo, short *status) ); _PROTOTYP( extern VOID s$get_process_id, (long *process_id) ); _PROTOTYP( extern VOID s$get_process_info, (long *process_id, VOID *info, short *status) ); _PROTOTYP( extern VOID s$expand_module_name, (CV(66) *src, CV(66) *dest, short *status) ); _PROTOTYP( extern VOID s$get_home_dir, (CV(256) *path) ); _PROTOTYP( extern int s$is_file_type_a_terminal, (short *type) ); _PROTOTYP( extern void s$jiffy_date_time, (void *jiffy) ); _PROTOTYP( extern VOID s$open, (short *port_id, short *organization, short *max_rec_len, short *io_type, short *locking_type, short *access_mode, CV(32) *index_name, short *status) ); _PROTOTYP( extern VOID s$read_event, (long *event_id, long *event_count, short *event_status, short *status) ); _PROTOTYP( extern VOID s$read_raw, (short *port_id, short *buff_size, short *rec_len, VOID *buffer, short *status) ); _PROTOTYP( extern VOID s$seq_open, (CV(256) *port_id, short *io_type, short *port, short *status) ); _PROTOTYP( extern VOID s$seq_read, (short *port_id, short *buff_size, short *rec_len, VOID *buffer, short *status) ); _PROTOTYP( extern VOID s$seq_write_partial, (short *port_id, short *rec_len, VOID *buffer, short *status) ); _PROTOTYP( extern VOID s$set_io_time_limit, (short *port, long *timeout, short *status) ); _PROTOTYP( extern VOID s$set_no_wait_mode, (short *port_id, long *event_id, short *status) ); _PROTOTYP( extern VOID s$set_wait_mode, (short *port_id, short *status) ); _PROTOTYP( extern VOID s$sleep, (int *ticks, short *status) ); _PROTOTYP( extern VOID s$wait_event, (short *count, long *event_id_array, long *event_count_array, long *timeout, short *which_event, short *status) ); _PROTOTYP( extern VOID s$write_raw, (short *port_id, short *length, void *buffer, short *status) ); #ifdef CK_ANSIC static char * xxlast(char *s, char c) #else static char * xxlast(s,c) char *s; char c; #endif /* CK_ANSIC */ /* xxlast */ { /* Last occurrence of character c in string s. */ return(strrchr (s, c)); } /* Timeout handler for communication line input functions */ SIGTYP timerh(foo) int foo; { ttimoff(); longjmp(sjbuf,1); } /* Control-C trap for communication line input functions */ int cc_int; /* Flag */ SIGTYP (* occt)(); /* For saving old SIGINT handler */ SIGTYP cctrap(foo) int foo; { /* Needs arg for ANSI C */ cc_int = 1; /* signal() prototype. */ debug(F101,"cctrap arg","",foo); SIGRETURN; } /* S Y S I N I T -- System-dependent program initialization. */ int sysinit() { int x; char * s; extern char uidbuf[]; short conport = TERMINAL_PORT_ID; short opcode = SET_PAUSE_LINES_OPCODE; short status; short lines = 0; strcpy (myttystr, connam()); dftty = myttystr; strcpy (ttnmful, myttystr); conbgt(0); /* See if we're in the background */ congm(); /* save console modes */ /* Initialize the setuid package. */ /* Change to the user's real user and group id. */ /* If this can't be done, don't run at all. */ if (x = priv_ini()) { if (x | 1) fprintf(stderr,"Fatal: setuid failure.\n"); if (x | 2) fprintf(stderr,"Fatal: setgid failure.\n"); if (x | 4) fprintf(stderr,"Fatal: C-Kermit setuid to root!\n"); exit(1); } if (!backgrd && vostty(conport)) { /* only if at terminal */ s$control (&conport, &opcode, &lines, &status); } s = whoami(); if (!s) s = ""; strncpy(uidbuf,(*s) ? s : "UNKNOWN", UIDBUFLEN); return(0); } /* S Y S C L E A N U P -- System-dependent program cleanup. */ int syscleanup() { short conport = TERMINAL_PORT_ID; short opcode = SET_PAUSE_LINES_OPCODE; short status; short lines = plines; /* No need to call anything in the suid package here, right? */ if (!backgrd && vostty(conport)) { /* only if at terminal */ s$control (&conport, &opcode, &lines, &status); } return(0); } /* T T O P E N -- Open a tty for exclusive access. */ /* Call with: ttname: character string - device name or network host name. lcl: If called with lcl < 0, sets value of lcl as follows: 0: the terminal named by ttname is the job's controlling terminal. 1: the terminal named by ttname is not the job's controlling terminal. But watch out: if a line is already open, or if requested line can't be opened, then lcl remains (and is returned as) -1. modem: Less than zero: ttname is a network host name. Zero or greater: ttname is a terminal device name. Zero means a local connection (don't use modem signals). Positive means use modem signals. timo: 0 = no timer. nonzero = number of seconds to wait for open() to return before timing out. Returns: 0 on success -5 if device is in use -4 if access to device is denied -3 if access to lock directory denied -2 upon timeout waiting for device to open -1 on other error */ int ttopen(ttname,lcl,modem,timo) char *ttname; int *lcl, modem, timo; { char *x; int temp; char cname[DEVNAMLEN+4]; debug(F111,"ttopen called ttname & timo",ttname,timo); if (ttyfd > -1) { /* if device already opened */ debug(F111,"ttopen ttyfd set, ttnmsv",ttnmsv,ttyfd); if (strncmp(ttname,ttnmsv,DEVNAMLEN)) { /* are new & old names same? */ ttclos(ttyfd); /* no, close old ttname, open new */ } else /* else same, ignore this call, */ return(0); /* and return. */ } chkalrm(); /* we save the name we were called with, full name goes elsewhere */ strcpy(ttnmsv,ttname); #ifdef NETCONN if (modem < 0) { /* modem < 0 = special code for net */ int x; ttmdm = modem; modem = -modem; /* Positive network type number */ ttnet = modem; /* save this */ fdflag = 0; /* Stdio not redirected. */ netconn = 1; /* And it's a network connection */ debug(F111,"ttopen net",ttname,modem); #ifdef NAMEFD for (p = ttname; isdigit(*p); p++) ; /* Check for all digits */ if (*p == '\0' && (telnetfd || x25fd)) { /* Avoid X.121 addresses */ ttyfd = atoi(ttname); /* Is there a way to test it's open? */ ttfdflg = 1; /* We got an open file descriptor */ debug(F111,"ttopen got open network fd",ttname,ttyfd); x = 1; /* Return code is "good". */ if (telnetfd) { ttnet = NET_TCPB; ttnproto = NP_TELNET; #ifdef STRATUSX25 } else if (x25fd) { ttnet = NET_VX25; ttnproto = NP_NONE; #endif /* STRATUSX25 */ } } else { /* Host name or address given */ #endif /* NAMEFD */ x = netopen(ttname, lcl, modem); /* (see ckcnet.h) */ if (x > -1) { strncpy(ttnmsv,ttname,DEVNAMLEN); } else netconn = 0; #ifdef NAMEFD } #endif /* NAMEFD */ chkalrm(); xlocal = *lcl = 1; /* Network connections are local. */ debug(F101,"ttopen net x","",x); #ifdef TCPSOCKET if (x > -1 && ttnet == NET_TCPB) x = tn_ini(); /* Initialize TELNET protocol */ #endif /* TCPSOCKET */ return(x); } else { /* Terminal device */ #endif /* NETCONN */ #ifdef NAMEFD /* This code lets you give Kermit an open file descriptor for a serial communication device, rather than a device name. Kermit assumes that the line is already open, locked, conditioned with the right parameters, etc. */ for (p = ttname; isdigit(*p); p++) ; /* Check for all digits */ if (*p == '\0') { ttyfd = atoi(ttname); /* Is there a way to test it's open? */ debug(F111,"ttopen got open fd",ttname,ttyfd); strncpy(ttnmsv,ttname,DEVNAMLEN); /* Remember the "name". */ xlocal = *lcl = 1; /* Assume it's local. */ netconn = 0; /* Assume it's not a network. */ tvtflg = 0; /* Might need to initialize modes. */ ttmdm = modem; /* Remember modem type. */ fdflag = 0; /* Stdio not redirected. */ ttfdflg = 1; /* Flag we were opened this way. */ return(0); /* Return success */ } #endif /* NAMEFD */ #ifdef NETCONN } #endif /* NETCONN */ /* Here we have to open a serial device of the given name. */ occt = signal(SIGINT, cctrap); /* Set Control-C trap, save old one */ tvtflg = 0; /* Flag for use by ttvt(). */ /* 0 = ttvt not called yet for this device */ ttmdm = modem; /* Make this available to other fns */ xlocal = *lcl; /* Make this available to other fns */ /* do_open need only handle TERMINAL/PRINTER/MODEM ports */ if (0 > (temp = do_open(ttname))) { debug(F111," do_open returned error",ttname,temp); return(temp); } chkalrm(); /* Make sure it's a real tty. */ if (!vostty(ttyfd)) { short ttyport = ttyfd; short status; fprintf(stderr,"%s is not a tty!\n",ttname); debug(F110,"ttopen not a tty",ttname,0); s$close (&ttyport, &status); s$detach_port (&ttyport, &status); ttyfd = -1; signal(SIGINT,occt); return(-1); } if (*lcl != 0) *lcl = xlocal; strcpy(cname,connam()); if (*lcl == -1) { if (strcmp(cname, ttname) == 0) /* this is the controlling terminal */ *lcl = 0; /* that means remote mode */ else if (strcmp("(terminal_name)", ttname) == 0) *lcl = 0; else *lcl = 1; } /* Get current speed */ ttspeed = ttgspd(); debug(F101,"ttopen ttspeed","",ttspeed); msleep (250); /* give it a little bit */ #ifdef COMMENT /* the modes have not been set yet, so don't do this */ ttflui (); /* get rid of noise in tty buffer */ #endif /* Done, make entries in debug log, restore Ctrl-C trap, and return. */ debug(F101,"ttopen, ttyfd","",ttyfd); debug(F101," lcl","",*lcl); if (*lcl != -1) xlocal = *lcl; signal(SIGINT,occt); chkalrm(); return(0); } /* D O _ O P E N -- Actually do an open for the tty. */ int do_open(ttname) char *ttname; { CV(66) ttncv; CV(66) expnam; CV(32) portnam; CV(32) index; char buf[300]; short ttyport; short status; short lock; short hold; short io_type; short access; short reclen; short org; short opcode; short maxlock; short nbrlock; long pids[1]; /* one is enough */ TERMINAL_INFO tinfo; ttyfd = -1; if (0 == strcmp (ttname, "(terminal_name)")) ttname = myttystr; if (strcmp (myttystr, ttname)) { /* not console */ strcpy (&ttncv, ttname); s$expand_module_name (&ttncv, &expnam, &status); debug(F111,"do_open s$expand_module_name status",ttname,status); if (status) return -1; strcpy (ttnmful, &expnam); /* save this for later */ maxlock = 0; nbrlock = 0; pids[0] = 0; lock = ANY_LOCK; s$get_lockers ((CV(256) *) &expnam, &maxlock, &nbrlock, pids, &lock, &status); if (status) { debug(F111,"do_open s$get_lockers status",ttnmful,status); return -1; /* an error occurred */ } if (nbrlock) { debug(F111,"do_open s$lockers in use",ttnmful,nbrlock); return -5; /* device is in use */ } strcpy (&portnam, "kermtty"); hold = DONT_HOLD; s$attach_port (&portnam, (CV(256) *) &expnam, &hold, &ttyport, &status); if (status) { debug(F111,"do_open s$attach_port status",ttnmful,status); return -1; } io_type = UPDATE_TYPE; lock = IMPLICIT_LOCKING; reclen = 2048; access = SEQUENTIAL_MODE; org = SEQUENTIAL_FILE; strcpy (&index, ""); s$open (&ttyport, &org, &reclen, &io_type, &lock, &access, &index, &status); if (status) { debug(F111,"do_open s$attach_port status",ttnmful,status); s$detach_port(&ttyport, &status); /* make sure it's detached */ return -1; } ttyfd = ttyport; } else { ttyfd = TERMINAL_PORT_ID; } if (ttyfd == -1) return(-1); debug(F100,"do_open getting modes","",0); /* Need to do it. */ ttyport = ttyfd; /* make sure it is set here. */ opcode = GET_INFO_OPCODE; tinfo.version = TERMINAL_INFO_VERSION_2; s$control (&ttyport, &opcode, &tinfo, &status); if (status) { debug(F111,"do_open s$control get_info status",ttnmful,status); s$close (&ttyport, &status); s$detach_port (&ttyport, &status); ttyfd = -1; return -1; } ttold = tinfo.modes; ttcur = tinfo.modes; ttraw = tinfo.modes; ttraw &= ~(OS_FUNCTION_KEY_INPUT | OS_BREAK_TABLE_RECORD | OS_GENERIC_INPUT | OS_FORMS_INPUT); ttraw |= OS_RAW_INPUT | OS_BULK_RAW_INPUT; tttvt = ttraw; debug(F111,"do_open return success, fd",ttnmful,ttyfd); return(0); } /* T T C L O S -- Close the TTY, releasing any lock. */ int ttclos(foo) int foo; { short status; short ttyport; debug(F110,"ttclos called",ttnmsv,0); strcpy (ttnmsv, ""); /* nothing opened right now */ chkalrm(); #ifdef NETCONN if (netconn) return netclos(); #endif ttyport = ttyfd; ttyfd = -1; /* wipe it out here */ /* don't close the console. That would be bad. */ if (ttyport >= 0 && ttyport != TERMINAL_PORT_ID) { s$close (&ttyport, &status); if (status) { debug(F101,"ttclos s$close status","",status); return (-1); } s$detach_port (&ttyport, &status); if (status) { debug(F101,"ttclos s$detach_port status","",status); return (-1); } } chkalrm(); return(0); } /* T T H A N G -- Hangup phone line or network connection. */ /* Returns: 0 if it does nothing. 1 if it believes that it hung up successfully. -1 if it believes that the hangup attempt failed. */ #define HUPTIME 500 /* Milliseconds for hangup */ int tthang() { short ttyport; short status; short opcode; short dummy; char oldtty[DEVNAMLEN+1]; int rv; /* return value */ long oldspeed; int oldflow; if (ttyfd < 0) return 0; /* no action taken */ chkalrm(); #ifdef NETCONN if (netconn) return((netclos() < 0) ? -1 : 1); /* Just close it. */ #endif ttyport = ttyfd; if (ttyport == TERMINAL_PORT_ID) return 0; /* don't hang up console */ if (ttfdflg) return(0); /* Don't mess with terminal if */ /* we got ttyfd from another process */ opcode = ASYNC_HANGUP_OPCODE; dummy = 0; s$control (&ttyport, &opcode, &dummy, &status); if (status) { debug(F111,"tthang s$control ASYNC_HANGUP status",ttnmful,status); return -1; } msleep(HUPTIME); chkalrm(); rv = 1; #ifdef CLSOPN /* * This may be needed to re-arm the async port. * This will REALLY hammer a vterm connection, though. */ strcpy (oldtty, ttnmsv); debug(F111,"tthang CLSOPN xlocal",oldtty,xlocal); oldspeed = ttspeed; oldflow = ttflow; ttclos (); sleep (2); /* wait a little bit, to let modem figure it out */ rv = ttopen (oldtty, &xlocal, ttmdm, 5); if (!rv) rv = ttvt(oldspeed,oldflow); #endif return rv; /* success */ } /* T T R E S -- Restore terminal to "normal" mode. */ /* ske@pkmab.se: There are two choices for what this function should do. * (1) Restore the tty to current "normal" mode, with carrier treatment * according to ttcarr, to be used after every kermit command. (2) Restore * the tty to the state it was in before kermit opened it. These choices * conflict, since ttold can't hold both choices of tty parameters. ttres() * is currently being called as in choice (1), but ttold basically holds * the initial parameters, as in (2), and the description at the beginning * of this file says (2). * * I don't think restoring tty parameters after all kermit commands makes * much of a difference. Restoring them upon exit from kermit may be of * some use in some cases (when the line is not restored automatically on * close, by the operating system). * * I can't choose which one it should be, so I haven't changed it. It * probably works as it is, too. It would probably even work even with * ttres() entirely deleted... * * (from fdc: Actually, this function operates in remote mode too, so * it restores the console (command) terminal to whatever mode it was * in before packet operations began, so that commands work right again.) */ int ttres() { /* Restore the tty to normal. */ short ttyport; short opcode; short status; chkalrm(); if (ttyfd < 0) return(-1); /* Not open. */ if (ttfdflg) return(0); /* Don't mess with terminal modes if */ /* we got ttyfd from another process */ #ifdef NETCONN if (netconn) return (0); /* Network connection, do nothing */ #endif /* NETCONN */ tvtflg = 0; /* Invalidate terminal mode settings */ ttyport = ttyfd; opcode = SET_MODES_OPCODE; s$control (&ttyport, &opcode, &ttold, &status); if (status) { debug(F111,"ttres s$control SET_MODES status",ttnmful,status); return -1; } ttcur = ttold; ttyraw = 0; chkalrm(); return(0); } /* T T H F L O W -- Set hardware flow control. */ static int tthflow(flow) int flow; { int x = 0; /* Return code */ chkalrm(); return(x); } /* T T P K T -- Condition the communication line for packets */ /* or for modem dialing */ /* If called with speed > -1, also set the speed. Returns 0 on success, -1 on failure. NOTE: the "xflow" parameter is supposed to be the currently selected type of flow control, but for historical reasons, this parameter is also used to indicate that we are dialing. Therefore, when the true flow control setting is needed, we access the external variable "flow", rather than trusting our "xflow" argument. */ int #ifdef CK_ANSIC ttpkt(long speed, int xflow, int parity) #else ttpkt(speed,xflow,parity) long speed; int xflow, parity; #endif /* CK_ANSIC */ /* ttpkt */ { int s2; int s = -1; int x; short status; short ttyport; short opcode; term_configure config; extern int flow; /* REAL flow-control setting */ chkalrm(); if (ttyfd < 0) return(-1); /* Not open. */ debug(F101,"ttpkt parity","",parity); debug(F101,"ttpkt xflow","",xflow); debug(F101,"ttpkt speed","",(int) speed); #ifdef NETCONN if (netconn) { chkalrm(); tvtflg = 1; /* Network connections */ return(0); /* require no special setup */ } #endif /* NETCONN */ tvtflg = 0; ttprty = parity; /* Let other tt functions see these. */ ttpflg = 0; /* Parity not sensed yet */ ttpmsk = ttprty ? 0177 : 0377; /* Parity stripping mask */ ttspeed = speed; /* Make global copy for this module */ if (ttfdflg && !vostty(ttyfd)) return(0); /* Don't change if not a tty */ ttyport = ttyfd; opcode = SET_MODES_OPCODE; s$control (&ttyport, &opcode, &ttraw, &status); if (status) { debug(F111,"ttpkt s$control SET_MODES status",ttnmful,status); return -1; } chkalrm(); ttyraw = 1; /* remember it's in raw mode */ ttcur = ttraw; opcode = TERM_GET_CONFIG_OPCODE; s$control (&ttyport, &opcode, &config, &status); if (status) { debug(F111,"ttpkt s$control TERM_GET_CONFIG status",ttnmful,status); return -1; } if (ttspeed > -1 && xlocal) { switch (ttspeed) { case 50: config.baud = OS_BAUD_50; break; case 75: config.baud = OS_BAUD_75; break; case 110: config.baud = OS_BAUD_110; break; case 150: config.baud = OS_BAUD_150; break; case 300: config.baud = OS_BAUD_300; break; case 600: config.baud = OS_BAUD_600; break; case 1200: config.baud = OS_BAUD_1200; break; case 2400: config.baud = OS_BAUD_2400; break; case 3600: config.baud = OS_BAUD_3600; break; case 4800: config.baud = OS_BAUD_4800; break; case 7200: config.baud = OS_BAUD_7200; break; case 9600: config.baud = OS_BAUD_9600; break; case 19200: config.baud = OS_BAUD_19200; break; case 38400: config.baud = OS_BAUD_38400; break; default: break; } } switch (ttprty) { case 0: config.parity = OS_NO_PARITY; break; case 'e': config.parity = OS_EVEN_PARITY; break; case 'o': config.parity = OS_ODD_PARITY; break; case 'm': config.parity = OS_MARK_PARITY; break; case 's': config.parity = OS_SPACE_PARITY;break; default: break; } opcode = TERM_CONFIGURE_OPCODE; s$control (&ttyport, &opcode, &config, &status); if (status) { debug(F111,"ttpkt s$control TERM_CONFIGURE status",ttnmful,status); /* DON'T RETURN FAILURE! window_term's can't do this! */ } /* we don't mess with the flow control */ /**/ chkalrm(); return (0); } /* T T V T -- Condition communication line for use as virtual terminal */ int #ifdef CK_ANSIC ttvt(long speed, int flow) #else ttvt(speed,flow) long speed; int flow; #endif /* CK_ANSIC */ /* ttvt */ { int s, s2; short status; short ttyport; short opcode; term_configure config; debug(F101,"ttvt ttyfd","",ttyfd); debug(F101,"ttvt tvtflg","",tvtflg); debug(F101,"ttvt speed","",speed); chkalrm(); if (ttyfd < 0) return(-1); /* Not open. */ #ifdef NETCONN if (netconn) { tvtflg = 1; /* Network connections */ debug(F101,"ttvt netconn","",netconn); return(0); /* require no special setup */ } #endif /* NETCONN */ if (tvtflg != 0 && speed == ttspeed && flow == ttflow && ttcarr == curcarr) return(0); /* Already been called. */ if (ttfdflg && !vostty(ttyfd)) return(0); ttspeed = speed; ttflow = flow; chkalrm(); if (xlocal) { /* For external lines... */ s2 = (int) (speed / 10L); s = ttsspd(s2); /* Check/set the speed */ carrctl(&tttvt, flow != FLO_DIAL /* Do carrier control */ && (ttcarr == CAR_ON || (ttcarr == CAR_AUT && ttmdm != 0))); } else s = s2 = -1; ttyport = ttyfd; opcode = SET_MODES_OPCODE; s$control (&ttyport, &opcode, &ttraw, &status); if (status) { debug(F111,"ttvt s$control SET_MODES status",ttnmful,status); return -1; } ttyraw = 1; /* remember it's in raw mode */ ttcur = ttraw; chkalrm(); opcode = TERM_GET_CONFIG_OPCODE; s$control (&ttyport, &opcode, &config, &status); if (status) { debug(F111,"ttvt s$control TERM_GET_CONFIG status",ttnmful,status); return -1; } if (ttspeed > -1 && xlocal) { switch (ttspeed) { case 50: config.baud = OS_BAUD_50; break; case 75: config.baud = OS_BAUD_75; break; case 110: config.baud = OS_BAUD_110; break; case 150: config.baud = OS_BAUD_150; break; case 300: config.baud = OS_BAUD_300; break; case 600: config.baud = OS_BAUD_600; break; case 1200: config.baud = OS_BAUD_1200; break; case 2400: config.baud = OS_BAUD_2400; break; case 3600: config.baud = OS_BAUD_3600; break; case 4800: config.baud = OS_BAUD_4800; break; case 7200: config.baud = OS_BAUD_7200; break; case 9600: config.baud = OS_BAUD_9600; break; case 19200: config.baud = OS_BAUD_19200; break; case 38400: config.baud = OS_BAUD_38400; break; default: break; } } switch (ttprty) { case 0: config.parity = OS_NO_PARITY; break; case 'e': config.parity = OS_EVEN_PARITY; break; case 'o': config.parity = OS_ODD_PARITY; break; case 'm': config.parity = OS_MARK_PARITY; break; case 's': config.parity = OS_SPACE_PARITY;break; default: break; } opcode = TERM_CONFIGURE_OPCODE; s$control (&ttyport, &opcode, &config, &status); if (status) { debug(F111,"ttvt s$control TERM_CONFIGURE status",ttnmful,status); /* DON'T RETURN FAILURE! window_term's can't do this! */ } /* we don't mess with the flow control */ /**/ chkalrm(); tvtflg = 1; debug(F101,"ttvt done, setting flag","",tvtflg); return(0); } /* T T S S P D -- Checks and sets transmission rate. */ /* Call with speed in characters (not bits!) per second. */ /* Returns internal speed code if successful, -1 otherwise. */ int ttsspd(cps) int cps; { int s, s2; short ttyport; short status; short opcode; term_configure config; debug(F101,"ttsspd","",cps); chkalrm(); #ifdef NETCONN if (netconn) return (0); #endif /* NETCONN */ if (cps < 0) return(-1); s = s2 = -1; /* First check that the given speed is valid. */ switch (cps) { case 5: s = OS_BAUD_50; break; case 7: s = OS_BAUD_75; break; case 11: s = OS_BAUD_110; break; case 15: s = OS_BAUD_150; break; case 30: s = OS_BAUD_300; break; case 60: s = OS_BAUD_600; break; case 120: s = OS_BAUD_1200; break; case 240: s = OS_BAUD_2400; break; case 360: s = OS_BAUD_3600; break; case 480: s = OS_BAUD_4800; break; case 720: s = OS_BAUD_7200; break; case 960: s = OS_BAUD_9600; break; case 1920: s = OS_BAUD_19200; break; case 3840: s = OS_BAUD_38400; break; default: debug(F111,"ttsspd","INVALID SPEED",cps); return(-1); } /* Actually set the speed */ chkalrm(); if (ttyfd > -1 && s > -1 && xlocal != 0) { if (s2 == -1) s2 = s; ttyport = ttyfd; opcode = TERM_GET_CONFIG_OPCODE; s$control (&ttyport, &opcode, &config, &status); if (status) { debug(F101,"ttsspd s$control get config status","",status); return (-1); } config.baud = s; opcode = TERM_CONFIGURE_OPCODE; s$control (&ttyport, &opcode, &config, &status); if (status) { debug(F101,"ttsspd s$control term configure status","",status); return (-1); } } else { debug(F101,"ttsspd ignoring request, xlocal","",xlocal); } ttspeed = cps * 10; chkalrm(); return(1); } /* T T G S P D - Get speed of currently selected tty line */ /* This isn't always 100% accurate. Some types of devices lie a lot about their speed (VTERMs are the worst about this). If in local mode, it should call congspd() to find out either what VOS thinks the speed is, which may or may not be right, or what the user said the speed is, say if you are using a PAD or front-end that lies about the speed. */ long ttgspd() { /* Get current tty speed */ int s; long ss; short ttyport; short opcode; short status; term_configure config; chkalrm(); #ifdef NETCONN if (netconn) return(-1); /* -1 if network connection */ #endif /* NETCONN */ if (ttyfd < 0) { return (-1); } if (ttyfd > -1 && xlocal != 0) { ttyport = ttyfd; opcode = TERM_GET_CONFIG_OPCODE; s$control (&ttyport, &opcode, &config, &status); if (status) { debug(F111,"ttgspd s$control TERM_GET_CONFIG status", ttnmful,status); return -1; } if (config.baud < 0 || config.baud >= NUMBAUDS) ttspeed = 0; else ttspeed = vosbaud[config.baud]; } chkalrm(); debug(F101,"ttgspd speed","",ttspeed); return(ttspeed); } long congspd() { /* Get current console speed */ int s; long ss; short conport; short opcode; short status; term_configure config; chkalrm(); if (comspd > 0) { debug(F101,"congspd returning saved speed","",comspd); return (comspd); } conport = TERMINAL_PORT_ID; /* always ask about TERMINAL */ opcode = TERM_GET_CONFIG_OPCODE; s$control (&conport, &opcode, &config, &status); if (status) { debug(F111,"congspd s$control TERM_GET_CONFIG status",ttnmful,status); return -1; } if (config.baud < 0 || config.baud >= NUMBAUDS) comspd = 0; else comspd = vosbaud[config.baud]; chkalrm(); debug(F101,"congspd speed","",comspd); return(comspd); } /* New buffered input scheme. */ #define TTXBUFL 1024 /* Internal buffer size */ CHAR ttxbuf[TTXBUFL+1]; /* The buffer */ int ttxbp = 0, ttxbn = 0; /* Buffer pointer and count */ #ifdef NETCONN _PROTOTYP(int netbufr, (int timo)); #endif /* T X B U F R Read bytes from communication device into internal buffer ttxbuf[]. To be called only when input buffer is empty, i.e. when ttxbn == 0. Other comm-device reading routines, like ttinc, ttinl, ttxin, should check the internal buffer first, and call this routine for a refill if necessary. When data is read successfully, the first character is returned and the global buffer count, ttxbn, is set to the number of characters remaining in ttxbuf after it, and the global buffer offset, ttxbp, is set to 1. ttxbp points to the first character which has not yet been read. ttxbn is the number of buffered characters that have not yet been read. When data is not read successfully, -1 is returned indicating a timeout, or -2 indicating disconnection. */ int txbufr(timo) int timo; { /* TT Buffer Read */ short ttyport; short status; short status2; short buflen; short readlen; long timeout; #ifdef COMMENT debug(F101,"txbufr entry, timo","",timo); #endif if (ttxbn > 0) { /* Should not be called */ debug(F101,"txbufr called with ttxbn","",ttxbn); /* if ttxbn > 0! */ ttxbn--; return(ttxbuf[ttxbp++] & 0xff); } ttxbp = ttxbn = 0; /* Reset buffer pointer and count */ if (timo < 0) /* Be safe */ timo = 0; #ifdef NETCONN if(netconn) { /* pass it over to the network version */ buflen = netbufr(timo); if (buflen > 0) { /* netbufr set up ttxb* variables with new data */ ttxbn--; /* count the one we're sending back */ return(ttxbuf[ttxbp++] & 0xff); } else if (buflen < 0) /* an error */ return (buflen); /* pass it back to caller */ else /* didn't get anything */ return (-1); /* no data available */ } #endif ttyport = ttyfd; if (timo > 0) { timeout = timo; /* not in seconds anymore! no conversion */ s$set_io_time_limit (&ttyport, &timeout, &status); if (status) return (-2); /* disconnect */ } buflen = TTXBUFL; /* Maximum characters to read */ s$read_raw (&ttyport, &buflen, &readlen, ttxbuf, &status); if (timo > 0) { timeout = -1; /* clear I/O time limit */ s$set_io_time_limit (&ttyport, &timeout, &status2); if (status2) return (-2); } /* Check for disconnection */ if (status == e$line_hangup || status == e$vc_disconnected) { debug(F100,"txbufr 1 hangup status","",status); return(-2); } /* check for some codes returned as errors, but with good data */ if (readlen > 0 && (status == e$caller_must_wait || status == e$short_record)) status = 0; if (status == e$timeout || status == e$caller_must_wait) { #ifdef COMMENT debug(F101, "txbufr returning timeout", "", 0); #endif return(-1); } if (status != 0) { debug(F101,"txbufr ignoring error, status","",status); status = 0; } #ifdef COMMENT debug(F101,"txbufr readlen","",readlen); #endif /* * Did anything useful happen? */ if(readlen > 0) { ttxbn = readlen; /* Set buffer count. */ ttxbn--; /* Less one for the one we return */ return(ttxbuf[ttxbp++]); /* Return it, bump offset */ } /* * We didn't get data, a timeout, or an error... This is weird. */ return (-2); /* call it an error */ } /* define a macro to get chars from the buffer */ /* ttxgetc has timo in seconds, txbufr has it in ticks */ #define ttxgetc(timo) (ttxbn ? (ttxbn--, ttxbuf[ttxbp++]) : txbufr(timo<<10)) /* T T F L U I -- Flush tty input buffer */ int ttflui() { int n; /* do flush of tty input buffer */ /* Network flush is done specially, in the network support module. */ chkalrm(); if (netconn) return(netflui()); debug(F101,"ttflui ttyfd","",ttyfd); if (ttyfd < 0) return(-1); /* kill what's in the ttxbuf */ if (ttxbn) { ttxbn = 0; ttxbp = 0; } txbufr(10); /* give it a miniscule timeout, .01 second */ /* kill what's been added to the ttxbuf */ if (ttxbn) { ttxbn = 0; ttxbp = 0; } if ((n = ttchk()) > 0) { debug(F101,"ttflui reading","",n); while ((n--) && ttinc(0) > 1) ; } return(0); } int ttfluo() { /* Flush output buffer */ chkalrm(); return(0); /* (dummy for now) */ } /* Interrupt Functions */ /* Set up terminal interrupts on console terminal */ #ifndef STRATUS /* we don't have sigquit. This was set pretty much the same as SIGINT. */ SIGTYP esctrp(foo) int foo; { /* trap console escapes (^\) */ signal(SIGQUIT,SIG_IGN); /* ignore until trapped */ conesc = 1; debug(F101,"esctrp caught SIGQUIT","",conesc); } #endif /* C O N B G T -- Background Test */ /* Call with flag == 1 to prevent signal test, which can not be expected to work during file transfer, when SIGINT probably *is* set to SIG_IGN. Call with flag == 0 to use the signal test, but only if the process-group test fails, as it does on some UNIX systems, where getpgrp() is buggy, requires an argument when the man page says it doesn't, or vice versa. If flag == 0 and the process-group test fails, then we determine background status simply (but not necessarily reliably) from isatty(). conbgt() sets the global backgrd = 1 if we appear to be in the background, and to 0 if we seem to be in the foreground. conbgt() is highly prone to misbehavior. On VOS, we check the process info block. If interactive mode bit is not set, then we are in the background. (Aren't OLTP OS's fun?) If we are in the forground, we still don't want to use a prompt if input has been redirected to a command macro or something, so we check vostty as well. The "Signal test" doesn't work anywhere close to the same on VOS, so we don't use it. */ VOID conbgt(flag) int flag; { int x = -1, /* process group or SIGINT test */ y = 0; /* vostty() test */ PROCESS_INFO pinfo; long pid; short status; /* Check for background operation, even if not running on real tty, so that background flag can be set correctly. If background status is detected, then Kermit will not issue its interactive prompt or most messages. If your prompt goes away, you can blame (and fix?) this function. */ /* Use process-type test if possible. */ chkalrm(); s$get_process_id (&pid); pinfo.version = PROCESS_INFO_VERSION; s$get_process_info (&pid, &pinfo, &status); if ((pinfo.info.flags & INTERACTIVE) == 0) backgrd = 1; else { if (vostty(TERMINAL_PORT_ID)) /* do we have a tty terminal port ? */ backgrd = 0; else backgrd = 1; } debug(F101,"conbgt backgrd","",backgrd); } /* C O N I N T -- Console Interrupt setter */ /* First arg is pointer to function to handle SIGTERM & SIGINT (like Ctrl-C). Second arg is pointer to function to handle SIGTSTP (suspend). */ VOID /* Set terminal interrupt traps. */ #ifdef CK_ANSIC conint(SIGTYP (*f)(int), SIGTYP (*s)(int)) #else conint(f,s) SIGTYP (*f)(), (*s)(); #endif /* CK_ANSIC */ /* conint */ { chkalrm(); conbgt(0); /* Do background test. */ /* Set the desired handlers for hangup and software termination. */ signal(SIGTERM,f); /* Software termination */ /* Now handle keyboard stop, quit, and interrupt signals. */ /* Check if invoked in background -- if so signals set to be ignored. */ /* However, if running under a job control shell, don't ignore them. */ /* We won't be getting any, as we aren't in the terminal's process group. */ debug(F101,"conint backgrd","",backgrd); if (backgrd) { /* In background, ignore signals */ debug(F101,"conint background ignoring signals","",0); signal(SIGINT,SIG_IGN); /* Keyboard interrupt */ } else { /* Else in foreground or suspended */ debug(F101,"conint foreground catching signals, jc","",0); signal(SIGINT,f); /* Catch terminal interrupt */ if (conesc) conesc = 0; /* Clear out pending escapes */ } chkalrm(); } /* C O N N O I -- Reset console terminal interrupts */ SIGTYP /* Dummy function to ignore signals */ #ifdef CK_ANSIC sig_ign(int foo) #else sig_ign(foo) int foo; #endif /* CK_ANSIC */ /* sig_IGN */ { /* Just like the real one, but has */ } /* different address. */ VOID connoi() { /* Console-no-interrupts */ debug(F100,"connoi","",0); /* Note the locally defined replacement for SIG_IGN that is used here */ /* for the SIGINT setting. This is done so that the Sys V background */ /* test -- (signal(SIGINT,SIG_IGN) == SIG_IGN) -- can work. If we use */ /* the real SIG_IGN here, then conint will always decide that this */ /* program is running in the background! */ signal(SIGINT,sig_ign); /* <--- note! */ signal(SIGHUP,SIG_DFL); signal(SIGTERM,SIG_IGN); chkalrm(); } initrawq(tty) int tty; { chkalrm(); return(0); } static VOID catch(foo) int foo; { longjmp(jjbuf, -1); } /* G E N B R K -- Simulate a modem break. */ VOID genbrk(fn,msec) int fn, msec; { short port = fn; short opcode = SEND_BREAK_OPCODE; short status; short dummy = 0; s$control (&port, &opcode, &dummy, &status); chkalrm(); debug(F101,"genbrk status","",status); return; } /* T T C H K -- Tell how many characters are waiting in tty input buffer */ /* Some callers of this want to know whether there is something to read * either in the system buffers or in our private buffers (and mostly don't * care how many characters, just whether it's more than zero or not), while * some others would be better off with the number of characters in our * private buffers only. * * Some systems can say how many characters there are in the system buffers. * Others can not. For those that can't, the number in the private buffers * will have to do (or should we put the tty into O_NDELAY-mode and try to * read one character?). If the system can tell whether there is zero or * more than zero characters, we can return 1 in the latter case even if the * private buffer is empty. (That is good for sliding windows.) */ int ttchk() { int n = 0; #ifdef NETCONN if (netconn) return (nettchk()); #endif /* NETCONN */ if (ttxbn > 0) n += ttxbn; chkalrm(); debug(F101,"ttchk returns","",n); return(n); } /* T T X I N -- Get n characters from tty input buffer */ /* Returns number of characters actually gotten, or -1 on failure */ /* Intended for use only when it is known that n characters are actually */ /* Available in the input buffer. */ int ttxin(n,buf) int n; CHAR *buf; { register int x, c; debug(F101,"ttxin n","",n); if (n < 1) return(0); #ifdef COMMENT #ifdef STRATUSX25 if (netconn && (ttnet == NET_VX25)) /* X.25 connection */ return(x25xin(n,buf)); #endif /* STRATUSX25 */ #endif /* COMMENT */ ttpmsk = (ttprty) ? 0177 : 0377; /* Parity stripping mask. */ if (ttxbn < n) { if (!netconn) { /* All network connections do this. */ debug(F101,"ttxin called w/ too few chars","",ttxbn); } n = ttxbn; } memmove (buf,&ttxbuf[ttxbp],n); ttxbp += n; ttxbn -= n; buf[n] = '\0'; for (c = 0; c < n; c++) buf[c] &= ttpmsk; return(n); } /* T T O L -- Write string s, length n, to communication device. */ /* Returns: >= 0 on success, number of characters actually written. -1 on failure. */ #define TTOLMAXT 10 int ttol(s,n) int n; CHAR *s; { short ttyport = (short) ttyfd; short status; short sys_len; int x; int tries; int len; if (ttyfd < 0) return(-1); /* Not open? */ debug(F101,"ttol n","",n); debug(F110,"ttol s",s,0); tries = TTOLMAXT; /* Allow up to this many tries */ len = n; /* Remember original length */ while (n > 0 && tries-- > 0) { /* Be persistent */ debug(F101,"ttol try","",TTOLMAXT - tries); if (netconn) { x = nettol (s, n); } else { sys_len = n; s$write_raw (&ttyport, &sys_len, s, &status); if (status == 0 || status == e$caller_must_wait) { x = sys_len; status = 0; } else x = -1; } chkalrm(); /* check for alarms */ if (x == n) { /* Worked? */ debug(F101,"ttol ok","",x); /* OK */ return(len); /* Done */ } else if (x < 0) { /* No, got error? */ debug(F101,"ttol failed","",errno); if (netconn && ttnet == NET_TCPB) { ttclos(0); /* Close the connection. */ } return(-1); } else { /* No error, so partial success */ debug(F101,"ttol partial","",x); s += x; /* Point to part not written yet */ n -= x; /* Adjust length */ if (x > 0) msleep(100); /* Wait 100 msec */ } /* Go back and try again */ } return(n < 1 ? len : -1); /* Return the results */ } /* T T O C -- Output a character to the communication line */ /* This function should only be used for interactive, character-mode operations, like terminal connection, script execution, dialer i/o, where the overhead of the signals and alarms does not create a bottleneck. */ int #ifdef CK_ANSIC ttoc(char c) #else ttoc(c) char c; #endif /* CK_ANSIC */ /* ttoc */ { int xx; short ttyport = ttyfd; short len = 1; short status; c &= 0xff; /* debug(F101,"ttoc","",(CHAR) c); */ chkalrm(); if (ttyfd < 0) return(-1); /* Check for not open. */ if (netconn) return (nettoc(c)); s$write_raw (&ttyport, &len, &c, &status); if (status != 0) return -1; return(0); /* Return good code. */ } /* T T I N L -- Read a record (up to break character) from comm line. */ /* Reads up to "max" characters from the communication line, terminating on: (a) the packet length field if the "turn" argument is zero, or (b) on the packet-end character (eol) if the "turn" argument is nonzero (c) two Ctrl-C's in a row and returns the number of characters read upon success, or if "max" was exceeded or the timeout interval expired before (a) or (b), returns -1. The characters that were input are copied into "dest" with their parity bits stripped if parity was selected. Returns the number of characters read. Characters after the eol are available upon the next call to this function. The idea is to minimize the number of system calls per packet, and also to minimize timeouts. This function is the inner loop of the program and must be as efficient as possible. The current strategy is to use myread(). WARNING: this function calls parchk(), which is defined in another module. Normally, ckutio.c does not depend on code from any other module, but there is an exception in this case because all the other ck?tio.c modules also need to call parchk(), so it's better to have it defined in a common place. Since this function has grown to have its fingers so deeply into the protocol, it is slated for removal: rpack() will take care of everything. */ #ifdef CTRLC #undef CTRLC #endif /* CTRLC */ #define CTRLC '\03' /* We have four different declarations here because: (a) to allow Kermit to be built without the automatic parity sensing feature (b) one of each type for ANSI C, one for non-ANSI. */ int #ifdef PARSENSE #ifdef CK_ANSIC ttinl(CHAR *dest, int max,int timo, CHAR eol, CHAR start, int turn) #else ttinl(dest,max,timo,eol,start,turn) int max,timo,turn; CHAR *dest, eol, start; #endif /* CK_ANSIC */ #else /* not PARSENSE */ #ifdef CK_ANSIC ttinl(CHAR *dest, int max,int timo, CHAR eol) #else ttinl(dest,max,timo,eol) int max,timo; CHAR *dest, eol; #endif /* CK_ANSIC */ #endif /* PARSENSE */ /* ttinl */ { #ifdef PARSENSE int pktlen = -1; int lplen = 0; int havelen = 0; int flag = 0; #endif /* PARSENSE */ register int i, m, n; /* local variables */ time_t tout, now; int ccn = 0; chkalrm(); if (ttyfd < 0) return(-1); /* Not open. */ debug(F101,"ttinl max","",max); debug(F101,"ttinl timo","",timo); *dest = '\0'; /* Clear destination buffer */ if (timo < 0) timo = 0; /* Safety */ now = time(NULL); tout = now + timo; #ifdef PARSENSE flag = 0; /* Start of packet flag */ #endif /* PARSENSE */ ttpmsk = m = (ttprty) ? 0177 : 0377; /* Set parity stripping mask. */ /* Now read into destination, stripping parity and looking for the */ /* the packet terminator, and also for transfer cancellation chars */ i = 0; /* Destination index */ #ifdef COMMENT debug(F101,"ttinl eol","",eol); #endif while (i < max-1) { now = time(NULL); if(timo && (tout < now)) { /* strictly less than */ debug(F101,"ttinl timed out","",tout); return (-1); /* timed out */ } n = ttinc(timo ? (tout-now) : 0); if (n < 0) { debug(F101,"ttinl ttinc error, char","",n); return (n); /* error */ } #ifdef COMMENT debug(F101,"ttinl char","", (n & ttpmsk)); #endif #ifdef PARSENSE /* Figure out what the length is supposed to be in case the packet has no terminator (as with Honeywell GCOS-8 Kermit). */ #ifndef xunchar #define xunchar(ch) (((ch) - 32 ) & 0xFF ) /* Character to number */ #endif /* xunchar */ if ((flag == 0) && ((n & 0x7f) == start)) flag = 1; if (flag) dest[i++] = n & ttpmsk; /* If we have not been instructed to wait for a turnaround character, we can go by the packet length field. If turn != 0, we must wait for the end of line (eol) character before returning. */ if (i == 2) { pktlen = xunchar(dest[1]); havelen = (pktlen > 1); debug(F101,"ttinl length","",pktlen); } else if (i == 5 && pktlen == 0) { lplen = xunchar(dest[4]); } else if (i == 6 && pktlen == 0) { pktlen = lplen * 95 + xunchar(dest[5]) + 5; havelen = 1; debug(F101,"ttinl length","",pktlen); } #else dest[i++] = n & ttpmsk; #endif /* PARSENSE */ /* Use parity mask, rather than always stripping parity, to check for cancellation. Otherwise, runs like \x03\x83\x03 in packet could cancel the transfer when parity is NONE. */ /* Check cancellation */ if (!xlocal && xfrcan && ((n & ttpmsk) == xfrchr)) { if (++ccn >= xfrnum) { /* If xfrnum in a row, bail out. */ if (timo) { /* Clear timer. */ debug(F100,"ttinl got ^C^C...","",0); ttimoff(); } fprintf(stderr,"^C...\r\n"); /* Echo Ctrl-C */ return(-2); } } else ccn = 0; /* Not ^C, so reset ^C counter, */ #ifdef PARSENSE if (flag == 0) { debug(F101,"ttinl skipping","",n); continue; } #endif /* PARSENSE */ /* Check for end of packet */ if (((n & 0x7f) == eol) #ifdef PARSENSE || (!turn && havelen && (i > pktlen+1)) #endif /* PARSENSE */ ) { #ifndef PARSENSE debug(F101,"ttinl got eol","",eol); dest[i] = '\0'; /* Yes, terminate the string, */ debug(F101,"ttinl i","",i); #else if ((n & 0x7f) != eol) { debug(F101,"ttinl EOP length","",pktlen); debug(F101,"ttinl i","",i); } else debug(F101,"ttinl got eol","",eol); dest[i] = '\0'; /* Terminate the string, */ /* Parity checked yet? */ if (ttpflg++ == 0 && ttprty == 0) { if ((ttprty = parchk(dest,start,i)) > 0) { /* No, check. */ int j; debug(F101,"ttinl senses parity","",ttprty); debug(F110,"ttinl packet before",dest,0); ttpmsk = 0x7f; for (j = 0; j < i; j++) dest[j] &= 0x7f; /* Strip parity from packet */ debug(F110,"ttinl packet after ",dest,0); } else ttprty = 0; /* restore if parchk error */ } #endif /* PARSENSE */ if (timo) { /* Turn off timer. */ ttimoff(); } debug(F111,"ttinl got", dest,i); return(i); } } /* end of while() */ debug(F101,"ttinl exited while loop, i","",i); ttimoff(); chkalrm(); return(-1); } /* T T I N C -- Read a character from the communication line */ /* On success, returns the character that was read, >= 0. On failure, returns -1 or other negative error code. */ int ttinc(timo) int timo; { int ch = 0; chkalrm(); if (ttyfd < 0) return(-1); /* Not open. */ /* this can be used for ALL character input, including network io, */ /* because ttxgetc will call txbufr, which is network aware. */ ch = ttxgetc(timo); return((ch >= 0) ? ch & ttpmsk : ch); /* return negative codes intact */ } /* S N D B R K -- Send a BREAK signal of the given duration */ static int #ifdef CK_ANSIC sndbrk(int msec) { /* Argument is milliseconds */ #else sndbrk(msec) int msec; { #endif /* CK_ANSIC */ #ifndef POSIX int x, n; #endif /* POSIX */ debug(F101,"ttsndb ttyfd","",ttyfd); if (ttyfd < 0) return(-1); /* Not open. */ if (netconn) /* Send network BREAK */ return(netbreak()); if (msec < 1 || msec > 5000) return(-1); /* Bad argument */ genbrk(ttyfd,250); /* Simulate a BREAK */ return(0); } /* T T S N D B -- Send a BREAK signal */ int ttsndb() { return(sndbrk(275)); } /* T T S N D L B -- Send a Long BREAK signal */ int ttsndlb() { return(sndbrk(1500)); } /* M S L E E P -- Millisecond version of sleep(). */ /* Call with number of milliseconds (thousandths of seconds) to sleep. Intended only for small intervals. For big ones, just use sleep(). Highly system-dependent. Returns 0 always, even if it didn't work. */ int msleep(m) int m; { short status; /* argument to s$sleep is really in 1024/s but, this is close enough */ m += m / 40; /* this gives 1025 per second */ s$sleep (&m, &status); chkalrm(); return(0); } /* R T I M E R -- Reset elapsed time counter */ VOID rtimer() { tcount = time( (time_t *) 0 ); debug(F101,"rtimer","",(long) tcount); chkalrm(); } /* G T I M E R -- Get current value of elapsed time counter in seconds */ int gtimer() { int x; x = (int) (time( (time_t *) 0 ) - tcount); debug(F101,"gtimer","",x); chkalrm(); return( (x < 0) ? 0 : x ); } #ifdef GFTIMER /* I know this is different than the structure in the manual. But since we know what it's really got in it, we can handle it this way. We've just moved the radix point a little. */ struct jiffies { unsigned long high; unsigned long low; }; static double dzero; VOID rftimer() { struct jiffies tzero; s$jiffy_date_time(&tzero); dzero = (tzero.high * (double)65536.0) + (tzero.low / (double)65536.0); } CKFLOAT gftimer() { struct jiffies tnow; double dnow; double s; #ifdef DEBUG char fpbuf[64]; #endif /* DEBUG */ s$jiffy_date_time(&tnow); dnow = (tnow.high * (double)65536.0) + (tnow.low / (double)65536.0); s = dnow - dzero; if (s == 0.0) s = 0.000001; #ifdef DEBUG if (deblog) { sprintf(fpbuf,"%f",s); debug(F110,"gftimer",fpbuf,0); } #endif /* DEBUG */ return((CKFLOAT)s); } #endif /* GFTIMER */ /* Z T I M E -- Return date/time string */ VOID ztime(s) char **s; { struct tm *tp; time_t xclock; time(&xclock); tp = localtime(&xclock); *s = asctime(tp); debug(F111,"ztime",*s,(long) xclock); chkalrm(); } /* C O N G M -- Get console terminal modes. */ /* Saves initial console mode, and establishes variables for switching between current (presumably normal) mode and other modes. Should be called when program starts, but only after establishing whether program is in the foreground or background. Returns 1 if it got the modes OK, 0 if it did nothing, -1 on error. */ int congm() { TERMINAL_INFO tinfo; short opcode; short conport = TERMINAL_PORT_ID; short status; chkalrm(); if (backgrd || !vostty(conport)) { /* If in background. */ cgmf = -1; /* Don't bother, modes are garbage. */ return(-1); } if (cgmf > 0) return(0); /* Already did this. */ debug(F100,"congm getting modes","",0); /* Need to do it. */ opcode = GET_INFO_OPCODE; tinfo.version = TERMINAL_INFO_VERSION_2; s$control (&conport, &opcode, &tinfo, &status); if (status) { debug(F111,"congm s$control GET_INFO status",ttnmful,status); return -1; } ccold = tinfo.modes; cccbrk = tinfo.modes; if (0 == plines) plines = tinfo.pause_lines; cccbrk &= ~(OS_FUNCTION_KEY_INPUT | OS_BREAK_TABLE_RECORD | OS_GENERIC_INPUT | OS_FORMS_INPUT); cccbrk |= OS_RAW_INPUT | OS_BULK_RAW_INPUT; ccraw = cccbrk; chkalrm(); cgmf = 1; /* Flag that we got them. */ return(1); } /* C O N C B -- Put console in cbreak mode. */ /* Returns 0 if ok, -1 if not */ int #ifdef CK_ANSIC concb(char esc) #else concb(esc) char esc; #endif /* CK_ANSIC */ /* concb */ { short opcode = SET_MODES_OPCODE; short port = TERMINAL_PORT_ID; short status; chkalrm(); if (cgmf < 1) return(0); /* Console modes not available yet */ debug(F101,"concb backgrd","",backgrd); if (backgrd) return(0); /* Do nothing if in background. */ if (!vostty(port)) return(0); /* Only for real ttys */ if (no_wait) cancio(); /* back to wait mode */ s$control (&port, &opcode, &ccraw, &status); if (status) { debug(F101,"concb s$control[SET_MODES] status","",status); return (-1); } escchr = esc; /* Make this available to other fns */ ckxech = 1; /* Program can echo characters */ conraw = 1; chkalrm(); return(0); } /* C O N B I N -- Put console in binary mode */ /* Returns 0 if ok, -1 if not */ int #ifdef CK_ANSIC conbin(char esc) #else conbin(esc) char esc; #endif /* CK_ANSIC */ /* conbin */ { short port = TERMINAL_PORT_ID; short opcode = SET_MODES_OPCODE; short status; chkalrm(); debug(F101,"conbin esc","",esc); if (!vostty(port)) /* only for real ttys */ return(0); congm(); /* Get modes if necessary. */ s$control (&port, &opcode, &ccraw, &status); if (status) debug(F101,"conbin s$control SET_MODES status","",status); escchr = esc; /* Make this available to other fns */ ckxech = 1; /* Program can echo characters */ conraw = 2; chkalrm(); return (0); } /* C O N R E S -- Restore the console terminal */ int conres() { short conport = TERMINAL_PORT_ID; short opcode = SET_MODES_OPCODE; short lines; short status; debug(F101,"conres cgmf","",cgmf); if (no_wait) /* turn it off */ cancio(); chkalrm(); if (cgmf < 1) return(0); /* Do nothing if modes unchanged */ if (!vostty(conport)) return(0); /* only for real ttys */ s$control (&conport, &opcode, &ccold, &status); if (status) debug(F101,"conres modes status","",status); ckxech = 0; /* System should echo chars */ conraw = 0; /* not in raw mode any more */ chkalrm(); return (0); } /* C O N O C -- Output a character to the console terminal */ int #ifdef CK_ANSIC conoc(char c) #else conoc(c) char c; #endif /* CK_ANSIC */ /* conoc */ { int rv; if (c == '\n' && conraw == 1) /* don't translate in binary or non-raw */ if (0 > (rv = conout("\r",1))) return rv; rv = conout(&c,1); return rv; } /* C O N X O -- Write x characters to the console terminal */ int conxo(x,s) int x; char *s; { /* this doesn't handle \n translations */ return(conout(s,x)); } /* C O N O L -- Write a line to the console terminal */ int conol(s) char *s; { int len; len = (int)strlen(s); return(vosprtf("%s",s)); } /* C O N O L A -- Write an array of lines to the console terminal */ int conola(s) char *s[]; { int i; for (i=0 ; *s[i] ; i++) if (conol(s[i]) < 0) return(-1);; return(0); } /* C O N O L L -- Output a string followed by CRLF */ int conoll(s) char *s; { conol(s); return(conoc('\n')); } /* C O N O U T -- Write to console device */ static int conout(s,n) char *s; int n; { short conport = TERMINAL_PORT_ID; short status; short slen = n; chkalrm(); if (backgrd) return -1; if (conraw) s$write_raw (&conport, &slen, s, &status); else s$seq_write_partial (&conport, &slen, s, &status); chkalrm(); if (status) { debug(F101,"conout s$write status","",status); return -1; } return slen; } /* C O N C H K -- Return how many characters available at console */ int conchk() { short status; short status2; short buflen; short reclen; long ticks; short conport; if (backgrd || !vostty(TERMINAL_PORT_ID)) return(0); chkalrm(); if (cincnt) return cincnt; /* just pass back the buffered number */ conport = TERMINAL_PORT_ID; ticks = 25; /* this isn't called very often */ s$set_io_time_limit (&conport, &ticks, &status); if (status) return (-1); buflen = sizeof cinbuff; s$read_raw (&conport, &buflen, &reclen, cinbuff, &status); if ((status == e$timeout) || (status == e$short_record)) status = 0; ticks = -1; /* turn timer off */ s$set_io_time_limit (&conport, &ticks, &status2); if (status || status2) { debug(F101,"conchk returning status","",status ? status : status2); return (-1); } cincnt = reclen; /* how many did we get? */ cinbp = 0; /* point to first character */ return (cincnt); } /* C O N I N C -- Get a character from the console */ /* Call with timo > 0 to do a timed read, timo == 0 to do an untimed read. Upon success, returns the character. Upon failure, returns -1. A timed read that does not complete within the timeout period returns -1. A no_wait timeout with nothing to read returns -2. */ int coninc(timo) int timo; { short conport = TERMINAL_PORT_ID; short status; short reclen; short buflen = sizeof cinbuff; long ticks; short sstat; if (cincnt) { cincnt--; return (cinbuff[cinbp++]); } chkalrm(); if (timo > 0 ) { /* set time limit */ ticks = timo * 1024; s$set_io_time_limit (&conport, &ticks, &status); if (status) return (-1); } while (1) { /* Keep trying till we get one. */ if (conraw) s$read_raw (&conport, &buflen, &reclen, cinbuff, &status); else s$seq_read (&conport, &buflen, &reclen, cinbuff, &status); switch (status) { case e$caller_must_wait: case e$short_record: if (reclen) status = 0; /* NO BREAK HERE */ case 0: cincnt = reclen; /* how many did we get? */ cinbp = 0; /* reset buffer pointer */ break; default: break; } if (status) break; if (reclen == 0) continue; /* Shouldn't happen. */ if (reclen > 0) { /* If read was successful, */ if (timo > 0 ) { ticks = -1; /* turn timer off */ s$set_io_time_limit (&conport, &ticks, &status); chkalrm(); if (status) return (-1); } chkalrm(); if (cincnt) { cincnt--; return ((cinbuff[cinbp++]) & 0377); /* return the character. */ } return(-1); /* shouldn't get here */ } } /* Come here if read() returned an error. */ sstat = status; if (timo > 0 ) { ticks = -1; /* turn timer off */ s$set_io_time_limit (&conport, &ticks, &status); chkalrm(); if (status) return (-1); } if (e$caller_must_wait == status) return (-2); debug(F101, "coninc(0) s$read status","",sstat); /* Log the error. */ return(-1); } /* C O N G K S -- Console Get Keyboard Scancode */ #ifndef congks /* This function needs to be filled in with the various system-dependent system calls used by SUNOS, NeXT OS, Xenix, Aviion, etc, to read a full keyboard scan code. For now, it's a dummy. */ int congks(timo) int timo; { return(coninc(timo)); } #endif /* congks */ /* contti() is used by the conect() routine to handle the problem of getting input from both the console and the tty device at the same time, without hanging on one waiting on the other. In VOS, we set the devices into no_wait mode, read the event counts, and try to read. If we don't get anything (actually, if we get e$caller_must_wait), then we call s$wait_event to do the waiting. */ int #ifdef CK_ANSIC contti(int *c, int *src) #else contti(c, src) int *c; int *src #endif /* contti () */ { short ttyport; short conport; short status; short buflen; short reclen; short which; long timeout; short count; short eventstat; char ch; *src = -1; *c = 0; ttyport = ttyfd; conport = TERMINAL_PORT_ID; chkalrm(); if (0 == no_wait) { no_wait = 1; #ifdef NETCONN if (netconn) { status = netwait (no_wait, ttyfd, &eventids[1], &eventcnts[1]); if (status) { debug(F101,"contti nowait net tty status","",status); return (-1); } } else { /* some sort of "terminal" */ #endif s$set_no_wait_mode (&ttyport, &eventids[1], &status); if (status) { debug(F101,"contti nowait tty status","",status); return (-1); } #ifdef NETCONN } #endif s$set_no_wait_mode (&conport, &eventids[0], &status); if (status) { debug(F101,"contti nowait con status","",status); return (-1); } } if (ttxbn) { /* return buffered tty character */ *src = 1; *c = ttxgetc(0); return 0; } if (cincnt) { /* return buffered console character */ *src = 0; *c = coninc(0); return 0; } #ifdef NETCONN if (netconn == 0) { /* Don't use read_event with socket events... */ #endif s$read_event (&eventids[1], &eventcnts[1], &eventstat, &status); if (status) { debug(F101,"contti read event tty status","",status); return (-1); } #ifdef NETCONN } #endif do { *c = ttxgetc(0); /* ttxgetc knows about net* io */ if (*c >= 0) { /* got one */ *src = 1; return 0; } if (*c == -2) { /* -1 is e$cmw, -2 hangup */ debug(F101,"contti read tty ch","",*c); return (-2); } s$read_event (&eventids[0], &eventcnts[0], &eventstat, &status); if (status) { debug(F101,"contti read event console status","",status); return (-1); } do { *c = coninc(0); if (*c >= 0) { /* read a character successfully */ *src = 0; return 0; } if (*c == -1) { /* error */ debug(F101,"contti coninc returns","",*c); return (-1); } timeout = -1; which = 1; /* must be initialized */ count = 2; s$wait_event (&count, eventids, eventcnts, &timeout, &which, &status); if (status) { debug(F101,"contti wait event status","",status); return (-1); chkalrm(); } } while (which == 1); /* console */ } while (which == 2); /* tty */ debug(F101,"contti cannot get here, which","",which); return (-1); } /* C O N R E S N E -- restore console to normal attributes, just like conres, but with system echo turned off. Used for escape processing during CONNECT. Actually, it wants concb without echo, but the description says conres. */ VOID conresne(void) { short conport = TERMINAL_PORT_ID; short status; short opcode = DISPLAY_OFF_OPCODE; short dummy = 0; concb(escchr); s$control (&conport, &opcode, &dummy, &status); debug(F101,"conresne s$control DISPLAY_OFF status","",status); ckxech = 1; /* Program can echo characters */ chkalrm(); } /* C A N C I O -- Finished with CONNECT, cancel no_wait and pending I/O */ VOID cancio (void) { short conport; short ttyport; short status; short opcode; short dummy = 0; chkalrm(); if (no_wait) { ttyport = ttyfd; conport = TERMINAL_PORT_ID; if (no_wait) { no_wait = 0; #ifdef NETCONN if (netconn) status = netwait (no_wait, ttyfd, NULL, NULL); else #endif /* NETCONN */ s$set_wait_mode (&ttyport, &status); if (status) debug(F101,"cancio s$set_wait_mode tty status","",status); s$set_wait_mode (&conport, &status); if (status) debug(F101,"cancio s$set_wait_mode con status","",status); } } #ifdef COMMENT /* we only do this for the console */ opcode = DISCARD_INPUT_OPCODE; s$control (&conport, &opcode, &dummy, &status); if (status) debug(F101,"cancio s$control DISCARD_INPUT status","",status); opcode = DISCARD_OUTPUT_OPCODE; s$control (&conport, &opcode, &dummy, &status); if (status) debug(F101,"cancio s$control DISCARD_OUTPUT status","",status); #endif } /* T T S C A R R -- Set ttcarr variable, controlling carrier handling. * * 0 = Off: Always ignore carrier. E.g. you can connect without carrier. * 1 = On: Heed carrier, except during dialing. Carrier loss gives disconnect. * 2 = Auto: For "modem direct": The same as "Off". * For real modem types: Heed carrier during connect, but ignore * it anytime else. Compatible with pre-5A C-Kermit versions. * * Of course, nothing of this applies to remote mode (xlocal = 0). * */ int ttscarr(carrier) int carrier; { ttcarr = carrier; debug(F101, "ttscarr","",ttcarr); chkalrm(); return(ttcarr); } /* C A R R C T L -- Set tty modes for carrier treatment. * * Sets the appropriate bits in a termio or sgttyb struct for carrier control * (actually, there are no bits in sgttyb for that), or performs any other * operations needed to control this on the current system. The function does * not do the actual TCSETA or stty, since often we want to set other bits too * first. Don't call this function when xlocal is 0, or the tty is not opened. * * We don't know how to do anything like carrier control on non-ATTSV systems, * except, apparently, ultrix. See above. It is also known that this doesn't * have much effect on a Xenix system. For Xenix, one should switch back and * forth between the upper and lower case device files. Maybe later. * Presently, Xenix will stick to the mode it was opened with. * * carrier: 0 = ignore carrier, 1 = require carrier. * The current state is saved in curcarr, and checked to save labour. */ int #ifdef CK_ANSIC carrctl(long *ttpar, int carrier) { #else carrctl(ttpar, carrier) long *ttpar; int carrier; { #endif int temp = 0; int modem_status; debug(F101, "carrctl","",carrier); chkalrm(); if (carrier == curcarr) return(0); curcarr = carrier; return(0); } /* T T G M D M -- Get modem signals */ /* Looks for RS-232 modem signals, and returns those that are on in as its return value, in a bit mask composed of the BM_xxx values defined in ckcdeb.h. Returns: -3 Not implemented -2 if the communication device does not have modem control (e.g. telnet) -1 on error. >= 0 on success, with a bit mask containing the modem signals that are on. */ int ttgmdm() { short status; DATA_SET_STATUS_INFO dssi; CV(66) data_set_name; int x, y, z; chkalrm(); #ifdef NETCONN if (netconn) return(-2); /* Network, no modem signals. */ #endif strcpy (&data_set_name, ttnmful); dssi.version = 1; dssi.flags = 0; s$get_data_set_status (&data_set_name, &dssi, &status); debug(F101,"ttgmdm",ttnmful,(long)dssi.flags); if (status) { debug(F101,"ttgmdm s$get_data_set_status status","",status); return (status == e$device_not_local) ? (-2) : (-1); } y = 0; if (dssi.flags & DSSI_DATA_SET_READY) y |= BM_DSR; if (dssi.flags & DSSI_CARRIER_DETECT) y |= BM_DCD; if (dssi.flags & DSSI_CLEAR_TO_SEND) y |= BM_CTS; return y; } /* P S U S P E N D -- Put this process in the background. */ /* Call with flag nonzero if suspending is allowed, zero if not allowed. Returns 0 on apparent success, -1 on failure (flag was zero, or kill() returned an error code. VOS doesn't support this. */ int psuspend(flag) int flag; { if (flag == 0) return(-1); chkalrm(); return(-1); /* Didn't work. Never works. */ } /* Variables for user and group IDs. */ static UID_T realuid = (UID_T) -1, privuid = (UID_T) -1; static GID_T realgid = (GID_T) -1, privgid = (GID_T) -1; /* P R I V _ I N I -- Initialize privileges package */ /* Called as early as possible in a set-uid or set-gid program to store the * set-to uid and/or gid and step down to the users real uid and gid. The * stored id's can be temporarily restored (allowed in System V) during * operations that require the privilege. Most of the time, the program * should execute in unpriviliged state, to not impose any security threat. * * Note: Don't forget that access() always uses the real id:s to determine * file access, even with privileges restored. * * Returns an error mask, with error values or:ed together: * 1 if setuid() fails, * 2 if setgid() fails, and * 4 if the program is set-user-id to "root", which can't be handled. * * Only the return value 0 indicates real success. In case of failure, * those privileges that could be reduced have been, at least, but the * program should be aborted none-the-less. * * Also note that these functions do not expect the uid or gid to change * without their knowing. It may work if it is only done temporarily, but * you're on your own. * * VOS does support suid programs (Called "owner access"), but not changing * these things around. It also only applies to file access permissions. */ int priv_ini() { int err = 0; return(err); /* always say it's okay */ } /* P R I V _ O N -- Turn on the setuid and/or setgid */ /* Go to the privileged uid (gid) that the program is set-user-id * (set-group-id) to, unless the program is running unprivileged. * If setuid() fails, return value will be 1. If getuid() fails it * will be 2. Return immediately after first failure, and the function * tries to restore any partial work done. Returns 0 on success. * Group id is changed first, since it is less serious than user id. */ int priv_on() { return(0); /* always say it's okay */ } /* P R I V _ O F F -- Turn on the real uid and gid */ /* Return to the unprivileged uid (gid) after an temporary visit to * privileged status, unless the program is running without set-user-id * (set-group-id). Returns 1 for failure in setuid() and 2 for failure * in setgid() or:ed together. The functions tries to return both uid * and gid to unprivileged state, regardless of errors. Returns 0 on * success. */ int priv_off() { int err = 0; return(err); /* always say it's okay */ } /* Turn off privilege permanently. No going back. This is necessary before * a fork() on BSD43 machines that don't save the setUID or setGID, because * we swap the real and effective ids, and we don't want to let the forked * process swap them again and get the privilege back. It will work on other * machines too, such that you can rely on its effect always being the same, * for instance, even when you're in priv_on() state when this is called. * (Well, that part about "permanent" is on System V only true if you follow * this with a call to exec(), but that's what we want it for anyway.) * Added by Dean Long -- dlong@midgard.ucsc.edu */ int priv_can() { return(priv_off()); } /* P R I V _ O P N -- For opening protected files or devices. */ int priv_opn(name, modes) char *name; int modes; { /* Return Failure, since we can't do an open with these parameters */ return(-1); } /* P R I V _ C H K -- Check privileges. */ /* Try to turn them off. If turning them off did not succeed, cancel them */ int priv_chk() { int x; x = priv_off(); /* Turn off privs. */ return(0); /* always say it's okay */ } UID_T real_uid() { return(realuid); } VOID ttimoff() { /* Turn off any timer interrupts */ short ttyport; short status; long timo = -1; /* turn it off */ if (ttyfd < 0) return; ttyport = ttyfd; s$set_io_time_limit (&ttyport, &timo, &status); debug(F101,"ttimoff status","",status); chkalrm(); return; } int vostty (int iport) { short port = (short) iport; short status; get_port_info info; int i; info.version = GET_PORT_INFO_VERSION_1; s$get_port_info (&port, &info, &status); if (status) { errno = status; return -1; } i = s$is_file_type_a_terminal (&info.type); return i; } static char *connam (void) { short port = TERMINAL_PORT_ID; short status; get_port_info info; static char tname[DEVNAMLEN+1]; info.version = GET_PORT_INFO_VERSION_1; s$get_port_info (&port, &info, &status); if (status) { errno = status; return ""; } strncpy (tname, &info.path_name, DEVNAMLEN); tname[DEVNAMLEN] = '\0'; return tname; } int vosprtf (char *fmt, ... ) { char buff[1024]; /* hope this is big enough */ va_list args; int rv; int diff; char *cp; char *lcp; va_start (args, fmt); if (backgrd) /* in background, printf is okay */ rv = vprintf (fmt, args); else /* in foreground, it is not */ { rv = vsprintf (buff, fmt, args); if (rv > sizeof buff) /* we're doomed... */ return (-1); /* loop, adding CR's to all the LF's */ lcp = buff; cp = strchr (lcp, '\n'); while (cp && *cp) { diff = cp - lcp; if (0 > conout (lcp, diff)) return (-1); if (0 > conout ("\r", 1)) return (-1); lcp = cp; cp = strchr (lcp+1, '\n'); } if (0 > conout (lcp, strlen (lcp))) return (-1); } va_end (args); return (rv); } /* * We fudge getenv here. VOS doesn't have an "environment," hence, no * environment variables. We find the ones that get asked for, and find a * way to get the requested value, and return it to the caller. The c-runtime * version always returns the empty string, so that's our default. */ char * #ifdef CK_ANSIC getenv(const char *str) #else getenv(str) char *str; #endif /* getenv */ { static char envbuf[256]; short status; short opcode; short port; TERMINAL_INFO tinfo; int len; CV(66) dummy; CV(66) modname; CV(256) envpath; char buff[300]; short io_type; short envport = 0; short bufflen; short reclen; debug(F110,"GETENV called",str,0); strcpy (envbuf, ""); /* make sure it's empty first */ chkalrm(); s$get_home_dir (&envpath); strcat (&envpath, ">ckermit.env"); io_type = INPUT_TYPE; s$seq_open (&envpath, &io_type, &envport, &status); strcpy (buff, &envpath); debug(F111," s$seq_open status",buff,status); len = strlen (str); while ('\0' == envbuf[0] && 0 == status) { bufflen = sizeof buff; s$seq_read (&envport, &bufflen, &reclen, buff, &status); if (0 == status) { buff[reclen] = '\0'; if (reclen > len && 0 == strncmp (str, buff, len)) { if ('=' == buff[len]) strcpy (envbuf, &buff[len+1]); } } } if (envport > 0) { s$close (&envport, &status); s$detach_port (&envport, &status); } if ('\0' == envbuf[0]) { /* haven't found it */ if(0 == strcmp(str, "TERM")) { /* console terminal type */ port = TERMINAL_PORT_ID; opcode = GET_INFO_OPCODE; tinfo.version = TERMINAL_INFO_VERSION_2; /* why is this 1? */ s$control (&port, &opcode, &tinfo, &status); if (status) debug(F101,"getenv getting term info status","",status); else strcpy(envbuf, &tinfo.terminal_type_name); } else if (0 == strcmp (str, "HOST")) { /* host name */ #ifdef TCPSOCKET /* we have the BSD sockets library from OS TCP/IP, */ /* so get the real hostname, if it is set. */ len = sizeof (envbuf); if (gethostname(envbuf,len) < 0) strcpy (envbuf, ""); #endif /* TCPSOCKET */ /* if that did not work or if we don't have sockets, get the */ /* system name from the name of the current module. Format */ /* is "%systemname#modulename". We return the whole thing. */ if (0 == strlen (envbuf)) { /* if 1st arg is empty string, returns current module name */ strcpy (&dummy, ""); s$expand_module_name (&dummy, &modname, &status); if (status) debug(F101,"getenv exp_mod_name status","",status); else strcpy (envbuf, &modname); } } /* add new ones here */ else { debug(F100,"GETENV returns NULL","",0); return NULL; } } debug(F110,"GETENV returns",envbuf,0); return envbuf; } #undef signal #undef alarm /* do we really need to know about all the signals? */ static char *signames[] = { /* 0 */ "SIG-0", "SIGABRT", "SIGFPE", "SIGILL", /* 4 */ "SIGINT", "SIGSEGV", "SIGTERM", "SIGUSR1", /* 8 */ "SIGUSR2", "SIGIO/POLL", "SIGHUP", "SIGURG", /* 12 */ "SIGALRM" }; SIGTYP (* #ifdef CK_ANSIC vsignal(int type, SIGTYP (*func)(int)))(int){ #else vsignal(type, func))() int type, SIGTYP (*func)(){ #endif char *cp; cp = (type <= _SIG_MAX && type >= 1) ? signames[type] : "UNKNOWN SIGNAL"; debug(F111,"VSIGNAL called",cp,(long) func); return (signal(type,func)); } int #ifdef CK_ANSIC valarm(interval) int interval; { #else valarm(int interval) { #endif time_t now; int rv; debug(F101,"VALARM called","",interval); time(&now); /* figure out how much was left. If there is time left, say * it's at least one second, even if it comes out to zero; this * prevents confusion about what a zero return value means. */ if (deadline) { rv = deadline - now; if (rv <= 0) /* may also have missed the deadline */ rv = 1; } else rv = 0; /* it wasn't set */ /* set the new deadline */ if (0 >= interval) /* uh... negative interval (space-like?) */ deadline = 0; else deadline = now + interval; return rv; } int chkalarm(void) { time_t now; if (0 == deadline) return 0; /* don't bail out */ time(&now); if (now < deadline) return 0; /* strictly less than */ /* it's time! */ debug(F101,"chkalarm timer expired, age","",now-deadline); kill (0, SIGALRM); /* We don't have raise(), either. */ /* most of the alarm handlers longjmp, so we may not get here */ return -1; }