/* prot.c Protocol support routines to move commands and data around. Copyright (C) 1991, 1992 Ian Lance Taylor This file is part of the Taylor UUCP package. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. The author of the program may be contacted at ian@airs.com or c/o AIRS, P.O. Box 520, Waltham, MA 02254. $Log: prot.c,v $ Revision 1.1.1.1 1993/03/21 09:45:37 cgd initial import of 386bsd-0.1 sources Revision 1.21 1992/04/02 22:51:09 ian Add gcc 2.0 format checking to ulog, and fixed discovered problems Revision 1.20 1992/03/30 04:49:10 ian Niels Baggesen: added debugging types abnormal and uucp-proto Revision 1.19 1992/03/30 04:07:13 ian Dirk Musstopf: remove temporary file if receive fails Revision 1.18 1992/03/13 22:59:25 ian Have breceive_char go through freceive_data Revision 1.17 1992/03/12 19:56:10 ian Debugging based on types rather than number Revision 1.16 1992/03/11 01:18:15 ian Niels Baggesen: drop the connection on a write failure Revision 1.15 1992/03/11 00:18:50 ian Save temporary file if file send fails Revision 1.14 1992/02/09 05:21:55 ian Bob Denny: call fmail_transfer before fsysdep_did_work Revision 1.13 1992/02/08 19:41:24 ian Simplify pffile calls for ancient stupid compilers Revision 1.12 1992/02/08 03:54:18 ian Include only in , added 1992 copyright Revision 1.11 1992/01/18 22:48:53 ian Reworked sending of mail and general handling of failed transfers Revision 1.10 1992/01/16 18:16:58 ian Niels Baggesen: add some debugging messages Revision 1.9 1991/12/31 19:34:19 ian Added number of bytes to pffile protocol entry point Revision 1.8 1991/12/30 04:28:30 ian John Theus: check for EOF to work around bug in fread Revision 1.7 1991/12/21 23:10:43 ian Terry Gardner: record failed file transfers in statistics file Revision 1.6 1991/12/13 04:33:38 ian Franc,ois Pinard: don't bother to warn if the final HY doesn't come in Revision 1.5 1991/11/15 21:00:59 ian Efficiency hacks for 'f' and 't' protocols Revision 1.4 1991/11/11 19:32:03 ian Added breceive_char to read characters through protocol buffering Revision 1.3 1991/11/11 04:21:16 ian Added 'f' protocol Revision 1.2 1991/11/10 19:24:22 ian Added pffile protocol entry point for file level control Revision 1.1 1991/11/09 18:51:50 ian Initial revision */ #include "uucp.h" #if USE_RCS_ID char prot_rcsid[] = "$Id: prot.c,v 1.1.1.1 1993/03/21 09:45:37 cgd Exp $"; #endif #include #include "system.h" #include "port.h" #include "prot.h" /* This file implements the generic UUCP protocol for making and confirming file transfer requests. This involves sending ASCII strings back and forth between the communicating daemons. It would be possible to use a different scheme when designing a new protocol, but this scheme is used by all traditional UUCP protocols. */ /* Local functions. */ static boolean fpsendfile_confirm P((void)); static boolean fprecfile_confirm P((void)); static boolean fploop P((void)); static void upadd_cmd P((const char *z, int clen, boolean flast)); /* Variables visible to the protocol-specific routines. */ /* Protocol structure. */ const struct sprotocol *qProto; /* Buffer to hold received data. */ char abPrecbuf[CRECBUFLEN]; /* Index of start of data in abPrecbuf. */ int iPrecstart; /* Index of end of data (first byte not included in data) in abPrecbuf. */ int iPrecend; /* Whether an unexpected shutdown is OK now; this is used to avoid giving a warning for systems that hang up in a hurry. */ boolean fPerror_ok; /* Amount of data sent for current send file; -1 means there is no current send file. */ static long cPsent_bytes = -1; /* Amount of data received for current receive file; -1 means there is no current receive file. */ static long cPreceived_bytes = -1; /* Send a file. If we are the master, we must send a command to transfer the file and wait for a confirmation that we can begin sending the file. If we are the slave, the master has sent us a command and is waiting for a reply; we must confirm that we will send the file. Either way, we begin transferring data. This function returns FALSE if there is a communication failure. It returns TRUE otherwise, even if the file transfer failed. */ boolean fsend_file (fmaster, e, qcmd, zmail, ztosys, fnew) boolean fmaster; openfile_t e; const struct scmd *qcmd; const char *zmail; const char *ztosys; boolean fnew; { if (fmaster) { int clen; char *zsend; const char *zrec; /* Send the string S zfrom zto zuser zoptions ztemp imode znotify to the remote system. We put a '-' in front of the (possibly empty) options and a '0' in front of the mode. The remote system will ignore ztemp, but it is supposed to be sent anyhow. If fnew is TRUE, we also send the size; in this case if ztemp is empty we must send it as "". */ clen = (strlen (qcmd->zfrom) + strlen (qcmd->zto) + strlen (qcmd->zuser) + strlen (qcmd->zoptions) + strlen (qcmd->ztemp) + strlen (qcmd->znotify) + 50); zsend = (char *) alloca (clen); if (! fnew) sprintf (zsend, "S %s %s %s -%s %s 0%o %s", qcmd->zfrom, qcmd->zto, qcmd->zuser, qcmd->zoptions, qcmd->ztemp, qcmd->imode, qcmd->znotify); else { const char *znotify; if (qcmd->znotify[0] != '\0') znotify = qcmd->znotify; else znotify = "\"\""; sprintf (zsend, "S %s %s %s -%s %s 0%o %s %ld", qcmd->zfrom, qcmd->zto, qcmd->zuser, qcmd->zoptions, qcmd->ztemp, qcmd->imode, znotify, qcmd->cbytes); } if (! (qProto->pfsendcmd) (zsend)) { (void) ffileclose (e); return FALSE; } /* Now we must await a reply. */ zrec = zgetcmd (); if (zrec == NULL) { (void) ffileclose (e); return FALSE; } if (zrec[0] != 'S' || (zrec[1] != 'Y' && zrec[1] != 'N')) { ulog (LOG_ERROR, "Bad response to send request"); (void) ffileclose (e); return FALSE; } if (zrec[1] == 'N') { const char *zerr; boolean fnever; fnever = TRUE; if (zrec[2] == '2') zerr = "permission denied"; else if (zrec[2] == '4') { zerr = "remote cannot create work files"; fnever = FALSE; } else if (zrec[2] == '6') { zerr = "too large for receiver now"; fnever = FALSE; } else if (zrec[2] == '7') { /* The file is too large to ever send. */ zerr = "too large for receiver"; } else { char *zset; zset = (char *) alloca (sizeof "unknown reason: " + strlen (zrec)); sprintf (zset, "unknown reason: %s", zrec); zerr = zset; } ulog (LOG_ERROR, "Can't send %s: %s", qcmd->zfrom, zerr); (void) ffileclose (e); if (fnever) { (void) fmail_transfer (FALSE, qcmd->zuser, zmail, zerr, qcmd->zfrom, zLocalname, qcmd->zto, ztosys, zsysdep_save_temp_file (qcmd->pseq)); (void) fsysdep_did_work (qcmd->pseq); } return TRUE; } } else { char absend[20]; /* We are the slave; confirm that we will send the file. We send the file mode in the confirmation string. */ sprintf (absend, "RY 0%o", qcmd->imode); if (! (qProto->pfsendcmd) (absend)) { (void) ffileclose (e); return FALSE; } } /* Record the file we are sending, and let the protocol take over. */ if (! fstore_sendfile (e, qcmd->pseq, qcmd->zfrom, qcmd->zto, ztosys, qcmd->zuser, zmail)) return FALSE; cPsent_bytes = 0; /* Tell the protocol that we are starting to send a file. */ if (qProto->pffile != NULL) { boolean (*pffile) P((boolean, boolean, boolean *, long)); /* Simplify expression for ancient compilers. */ pffile = qProto->pffile; if (! pffile (TRUE, TRUE, (boolean *) NULL, qcmd->cbytes)) return FALSE; } return fploop (); } /* Confirm that a file has been received correctly by the other side. Return FALSE for a communication error. We expect the receiving system to send back CY; if an error occurred while moving the received file into its final location, the receiving system will send back CN5. */ static boolean fpsendfile_confirm () { const char *zrec; long cbytes; const char *zerr; zrec = zgetcmd (); if (zrec == NULL) return FALSE; cbytes = cPsent_bytes; cPsent_bytes = -1; if (zrec[0] != 'C' || (zrec[1] != 'Y' && zrec[1] != 'N')) { zerr = "Bad confirmation for sent file"; ulog (LOG_ERROR, zerr); (void) fsent_file (FALSE, cbytes, zerr, FALSE); } else if (zrec[1] == 'N') { if (zrec[2] == '5') zerr = "File could not be stored in final location"; else { char *zset; zset = (char *) alloca (sizeof "File send failed: " + strlen (zrec)); sprintf (zset, "File send failed: %s", zrec); zerr = zset; } ulog (LOG_ERROR, zerr); (void) fsent_file (FALSE, cbytes, zerr, TRUE); } else (void) fsent_file (TRUE, cbytes, (const char *) NULL, FALSE); return TRUE; } /* Receive a file. If we are the master, we must set up a file request and wait for the other side to confirm it. If we are the slave, we must confirm a request made by the other side. We then start receiving the file. This function must return FALSE if there is a communication error and TRUE otherwise. We return TRUE even if the file transfer fails. */ boolean freceive_file (fmaster, e, qcmd, zmail, zfromsys, fnew) boolean fmaster; openfile_t e; const struct scmd *qcmd; const char *zmail; const char *zfromsys; boolean fnew; { unsigned int imode; if (fmaster) { int clen; char *zsend; const char *zrec; /* We send the string R from to user options We put a dash in front of options. If we are talking to a counterpart, we also send the maximum size file we are prepared to accept, as returned by esysdep_open_receive. */ clen = (strlen (qcmd->zfrom) + strlen (qcmd->zto) + strlen (qcmd->zuser) + strlen (qcmd->zoptions) + 30); zsend = (char *) alloca (clen); if (! fnew) sprintf (zsend, "R %s %s %s -%s", qcmd->zfrom, qcmd->zto, qcmd->zuser, qcmd->zoptions); else sprintf (zsend, "R %s %s %s -%s %ld", qcmd->zfrom, qcmd->zto, qcmd->zuser, qcmd->zoptions, qcmd->cbytes); if (! (qProto->pfsendcmd) (zsend)) { (void) ffileclose (e); (void) remove (qcmd->ztemp); return FALSE; } /* Wait for a reply. */ zrec = zgetcmd (); if (zrec == NULL) { (void) ffileclose (e); (void) remove (qcmd->ztemp); return FALSE; } if (zrec[0] != 'R' || (zrec[1] != 'Y' && zrec[1] != 'N')) { ulog (LOG_ERROR, "Bad response to receive request"); (void) ffileclose (e); (void) remove (qcmd->ztemp); return FALSE; } if (zrec[1] == 'N') { const char *zerr; if (zrec[2] == '2') zerr = "no such file"; else if (zrec[2] == '6') { /* We sent over the maximum file size we were prepared to receive, and the remote system is telling us that the file is larger than that. Try again later. It would be better if we could know whether there will ever be enough room. */ ulog (LOG_ERROR, "Can't receive %s: too large", qcmd->zfrom); (void) ffileclose (e); (void) remove (qcmd->ztemp); return TRUE; } else { char *zset; zset = (char *) alloca (sizeof "unknown reason: " + strlen (zrec)); sprintf (zset, "unknown reason: %s", zrec); zerr = zset; } ulog (LOG_ERROR, "Can't receive %s: %s", qcmd->zfrom, zerr); (void) ffileclose (e); (void) remove (qcmd->ztemp); (void) fmail_transfer (FALSE, qcmd->zuser, zmail, zerr, qcmd->zfrom, zfromsys, qcmd->zto, zLocalname, (const char *) NULL); (void) fsysdep_did_work (qcmd->pseq); return TRUE; } /* The mode should have been sent as "RY 0%o". If it wasn't, we use 0666. */ imode = (unsigned int) strtol (zrec + 2, (char **) NULL, 8); if (imode == 0) imode = 0666; } else { /* Tell the other system to go ahead and send. */ if (! (qProto->pfsendcmd) ("SY")) { (void) ffileclose (e); (void) remove (qcmd->ztemp); return FALSE; } imode = qcmd->imode; } if (! fstore_recfile (e, qcmd->pseq, qcmd->zfrom, qcmd->zto, zfromsys, qcmd->zuser, imode, zmail, qcmd->ztemp)) return FALSE; cPreceived_bytes = 0; /* Tell the protocol that we are starting to receive a file. */ if (qProto->pffile != NULL) { boolean (*pffile) P((boolean, boolean, boolean *, long)); /* Simplify expression for ancient compilers. */ pffile = qProto->pffile; if (! pffile (TRUE, FALSE, (boolean *) NULL, (long) -1)) return FALSE; } return fploop (); } /* Confirm that a file was received correctly. */ static boolean fprecfile_confirm () { long cbytes; cbytes = cPreceived_bytes; cPreceived_bytes = -1; if (freceived_file (TRUE, cbytes, (const char *) NULL, FALSE)) return (qProto->pfsendcmd) ("CY"); else return (qProto->pfsendcmd) ("CN5"); } /* Send a transfer request. This is only called by the master. It ignored the pseq entry in the scmd structure. */ boolean fxcmd (qcmd, pfnever) const struct scmd *qcmd; boolean *pfnever; { int clen; char *zsend; const char *zrec; *pfnever = FALSE; /* We send the string X from to user options We put a dash in front of options. */ clen = (strlen (qcmd->zfrom) + strlen (qcmd->zto) + strlen (qcmd->zuser) + strlen (qcmd->zoptions) + 7); zsend = (char *) alloca (clen); sprintf (zsend, "X %s %s %s -%s", qcmd->zfrom, qcmd->zto, qcmd->zuser, qcmd->zoptions); if (! (qProto->pfsendcmd) (zsend)) return FALSE; /* Wait for a reply. */ zrec = zgetcmd (); if (zrec == NULL) return FALSE; if (zrec[0] != 'X' || (zrec[1] != 'Y' && zrec[1] != 'N')) { ulog (LOG_ERROR, "Bad response to wildcard request"); return FALSE; } if (zrec[1] == 'N') { ulog (LOG_ERROR, "Work request denied"); *pfnever = TRUE; return TRUE; } return TRUE; } /* Confirm a transfer request. */ boolean fxcmd_confirm () { return (qProto->pfsendcmd) ("XY"); } /* Signal a file transfer failure to the other side. This is only called by the slave. */ boolean ftransfer_fail (bcmd, twhy) int bcmd; enum tfailure twhy; { const char *z; switch (bcmd) { case 'S': switch (twhy) { case FAILURE_PERM: z = "SN2"; break; case FAILURE_OPEN: z = "SN4"; break; case FAILURE_SIZE: z = "SN6"; break; default: z = "SN"; break; } break; case 'R': switch (twhy) { case FAILURE_PERM: case FAILURE_OPEN: z = "RN2"; break; case FAILURE_SIZE: z = "RN6"; break; default: z = "RN"; break; } break; case 'X': z = "XN"; break; default: #if DEBUG > 0 ulog (LOG_ERROR, "ftransfer_fail: Can't happen"); #endif return FALSE; } return (qProto->pfsendcmd) (z); } /* Get and parse a command from the other system. Handle hangups specially. */ boolean fgetcmd (fmaster, qcmd) boolean fmaster; struct scmd *qcmd; { static char *z; static int c; while (TRUE) { const char *zcmd; int clen; zcmd = zgetcmd (); if (zcmd == NULL) return FALSE; clen = strlen (zcmd); if (clen + 1 > c) { c = clen + 1; z = (char *) xrealloc ((pointer) z, c); } strcpy (z, zcmd); if (! fparse_cmd (z, qcmd)) continue; /* Handle hangup commands specially. If it's just 'H', return it. If it's 'N', the other side is denying a hangup request which we can just ignore (since the top level code assumes that hangup requests are denied). If it's 'Y', the other side is confirming a hangup request. In this case we confirm with an "HY", wait for yet another "HY" from the other side, and then finally shut down the protocol (I don't know why it works this way, but it does). We then return a 'Y' command to the top level code. */ if (qcmd->bcmd == 'N') { #if DEBUG > 0 if (fmaster) ulog (LOG_ERROR, "Got hangup reply as master"); #endif continue; } if (qcmd->bcmd == 'Y') { #if DEBUG > 0 if (fmaster) ulog (LOG_ERROR, "Got hangup reply as master"); #endif /* Don't check errors rigorously here, since the other side might jump the gun and hang up. */ if (! (qProto->pfsendcmd) ("HY")) return TRUE; fPerror_ok = TRUE; zcmd = zgetcmd (); fPerror_ok = FALSE; if (zcmd == NULL) return TRUE; #if DEBUG > 1 if (strcmp (zcmd, "HY") != 0) DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO | DEBUG_ABNORMAL, "fgetcmd: Got \"%s\" when expecting \"HY\"", zcmd); #endif (void) (qProto->pfshutdown) (); return TRUE; } return TRUE; } /*NOTREACHED*/ } /* Hangup. */ boolean fhangup_request () { return (qProto->pfsendcmd) ("H"); } /* Reply to a hangup request. This is only called by the slave. If fconfirm is TRUE, we are closing down the protocol. We send an HY message. The master responds with an HY message. We send another HY message, and then shut down the protocol. */ boolean fhangup_reply (fconfirm) boolean fconfirm; { if (! fconfirm) return (qProto->pfsendcmd) ("HN"); else { const char *z; if (! (qProto->pfsendcmd) ("HY")) return FALSE; z = zgetcmd (); if (z == NULL) return FALSE; if (strcmp (z, "HY") != 0) ulog (LOG_ERROR, "Got \"%s\" when expecting \"HY\"", z); else { if (! (qProto->pfsendcmd) ("HY")) return FALSE; } return (qProto->pfshutdown) (); } } /* Loop sending and/or receiving data. If there is a file to send, this will send it until the entire file has been sent or a command has been received from the remote system or a complete file has been received from the remote system. Otherwise this will simply call the protocol to wait until a complete file or command has been received from the remote system. */ static boolean fploop () { boolean fexit; DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO, "fploop: Main protocol loop"); if (ffileisopen (eSendfile)) { int iend; iend = iPrecend; while (TRUE) { /* We keep sending out packets until we have something in the receive buffer. */ while (iend == iPrecend) { char *zdata; int cdata; /* Get a packet and fill it with data. */ zdata = (qProto->pzgetspace) (&cdata); if (zdata == NULL) return FALSE; if (ffileeof (eSendfile)) cdata = 0; else { cdata = cfileread (eSendfile, zdata, cdata); if (ffilereaderror (eSendfile, cdata)) { /* The protocol gives us no way to report a file sending error, so we just drop the connection. What else can we do? */ ulog (LOG_ERROR, "read: %s", strerror (errno)); usendfile_error (); return FALSE; } } if (! (qProto->pfsenddata) (zdata, cdata)) return FALSE; cPsent_bytes += cdata; /* If we have reached the end of the file, tell the protocol that the file is finished (the protocol could also detect this by looking for zero passed as the data length to the send data routine, but would have no convenient way to tell us to redo the file send). If we are not supposed to redo the file transfer, wait for confirmation and return out to get the next file. */ if (cdata == 0) { if (qProto->pffile != NULL) { boolean fredo; boolean (*pffile) P((boolean, boolean, boolean *, long)); /* Simplify expression for ancient compilers. */ pffile = qProto->pffile; if (! pffile (FALSE, TRUE, &fredo, (long) -1)) return FALSE; if (fredo) { ulog (LOG_NORMAL, "Resending file"); if (! ffilerewind (eSendfile)) { ulog (LOG_ERROR, "rewind: %s", strerror (errno)); usendfile_error (); return FALSE; } continue; } } return fpsendfile_confirm (); } } /* Process the data in the receive buffer, and decide whether it's time to get out. */ if (! (qProto->pfprocess) (&fexit)) return FALSE; if (fexit) return TRUE; iend = iPrecend; } } #if DEBUG > 0 /* If there is no file to send, there really should be a file to receive. */ if (! ffileisopen(eRecfile)) ulog (LOG_FATAL, "fploop: No send or receive file"); #endif /* We have no file to send. Wait for data to come in. */ return (qProto->pfwait) (); } /* This function is called by the protocol routines when data has arrived. Some protocols may know whether the data is for a command or a file; for others, if a receive file is open it is for the file and is otherwise for a command. This function will set *pfexit to TRUE if it has received a complete file (assumed to be true if cdata is zero) or a complete command (assumed to be true if the argument data contains a null byte). It will return FALSE on error. */ boolean fgot_data (zdata, cdata, fcmd, ffile, pfexit) const char *zdata; int cdata; boolean fcmd; boolean ffile; boolean *pfexit; { *pfexit = FALSE; if (! fcmd && ! ffile) { if (ffileisopen (eRecfile)) ffile = TRUE; else fcmd = TRUE; } #if DEBUG > 0 if (ffile && ! ffileisopen (eRecfile)) ulog (LOG_FATAL, "fgot_data: No file to receive into"); #endif if (ffile) { if (cdata == 0) { /* The file transfer is complete. If the protocol has a file level routine, call it to see whether we have to receive the file again. */ if (qProto->pffile != NULL) { boolean fredo; boolean (*pffile) P((boolean, boolean, boolean *, long)); /* Simplify expression for ancient compilers. */ pffile = qProto->pffile; if (! pffile (FALSE, FALSE, &fredo, (long) -1)) return FALSE; if (fredo) { ulog (LOG_NORMAL, "File being resent"); if (! frecfile_rewind ()) return FALSE; return TRUE; } } if (! fprecfile_confirm ()) return FALSE; *pfexit = TRUE; return TRUE; } else { int cwrote; /* Cast zdata to avoid warnings because of erroneous prototypes on Ultrix. */ cwrote = cfilewrite (eRecfile, (char *) zdata, cdata); if (cwrote != cdata) { const char *zerr; if (cwrote < 0) zerr = strerror (errno); else zerr = "could not write all data"; ulog (LOG_ERROR, "write: %s", zerr); urecfile_error (); /* Any write error is almost certainly a temporary condition, or else UUCP would not be functioning at all. If we continue to accept the file, we will wind up rejecting it at the end (what else could we do?) and the remote system will throw away the request. We're better off just dropping the connection, which is what happens when we return FALSE, and trying again later. */ return FALSE; } cPreceived_bytes += cdata; return TRUE; } } else { const char *z; /* We want to add this data to the current command string. If there is no null character in the data, this string will be continued by the next packet. Otherwise this must be the last string in the command, and we don't care about what comes after the null byte. */ z = (const char *) memchr ((constpointer) zdata, '\0', cdata); if (z == NULL) upadd_cmd (zdata, cdata, FALSE); else { upadd_cmd (zdata, z - zdata, TRUE); *pfexit = TRUE; } return TRUE; } } /* This function is called by fgot_data when a command string is received. We must queue up received commands since we don't know when we'll be able to get to them (for example, the acknowledgements for the last few packets of a sent file may contain the string indicating whether the file was received correctly). */ struct spcmdqueue { struct spcmdqueue *qnext; int csize; int clen; char *z; }; static struct spcmdqueue *qPcmd_queue; static struct spcmdqueue *qPcmd_free; static void upadd_cmd (z, clen, flast) const char *z; int clen; boolean flast; { struct spcmdqueue *q; q = qPcmd_free; if (q == NULL) { q = (struct spcmdqueue *) xmalloc (sizeof (struct spcmdqueue)); q->qnext = NULL; q->csize = 0; q->clen = 0; q->z = NULL; qPcmd_free = q; } if (q->clen + clen + 1 > q->csize) { q->csize = q->clen + clen + 1; q->z = (char *) xrealloc ((pointer) q->z, q->csize); } memcpy (q->z + q->clen, z, clen); q->clen += clen; q->z[q->clen] = '\0'; /* If the last string in this command, add it to the queue of finished commands. */ if (flast) { struct spcmdqueue **pq; for (pq = &qPcmd_queue; *pq != NULL; pq = &(*pq)->qnext) ; *pq = q; qPcmd_free = q->qnext; q->qnext = NULL; } } /* Get a command string. We just have to wait until the receive packet function gives us something in qPcmd_queue. The return value of this may be treated as a static buffer; it will last at least until the next packet is received. */ const char * zgetcmd () { struct spcmdqueue *q; /* Wait until a command comes in. */ while (qPcmd_queue == NULL) { DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO, "zgetcmd: Waiting for packet"); if (! (qProto->pfwait) ()) return NULL; } q = qPcmd_queue; qPcmd_queue = q->qnext; q->clen = 0; /* We must not replace qPcmd_free, because it may already be receiving a new command string. */ if (qPcmd_free == NULL) { q->qnext = NULL; qPcmd_free = q; } else { q->qnext = qPcmd_free->qnext; qPcmd_free->qnext = q; } DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, "zgetcmd: Got command \"%s\"", q->z); return q->z; } /* We want to output and input at the same time, if supported on this machine. If we have something to send, we send it all while accepting a large amount of data. Once we have sent everything we look at whatever we have received. If data comes in faster than we can send it, we may run out of buffer space. */ boolean fsend_data (zsend, csend, fdoread) const char *zsend; int csend; boolean fdoread; { if (! fdoread) return fport_write (zsend, csend); while (csend > 0) { char *zrec; int crec, csent; if (iPrecend < iPrecstart) { zrec = abPrecbuf + iPrecend; crec = iPrecstart - iPrecend - 1; } else if (iPrecend < CRECBUFLEN) { zrec = abPrecbuf + iPrecend; crec = CRECBUFLEN - iPrecend; } else { zrec = abPrecbuf; crec = iPrecstart - 1; } csent = csend; if (! fport_io (zsend, &csent, zrec, &crec)) return FALSE; csend -= csent; zsend += csent; iPrecend = (iPrecend + crec) % CRECBUFLEN; } return TRUE; } /* Read data from the other system when we have nothing to send. The argument cneed is the amount of data the caller wants, and ctimeout is the timeout in seconds. The function sets *pcrec to the amount of data which was actually received, which may be less than cneed if there isn't enough room in the receive buffer. If no data is received before the timeout expires, *pcrec will be returned as 0. If an error occurs, the function returns FALSE. If the freport argument is FALSE, no error should be reported. */ boolean freceive_data (cneed, pcrec, ctimeout, freport) int cneed; int *pcrec; int ctimeout; boolean freport; { /* Set *pcrec to the maximum amount of data we can read. fport_read expects *pcrec to be the buffer size, and sets it to the amount actually received. */ if (iPrecend < iPrecstart) *pcrec = iPrecstart - iPrecend - 1; else { *pcrec = CRECBUFLEN - iPrecend; if (iPrecstart == 0) --(*pcrec); } #if DEBUG > 0 /* If we have no room in the buffer, we're in trouble. The protocols must be written to ensure that this can't happen. */ if (*pcrec == 0) ulog (LOG_FATAL, "freceive_data: No room in buffer"); #endif /* If we don't have room for all the data the caller wants, we simply have to expect less. We'll get the rest later. */ if (*pcrec < cneed) cneed = *pcrec; if (! fport_read (abPrecbuf + iPrecend, pcrec, cneed, ctimeout, freport)) return FALSE; iPrecend = (iPrecend + *pcrec) % CRECBUFLEN; return TRUE; } /* Read a single character. Get it out of the receive buffer if it's there, otherwise ask freceive_data for at least one character. This is used because as a protocol is shutting down freceive_data may read ahead and eat characters that should be read outside the protocol routines. We call freceive_data rather than fport_read with an argument of 1 so that we can get all the available data in a single system call. The ctimeout argument is the timeout in seconds; the freport argument is FALSE if no error should be reported. This returns a character, or -1 on timeout or -2 on error. */ int breceive_char (ctimeout, freport) int ctimeout; boolean freport; { char b; if (iPrecstart == iPrecend) { int crec; if (! freceive_data (1, &crec, ctimeout, freport)) return -2; if (crec == 0) return -1; } b = abPrecbuf[iPrecstart]; iPrecstart = (iPrecstart + 1) % CRECBUFLEN; return BUCHAR (b); } /* This routine is called when an error occurred and we are crashing out of the connection. It is only used to report statistics on failed transfers to the statistics file. Note that the number of bytes we report as having been sent has little or nothing to do with the number of bytes the remote site actually received. */ void ustats_failed () { long cbytes; if (cPsent_bytes != -1) { cbytes = cPsent_bytes; cPsent_bytes = -1; (void) fsent_file (FALSE, cbytes, "connection failure", FALSE); } if (cPreceived_bytes != -1) { cbytes = cPreceived_bytes; cPreceived_bytes = -1; (void) freceived_file (FALSE, cbytes, "connection failure", FALSE); } }