home *** CD-ROM | disk | FTP | other *** search
- /* trans.c
- Routines to handle file transfers.
-
- Copyright (C) 1992, 1993, 1995 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
- The author of the program may be contacted at ian@airs.com or
- c/o Cygnus Support, 48 Grove Street, Somerville, MA 02144.
- */
-
- #include "uucp.h"
-
- #if USE_RCS_ID
- const char trans_rcsid[] = "$Id: trans.c,v 1.41 1995/08/17 01:25:29 ian Rel $";
- #endif
-
- #include <errno.h>
-
- #include "uudefs.h"
- #include "uuconf.h"
- #include "prot.h"
- #include "system.h"
- #include "trans.h"
-
- /* Local functions. */
-
- static void utqueue P((struct stransfer **, struct stransfer *,
- boolean fhead));
- static void utdequeue P((struct stransfer *));
- static void utchanalc P((struct sdaemon *qdaemon, struct stransfer *qtrans));
- __inline__ static struct stransfer *qtchan P((int ichan));
- __inline__ static void utchanfree P((struct stransfer *qtrans));
- static boolean fttime P((struct sdaemon *qdaemon, long *pisecs,
- long *pimicros));
- static boolean fcheck_queue P((struct sdaemon *qdaemon));
- static boolean ftadd_cmd P((struct sdaemon *qdaemon, const char *z,
- size_t cdata, int iremote, boolean flast));
- static boolean fremote_hangup_reply P((struct stransfer *qtrans,
- struct sdaemon *qdaemon));
- static boolean flocal_poll_file P((struct stransfer *qtrans,
- struct sdaemon *qdaemon));
-
- /* Queue of transfer structures that are ready to start which have
- been requested by the local system. These are only permitted to
- start when the local system is the master. */
- static struct stransfer *qTlocal;
-
- /* Queue of transfer structures that are ready to start which have
- been requested by the remote system. These are responses to
- commands received from the remote system, and should be started as
- soon as possible. */
- static struct stransfer *qTremote;
-
- /* Queue of transfer structures that have been started and want to
- send information. This should be static, but the 'a' protocol
- looks at it, at least for now. */
- struct stransfer *qTsend;
-
- /* Queue of transfer structures that have been started and are waiting
- to receive information. */
- static struct stransfer *qTreceive;
-
- /* Queue of free transfer structures. */
- static struct stransfer *qTavail;
-
- /* Array of transfer structures indexed by local channel number. This
- is maintained for local jobs. */
- static struct stransfer *aqTchan[IMAX_CHAN + 1];
-
- /* Number of local channel numbers currently allocated. */
- static int cTchans;
-
- /* Next channel number to allocate. */
- static int iTchan;
-
- /* Array of transfer structures indexed by remote channel number.
- This is maintained for remote jobs. */
- static struct stransfer *aqTremote[IMAX_CHAN + 1];
-
- /* The transaction we are currently receiving. This is used to avoid
- getting the time too frequently. */
- static struct stransfer *qTtiming_rec;
-
- /* The time from which to charge any received data. This is either
- the last time we charged for received data, or the last time
- something was put on the empty receive queue. */
- static long iTrecsecs;
- static long iTrecmicros;
-
- /* The minimum amount of time, in seconds, to wait between times we
- check the spool directory, if we are busy transferring data. If we
- have nothing to do, we will check the spool directory regardless of
- how long ago the last check was. This should probably be
- configurable. */
- #define CCHECKWAIT (600)
-
- /* The time we last checked the spool directory for work. This is set
- from the return value of ixsysdep_process_time, not ixsysdep_time,
- for convenience in the routines which use it. */
- static long iTchecktime;
-
- /* The size of the command we have read so far in ftadd_cmd. */
- static size_t cTcmdlen;
-
- /* The structure we use when waiting for an acknowledgement of a
- confirmed received file in fsent_receive_ack, and a list of those
- structures. */
-
- struct sreceive_ack
- {
- struct sreceive_ack *qnext;
- char *zto;
- char *ztemp;
- boolean fmarked;
- };
-
- static struct sreceive_ack *qTreceive_ack;
-
- /* Queue up a transfer structure before *pq. This puts it at the head
- or the tail of the list headed by *pq. */
-
- static void
- utqueue (pq, q, fhead)
- struct stransfer **pq;
- struct stransfer *q;
- boolean fhead;
- {
- if (*pq == NULL)
- {
- *pq = q;
- q->qprev = q->qnext = q;
- }
- else
- {
- q->qnext = *pq;
- q->qprev = (*pq)->qprev;
- q->qprev->qnext = q;
- q->qnext->qprev = q;
- if (fhead)
- *pq = q;
- }
- q->pqqueue = pq;
- }
-
- /* Dequeue a transfer structure. */
-
- static void
- utdequeue (q)
- struct stransfer *q;
- {
- if (q->pqqueue != NULL)
- {
- if (*(q->pqqueue) == q)
- {
- if (q->qnext == q)
- *(q->pqqueue) = NULL;
- else
- *(q->pqqueue) = q->qnext;
- }
- q->pqqueue = NULL;
- }
- if (q->qprev != NULL)
- q->qprev->qnext = q->qnext;
- if (q->qnext != NULL)
- q->qnext->qprev = q->qprev;
- q->qprev = NULL;
- q->qnext = NULL;
- }
-
- /* Queue up a transfer structure requested by the local system. */
-
- /*ARGSIGNORED*/
- boolean
- fqueue_local (qdaemon, qtrans)
- struct sdaemon *qdaemon;
- struct stransfer *qtrans;
- {
- utdequeue (qtrans);
- utqueue (&qTlocal, qtrans, FALSE);
- return TRUE;
- }
-
- /* Queue up a transfer structure requested by the remote system. The
- stransfer structure should have the iremote field set. We need to
- record it, so that any subsequent data associated with this
- channel can be routed to the right place. */
-
- boolean
- fqueue_remote (qdaemon, qtrans)
- struct sdaemon *qdaemon;
- struct stransfer *qtrans;
- {
- DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, "fqueue_remote: Channel %d",
- qtrans->iremote);
- if (qtrans->iremote > 0)
- aqTremote[qtrans->iremote] = qtrans;
- utdequeue (qtrans);
- utqueue (&qTremote, qtrans, FALSE);
- return TRUE;
- }
-
- /* Queue up a transfer with something to send. */
-
- boolean
- fqueue_send (qdaemon, qtrans)
- struct sdaemon *qdaemon;
- struct stransfer *qtrans;
- {
- #if DEBUG > 0
- if (qtrans->psendfn == NULL)
- ulog (LOG_FATAL, "fqueue_send: Bad call");
- #endif
- utdequeue (qtrans);
-
- /* Sort the send queue to always send commands before files, and to
- sort jobs by grade. */
- if (qTsend == NULL)
- utqueue (&qTsend, qtrans, FALSE);
- else
- {
- register struct stransfer *q;
- boolean ffirst;
-
- ffirst = TRUE;
- q = qTsend;
- do
- {
- if (! qtrans->fsendfile && q->fsendfile)
- break;
- if ((! qtrans->fsendfile || q->fsendfile)
- && UUCONF_GRADE_CMP (qtrans->s.bgrade, q->s.bgrade) < 0)
- break;
-
- ffirst = FALSE;
- q = q->qnext;
- }
- while (q != qTsend);
-
- qtrans->qnext = q;
- qtrans->qprev = q->qprev;
- q->qprev = qtrans;
- qtrans->qprev->qnext = qtrans;
- if (ffirst)
- qTsend = qtrans;
- qtrans->pqqueue = &qTsend;
- }
-
- return TRUE;
- }
-
- /* Queue up a transfer with something to receive. */
-
- boolean
- fqueue_receive (qdaemon, qtrans)
- struct sdaemon *qdaemon;
- struct stransfer *qtrans;
- {
- #if DEBUG > 0
- if (qtrans->precfn == NULL)
- ulog (LOG_FATAL, "fqueue_receive: Bad call");
- #endif
-
- /* If this is the only item on the receive queue, we do not want to
- charge it for any time during which we have not been waiting for
- anything, so update the receive timestamp. */
- if (qTreceive == NULL)
- iTrecsecs = ixsysdep_process_time (&iTrecmicros);
-
- utdequeue (qtrans);
- utqueue (&qTreceive, qtrans, FALSE);
-
- return TRUE;
- }
-
- /* Get a new local channel number. */
-
- static void
- utchanalc (qdaemon, qtrans)
- struct sdaemon *qdaemon;
- struct stransfer *qtrans;
- {
- do
- {
- ++iTchan;
- if (iTchan > qdaemon->cchans)
- iTchan = 1;
- }
- while (aqTchan[iTchan] != NULL);
-
- qtrans->ilocal = iTchan;
- aqTchan[iTchan] = qtrans;
- ++cTchans;
- }
-
- /* Return the transfer for a channel number. */
-
- __inline__
- static struct stransfer *
- qtchan (ic)
- int ic;
- {
- return aqTchan[ic];
- }
-
- /* Clear the channel number for a transfer. */
-
- __inline__
- static void
- utchanfree (qt)
- struct stransfer *qt;
- {
- if (qt->ilocal != 0)
- {
- aqTchan[qt->ilocal] = NULL;
- qt->ilocal = 0;
- --cTchans;
- }
- }
-
- /* Allocate a new transfer structure. */
-
- struct stransfer *
- qtransalc (qcmd)
- struct scmd *qcmd;
- {
- register struct stransfer *q;
-
- q = qTavail;
- if (q != NULL)
- utdequeue (q);
- else
- q = (struct stransfer *) xmalloc (sizeof (struct stransfer));
- q->qnext = NULL;
- q->qprev = NULL;
- q->pqqueue = NULL;
- q->psendfn = NULL;
- q->precfn = NULL;
- q->pinfo = NULL;
- q->fsendfile = FALSE;
- q->frecfile = FALSE;
- q->e = EFILECLOSED;
- q->ipos = 0;
- q->fcmd = FALSE;
- q->zcmd = NULL;
- q->ccmd = 0;
- q->ilocal = 0;
- q->iremote = 0;
- if (qcmd != NULL)
- {
- q->s = *qcmd;
- q->s.zfrom = zbufcpy (qcmd->zfrom);
- q->s.zto = zbufcpy (qcmd->zto);
- q->s.zuser = zbufcpy (qcmd->zuser);
- q->s.zoptions = zbufcpy (qcmd->zoptions);
- q->s.ztemp = zbufcpy (qcmd->ztemp);
- q->s.znotify = zbufcpy (qcmd->znotify);
- q->s.zcmd = zbufcpy (qcmd->zcmd);
- }
- else
- {
- q->s.zfrom = NULL;
- q->s.zto = NULL;
- q->s.zuser = NULL;
- q->s.zoptions = NULL;
- q->s.ztemp = NULL;
- q->s.znotify = NULL;
- q->s.zcmd = NULL;
- }
- q->zlog = NULL;
- q->isecs = 0;
- q->imicros = 0;
- q->cbytes = 0;
-
- return q;
- }
-
- /* Free a transfer structure. This does not free any pinfo
- information that may have been allocated. */
-
- void
- utransfree (q)
- struct stransfer *q;
- {
- ubuffree (q->zcmd);
- ubuffree ((char *) q->s.zfrom);
- ubuffree ((char *) q->s.zto);
- ubuffree ((char *) q->s.zuser);
- ubuffree ((char *) q->s.zoptions);
- ubuffree ((char *) q->s.ztemp);
- ubuffree ((char *) q->s.znotify);
- ubuffree ((char *) q->s.zcmd);
-
- utchanfree (q);
- if (q->iremote > 0)
- {
- aqTremote[q->iremote] = NULL;
- q->iremote = 0;
- }
-
- #if DEBUG > 0
- q->e = EFILECLOSED;
- q->zcmd = NULL;
- q->s.zfrom = NULL;
- q->s.zto = NULL;
- q->s.zuser = NULL;
- q->s.zoptions = NULL;
- q->s.ztemp = NULL;
- q->s.znotify = NULL;
- q->s.zcmd = NULL;
- q->psendfn = NULL;
- q->precfn = NULL;
- #endif
-
- /* Avoid any possible confusion in the timing code. */
- if (qTtiming_rec == q)
- qTtiming_rec = NULL;
-
- utdequeue (q);
- utqueue (&qTavail, q, FALSE);
- }
-
- /* Get the time. This is a wrapper around ixsysdep_process_time. If
- enough time has elapsed since the last time we got the time, check
- the work queue. */
-
- static boolean
- fttime (qdaemon, pisecs, pimicros)
- struct sdaemon *qdaemon;
- long *pisecs;
- long *pimicros;
- {
- *pisecs = ixsysdep_process_time (pimicros);
- if (*pisecs - iTchecktime >= CCHECKWAIT)
- {
- if (! fcheck_queue (qdaemon))
- return FALSE;
- }
- return TRUE;
- }
-
- /* Gather local commands and queue them up for later processing. Also
- recompute time based control values. */
-
- boolean
- fqueue (qdaemon, pfany)
- struct sdaemon *qdaemon;
- boolean *pfany;
- {
- const struct uuconf_system *qsys;
- long ival;
- int bgrade;
- struct uuconf_timespan *qlocal_size, *qremote_size;
-
- if (pfany != NULL)
- *pfany = FALSE;
-
- qsys = qdaemon->qsys;
-
- /* If we are not the caller, the grade will be set during the
- initial handshake, although this may be overridden by the
- calledtimegrade configuration option. */
- if (! qdaemon->fcaller)
- {
- if (! ftimespan_match (qsys->uuconf_qcalledtimegrade, &ival,
- (int *) NULL))
- bgrade = qdaemon->bgrade;
- else
- bgrade = (char) ival;
- }
- else
- {
- if (! ftimespan_match (qsys->uuconf_qtimegrade, &ival,
- (int *) NULL))
- bgrade = '\0';
- else
- bgrade = (char) ival;
- }
-
- /* Determine the maximum sizes we can send and receive. */
- if (qdaemon->fcaller)
- {
- qlocal_size = qsys->uuconf_qcall_local_size;
- qremote_size = qsys->uuconf_qcall_remote_size;
- }
- else
- {
- qlocal_size = qsys->uuconf_qcalled_local_size;
- qremote_size = qsys->uuconf_qcalled_remote_size;
- }
-
- if (! ftimespan_match (qlocal_size, &qdaemon->clocal_size, (int *) NULL))
- qdaemon->clocal_size = (long) -1;
- if (! ftimespan_match (qremote_size, &qdaemon->cremote_size, (int *) NULL))
- qdaemon->cremote_size = (long) -1;
-
- if (bgrade == '\0')
- return TRUE;
-
- if (! fsysdep_get_work_init (qsys, bgrade))
- return FALSE;
-
- while (TRUE)
- {
- struct scmd s;
-
- if (! fsysdep_get_work (qsys, bgrade, &s))
- return FALSE;
-
- if (s.bcmd == 'H')
- {
- ulog_user ((const char *) NULL);
- break;
- }
-
- if (s.bcmd == 'P')
- {
- struct stransfer *qtrans;
-
- /* A poll file. */
- ulog_user ((const char *) NULL);
- qtrans = qtransalc (&s);
- qtrans->psendfn = flocal_poll_file;
- if (! fqueue_local (qdaemon, qtrans))
- return FALSE;
- continue;
- }
-
- ulog_user (s.zuser);
-
- switch (s.bcmd)
- {
- case 'S':
- case 'E':
- if (! flocal_send_file_init (qdaemon, &s))
- return FALSE;
- break;
- case 'R':
- if (! flocal_rec_file_init (qdaemon, &s))
- return FALSE;
- break;
- case 'X':
- if (! flocal_xcmd_init (qdaemon, &s))
- return FALSE;
- break;
- #if DEBUG > 0
- default:
- ulog (LOG_FATAL, "fqueue: Can't happen");
- break;
- #endif
- }
- }
-
- if (pfany != NULL)
- *pfany = qTlocal != NULL;
-
- iTchecktime = ixsysdep_process_time ((long *) NULL);
-
- return TRUE;
- }
-
- /* Clear everything off the work queue. This is used when the call is
- complete, or if the call is never made. */
-
- void
- uclear_queue (qdaemon)
- struct sdaemon *qdaemon;
- {
- int i;
-
- usysdep_get_work_free (qdaemon->qsys);
-
- qTlocal = NULL;
- qTremote = NULL;
- qTsend = NULL;
- qTreceive = NULL;
- cTchans = 0;
- iTchan = 0;
- qTtiming_rec = NULL;
- cTcmdlen = 0;
- qTreceive_ack = NULL;
- for (i = 0; i < IMAX_CHAN + 1; i++)
- {
- aqTchan[i] = NULL;
- aqTremote[i] = NULL;
- }
- }
-
- /* Recheck the work queue during a conversation. This is only called
- if it's been more than CCHECKWAIT seconds since the last time the
- queue was checked. */
-
- static boolean
- fcheck_queue (qdaemon)
- struct sdaemon *qdaemon;
- {
- /* Only check if we are the master, or if there are multiple
- channels, or if we aren't already trying to get the other side to
- hang up. Otherwise, there's nothing we can do with any new jobs
- we might find. */
- if (qdaemon->fmaster
- || qdaemon->cchans > 1
- || ! qdaemon->frequest_hangup)
- {
- boolean fany;
-
- DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO,
- "fcheck_queue: Rechecking work queue");
- if (! fqueue (qdaemon, &fany))
- return FALSE;
-
- /* If we found something to do, and we're not the master, and we
- don't have multiple channels to send new jobs over, try to
- get the other side to hang up. */
- if (fany && ! qdaemon->fmaster && qdaemon->cchans <= 1)
- qdaemon->frequest_hangup = TRUE;
- }
-
- return TRUE;
- }
-
- /* The main transfer loop. The uucico daemon spends essentially all
- its time in this function. */
-
- boolean
- floop (qdaemon)
- struct sdaemon *qdaemon;
- {
- boolean fret;
-
- fret = TRUE;
-
- while (! qdaemon->fhangup)
- {
- register struct stransfer *q;
-
- #if DEBUG > 1
- /* If we're doing any debugging, close the log and debugging
- files regularly. This will let people copy them off and
- remove them while the conversation is in progresss. */
- if (iDebug != 0)
- {
- ulog_close ();
- ustats_close ();
- }
- #endif
-
- if (qdaemon->fmaster)
- {
- boolean fhangup;
-
- /* We've managed to become the master, so we no longer want
- to request a hangup. */
- qdaemon->frequest_hangup = FALSE;
-
- fhangup = FALSE;
-
- if (qdaemon->fhangup_requested
- && qTsend == NULL)
- {
- /* The remote system has requested that we transfer
- control by sending CYM after receiving a file. */
- DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO,
- "floop: Transferring control at remote request");
- fhangup = TRUE;
- }
- else if (qTremote == NULL
- && qTlocal == NULL
- && qTsend == NULL
- && qTreceive == NULL)
- {
- /* We don't have anything to do. Try to find some new
- jobs. If we can't, transfer control. */
- if (! fqueue (qdaemon, (boolean *) NULL))
- {
- fret = FALSE;
- break;
- }
- if (qTlocal == NULL)
- {
- DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO,
- "floop: No work for master");
- fhangup = TRUE;
- }
- }
-
- if (fhangup)
- {
- if (! (*qdaemon->qproto->pfsendcmd) (qdaemon, "H", 0, 0))
- {
- fret = FALSE;
- break;
- }
- qdaemon->fmaster = FALSE;
- }
- }
-
- /* If we are no long the master, clear any requested hangup. We
- may have already hung up before checking this variable in the
- block above. */
- if (! qdaemon->fmaster)
- qdaemon->fhangup_requested = FALSE;
-
- /* Immediately queue up any remote jobs. We don't need local
- channel numbers for them, since we can disambiguate based on
- the remote channel number. */
- while (qTremote != NULL)
- {
- q = qTremote;
- utdequeue (q);
- utqueue (&qTsend, q, TRUE);
- }
-
- /* If we are the master, or if we have multiple channels, try to
- queue up additional local jobs. */
- if (qdaemon->fmaster || qdaemon->cchans > 1)
- {
- while (qTlocal != NULL && cTchans < qdaemon->cchans)
- {
- /* We have room for an additional channel. */
- q = qTlocal;
- if (! fqueue_send (qdaemon, q))
- {
- fret = FALSE;
- break;
- }
- utchanalc (qdaemon, q);
- }
- if (! fret)
- break;
- }
-
- q = qTsend;
-
- if (q == NULL)
- {
- ulog_user ((const char *) NULL);
- DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO, "floop: Waiting for data");
- if (! (*qdaemon->qproto->pfwait) (qdaemon))
- {
- fret = FALSE;
- break;
- }
- }
- else
- {
- ulog_user (q->s.zuser);
-
- if (! q->fsendfile)
- {
- /* Technically, we should add the time required for this
- call to q->isecs and q->imicros. In practice, the
- amount of time required should be sufficiently small
- that it can be safely disregarded. */
- if (! (*q->psendfn) (q, qdaemon))
- {
- fret = FALSE;
- break;
- }
- }
- else
- {
- long isecs, imicros;
- boolean fcharged;
- long inextsecs = 0, inextmicros;
-
- if (! fttime (qdaemon, &isecs, &imicros))
- {
- fret = FALSE;
- break;
- }
- fcharged = FALSE;
-
- if (q->zlog != NULL)
- {
- ulog (LOG_NORMAL, "%s", q->zlog);
- ubuffree (q->zlog);
- q->zlog = NULL;
- }
-
- /* We can read the file in a tight loop until we have a
- command to send, or the file send has been cancelled,
- or we have a remote job to deal with. We can
- disregard any changes to qTlocal since we already
- have something to send anyhow. */
- while (q == qTsend
- && q->fsendfile
- && qTremote == NULL)
- {
- char *zdata;
- size_t cdata;
- long ipos;
-
- zdata = (*qdaemon->qproto->pzgetspace) (qdaemon, &cdata);
- if (zdata == NULL)
- {
- fret = FALSE;
- break;
- }
-
- if (ffileeof (q->e))
- cdata = 0;
- else
- {
- cdata = cfileread (q->e, zdata, cdata);
- if (ffileioerror (q->e, cdata))
- {
- /* There is no way to report a file reading
- error, so we just drop the connection. */
- ulog (LOG_ERROR, "read: %s", strerror (errno));
- fret = FALSE;
- break;
- }
- }
-
- ipos = q->ipos;
- q->ipos += cdata;
- q->cbytes += cdata;
-
- if (! (*qdaemon->qproto->pfsenddata) (qdaemon, zdata,
- cdata, q->ilocal,
- q->iremote, ipos))
- {
- fret = FALSE;
- break;
- }
-
- if (cdata == 0)
- {
- /* We must update the time now, because this
- call may make an entry in the statistics
- file. */
- inextsecs = ixsysdep_process_time (&inextmicros);
- DEBUG_MESSAGE4 (DEBUG_UUCP_PROTO,
- "floop: Charging %ld to %c %s %s",
- ((inextsecs - isecs) * 1000000
- + inextmicros - imicros),
- q->s.bcmd, q->s.zfrom, q->s.zto);
- q->isecs += inextsecs - isecs;
- q->imicros += inextmicros - imicros;
- fcharged = TRUE;
-
- q->fsendfile = FALSE;
-
- if (! (*q->psendfn) (q, qdaemon))
- fret = FALSE;
-
- break;
- }
- }
-
- if (! fret)
- break;
-
- if (! fcharged)
- {
- inextsecs = ixsysdep_process_time (&inextmicros);
- DEBUG_MESSAGE4 (DEBUG_UUCP_PROTO,
- "floop: Charging %ld to %c %s %s",
- ((inextsecs - isecs) * 1000000
- + inextmicros - imicros),
- q->s.bcmd, q->s.zfrom, q->s.zto);
- q->isecs += inextsecs - isecs;
- q->imicros += inextmicros - imicros;
- }
-
- if (inextsecs - iTchecktime >= CCHECKWAIT)
- {
- if (! fcheck_queue (qdaemon))
- {
- fret = FALSE;
- break;
- }
- }
- }
- }
- }
-
- ulog_user ((const char *) NULL);
-
- (void) (*qdaemon->qproto->pfshutdown) (qdaemon);
-
- if (fret)
- uwindow_acked (qdaemon, TRUE);
- else
- ufailed (qdaemon);
-
- return fret;
- }
-
- /* This is called by the protocol routines when they have received
- some data. If pfexit is not NULL, *pfexit should be set to TRUE if
- the protocol receive loop should exit back to the main floop
- routine, above. It is only important to set *pfexit to TRUE if the
- main loop called the pfwait entry point, so we need never set it to
- TRUE if we just receive data for a file. This routine never sets
- *pfexit to FALSE. */
-
- boolean
- fgot_data (qdaemon, zfirst, cfirst, zsecond, csecond, ilocal, iremote, ipos,
- fallacked, pfexit)
- struct sdaemon *qdaemon;
- const char *zfirst;
- size_t cfirst;
- const char *zsecond;
- size_t csecond;
- int ilocal;
- int iremote;
- long ipos;
- boolean fallacked;
- boolean *pfexit;
- {
- struct stransfer *q;
- int cwrote;
- boolean fret;
- long isecs, imicros;
-
- if (fallacked && qTreceive_ack != NULL)
- uwindow_acked (qdaemon, TRUE);
-
- /* Now we have to decide which transfer structure gets the data. If
- ilocal is -1, it means that the protocol does not know where to
- route the data. In that case we route it to the first transfer
- that is waiting for data, or, if none, as a new command. If
- ilocal is 0, we either select based on the remote channel number
- or we have a new command. */
- if (ilocal == -1 && qTreceive != NULL)
- q = qTreceive;
- else if (ilocal == 0 && iremote > 0 && aqTremote[iremote] != NULL)
- q = aqTremote[iremote];
- else if (ilocal <= 0)
- {
- const char *znull;
-
- ulog_user ((const char *) NULL);
-
- /* This data is part of a command. 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. */
- znull = (const char *) memchr (zfirst, '\0', cfirst);
- if (znull != NULL)
- fret = ftadd_cmd (qdaemon, zfirst, (size_t) (znull - zfirst),
- iremote, TRUE);
- else
- {
- fret = ftadd_cmd (qdaemon, zfirst, cfirst, iremote, FALSE);
- if (fret && csecond > 0)
- {
- znull = (const char *) memchr (zsecond, '\0', csecond);
- if (znull != NULL)
- fret = ftadd_cmd (qdaemon, zsecond,
- (size_t) (znull - zsecond), iremote, TRUE);
- else
- fret = ftadd_cmd (qdaemon, zsecond, csecond, iremote, FALSE);
- }
- }
-
- if (pfexit != NULL && (qdaemon->fhangup || qTremote != NULL))
- *pfexit = TRUE;
-
- /* Time spent waiting for a new command is not charged to
- anybody. */
- if (! fttime (qdaemon, &iTrecsecs, &iTrecmicros))
- fret = FALSE;
-
- return fret;
- }
- else
- {
- /* Get the transfer structure this data is intended for. */
- q = qtchan (ilocal);
- }
-
- #if DEBUG > 0
- if (q == NULL || q->precfn == NULL)
- {
- ulog (LOG_ERROR, "Protocol error: %lu bytes remote %d local %d",
- (unsigned long) (cfirst + csecond),
- iremote, ilocal);
- return FALSE;
- }
- #endif
-
- ulog_user (q->s.zuser);
-
- fret = TRUE;
-
- if (q->zlog != NULL && ! q->fsendfile)
- {
- ulog (LOG_NORMAL, "%s", q->zlog);
- ubuffree (q->zlog);
- q->zlog = NULL;
- }
-
- if (cfirst == 0 || q->fcmd || ! q->frecfile || q != qTtiming_rec)
- {
- struct stransfer *qcharge;
-
- /* Either we are receiving some sort of command, or we are
- receiving data for a transfer other than the one we are
- currently timing. It we are currently timing a transfer,
- charge any accumulated time to it. Otherwise, if we
- currently have something to send, just forget about the
- accumulated time (when using a bidirectional protocol, it's
- very difficult to charge this time correctly). Otherwise,
- charge it to whatever transfer receives it. */
- if (! fttime (qdaemon, &isecs, &imicros))
- fret = FALSE;
- if (qTtiming_rec != NULL)
- qcharge = qTtiming_rec;
- else if (qTsend != NULL)
- qcharge = NULL;
- else
- qcharge = q;
- if (qcharge != NULL)
- {
- DEBUG_MESSAGE4 (DEBUG_UUCP_PROTO,
- "fgot_data: Charging %ld to %c %s %s",
- ((isecs - iTrecsecs) * 1000000
- + imicros - iTrecmicros),
- qcharge->s.bcmd, qcharge->s.zfrom,
- qcharge->s.zto);
- qcharge->isecs += isecs - iTrecsecs;
- qcharge->imicros += imicros - iTrecmicros;
- }
- iTrecsecs = isecs;
- iTrecmicros = imicros;
-
- /* If we received file data, start timing the new transfer. */
- if (cfirst == 0 || q->fcmd || ! q->frecfile)
- qTtiming_rec = NULL;
- else
- qTtiming_rec = q;
- }
-
- /* If we're receiving a command, then accumulate it up to the null
- byte. */
- if (q->fcmd)
- {
- const char *znull;
-
- znull = NULL;
- while (cfirst > 0)
- {
- size_t cnew;
- char *znew;
-
- znull = (const char *) memchr (zfirst, '\0', cfirst);
- if (znull != NULL)
- cnew = znull - zfirst;
- else
- cnew = cfirst;
- znew = zbufalc (q->ccmd + cnew + 1);
- if (q->ccmd > 0)
- memcpy (znew, q->zcmd, q->ccmd);
- memcpy (znew + q->ccmd, zfirst, cnew);
- znew[q->ccmd + cnew] = '\0';
- ubuffree (q->zcmd);
- q->zcmd = znew;
- q->ccmd += cnew;
-
- if (znull != NULL)
- break;
-
- zfirst = zsecond;
- cfirst = csecond;
- csecond = 0;
- }
-
- if (znull != NULL)
- {
- char *zcmd;
- size_t ccmd;
-
- zcmd = q->zcmd;
- ccmd = q->ccmd;
- q->fcmd = FALSE;
- q->zcmd = NULL;
- q->ccmd = 0;
- if (! (*q->precfn) (q, qdaemon, zcmd, ccmd + 1))
- fret = FALSE;
- ubuffree (zcmd);
- }
-
- if (pfexit != NULL
- && (qdaemon->fhangup
- || qdaemon->fmaster
- || qTsend != NULL))
- *pfexit = TRUE;
- }
- else if (! q->frecfile || cfirst == 0)
- {
- /* We're either not receiving a file or the file transfer is
- complete. */
- q->frecfile = FALSE;
- if (! (*q->precfn) (q, qdaemon, zfirst, cfirst))
- fret = FALSE;
- if (fret && csecond > 0)
- return fgot_data (qdaemon, zsecond, csecond,
- (const char *) NULL, (size_t) 0,
- ilocal, iremote, ipos + (long) cfirst,
- FALSE, pfexit);
- if (pfexit != NULL
- && (qdaemon->fhangup
- || qdaemon->fmaster
- || qTsend != NULL))
- *pfexit = TRUE;
- }
- else
- {
- if (ipos != -1 && ipos != q->ipos)
- {
- DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO,
- "fgot_data: Seeking to %ld", ipos);
- if (! ffileseek (q->e, ipos))
- {
- ulog (LOG_ERROR, "seek: %s", strerror (errno));
- fret = FALSE;
- }
- q->ipos = ipos;
- }
-
- if (fret)
- {
- while (cfirst > 0)
- {
- cwrote = cfilewrite (q->e, (char *) zfirst, cfirst);
- if (cwrote == cfirst)
- {
- #if FREE_SPACE_DELTA > 0
- long cfree_space;
-
- /* Check that there is still enough space on the
- disk. If there isn't, we drop the connection,
- because we have no way to abort a file transfer
- in progress. */
- cfree_space = qdaemon->qsys->uuconf_cfree_space;
- if (cfree_space > 0
- && ((q->cbytes / FREE_SPACE_DELTA)
- != (q->cbytes + cfirst) / FREE_SPACE_DELTA)
- && ! frec_check_free (q, cfree_space))
- {
- fret = FALSE;
- break;
- }
- #endif
- q->cbytes += cfirst;
- q->ipos += cfirst;
- }
- else
- {
- if (ffileioerror (q->e, cwrote))
- ulog (LOG_ERROR, "write: %s", strerror (errno));
- else
- ulog (LOG_ERROR,
- "Wrote %d to file when trying to write %lu",
- cwrote, (unsigned long) cfirst);
-
- /* 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. */
- fret = FALSE;
- break;
- }
-
- zfirst = zsecond;
- cfirst = csecond;
- csecond = 0;
- }
- }
-
- if (pfexit != NULL && qdaemon->fhangup)
- *pfexit = TRUE;
- }
-
- return fret;
- }
-
- /* Accumulate a string into a command. If the command is complete,
- start up a new transfer. */
-
- static boolean
- ftadd_cmd (qdaemon, z, clen, iremote, flast)
- struct sdaemon *qdaemon;
- const char *z;
- size_t clen;
- int iremote;
- boolean flast;
- {
- static char *zbuf;
- static size_t cbuf;
- size_t cneed;
- struct scmd s;
-
- cneed = cTcmdlen + clen + 1;
- if (cneed > cbuf)
- {
- zbuf = (char *) xrealloc ((pointer) zbuf, cneed);
- cbuf = cneed;
- }
-
- memcpy (zbuf + cTcmdlen, z, clen);
- zbuf[cTcmdlen + clen] = '\0';
-
- if (! flast)
- {
- cTcmdlen += clen;
- return TRUE;
- }
-
- /* Don't save this string for next time. */
- cTcmdlen = 0;
-
- DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO,
- "ftadd_cmd: Got command \"%s\"", zbuf);
-
- if (! fparse_cmd (zbuf, &s)
- || s.bcmd == 'P')
- {
- ulog (LOG_ERROR, "Received garbled command \"%s\"", zbuf);
- return TRUE;
- }
-
- /* Some systems seem to sometimes send garbage at the end of the
- command. Avoid interpreting it as a size if sizes are not
- supported. */
- if ((qdaemon->ifeatures & FEATURE_SIZES) == 0)
- s.cbytes = -1;
-
- if (s.bcmd != 'H' && s.bcmd != 'Y' && s.bcmd != 'N')
- ulog_user (s.zuser);
- else
- ulog_user ((const char *) NULL);
-
- switch (s.bcmd)
- {
- case 'S':
- case 'E':
- return fremote_send_file_init (qdaemon, &s, iremote);
- case 'R':
- return fremote_rec_file_init (qdaemon, &s, iremote);
- case 'X':
- return fremote_xcmd_init (qdaemon, &s, iremote);
- case 'H':
- /* This is a remote request for a hangup. We close the log
- files so that they may be moved at this point. */
- ulog_close ();
- ustats_close ();
- {
- struct stransfer *q;
-
- q = qtransalc ((struct scmd *) NULL);
- q->psendfn = fremote_hangup_reply;
- q->iremote = iremote;
- q->s.bcmd = 'H';
- return fqueue_remote (qdaemon, q);
- }
- case 'N':
- /* This means a hangup request is being denied; we just ignore
- this and wait for further commands. */
- return TRUE;
- case 'Y':
- /* This is a remote confirmation of a hangup. We reconfirm. */
- if (qdaemon->fhangup)
- return TRUE;
- #if DEBUG > 0
- if (qdaemon->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. The fLog_sighup variable
- will get set TRUE again when the port is closed. */
- fLog_sighup = FALSE;
- (void) (*qdaemon->qproto->pfsendcmd) (qdaemon, "HY", 0, iremote);
- qdaemon->fhangup = TRUE;
- return TRUE;
- #if DEBUG > 0
- default:
- ulog (LOG_FATAL, "ftadd_cmd: Can't happen");
- return FALSE;
- #endif
- }
- }
-
- /* The remote system is requesting a hang up. If we have something to
- do, send an HN. Otherwise send two HY commands (the other side is
- presumed to send an HY command between the first and second, but we
- don't bother to wait for it) and hang up. */
-
- static boolean
- fremote_hangup_reply (qtrans, qdaemon)
- struct stransfer *qtrans;
- struct sdaemon *qdaemon;
- {
- boolean fret;
-
- utransfree (qtrans);
-
- if (qTremote == NULL
- && qTlocal == NULL
- && qTsend == NULL
- && qTreceive == NULL)
- {
- if (! fqueue (qdaemon, (boolean *) NULL))
- return FALSE;
-
- if (qTlocal == NULL)
- {
- DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO, "fremote_hangup_reply: No work");
- fret = ((*qdaemon->qproto->pfsendcmd) (qdaemon, "HY", 0, 0)
- && (*qdaemon->qproto->pfsendcmd) (qdaemon, "HY", 0, 0));
- qdaemon->fhangup = TRUE;
- return fret;
- }
- }
-
- DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO, "fremote_hangup_reply: Found work");
- fret = (*qdaemon->qproto->pfsendcmd) (qdaemon, "HN", 0, 0);
- qdaemon->fmaster = TRUE;
- return fret;
- }
-
- /* As described in system.h, we need to keep track of which files have
- been successfully received for which we do not know that the other
- system has received our acknowledgement. This routine is called to
- keep a list of such files. */
-
- static struct sreceive_ack *qTfree_receive_ack;
-
- void
- usent_receive_ack (qdaemon, qtrans)
- struct sdaemon *qdaemon;
- struct stransfer *qtrans;
- {
- struct sreceive_ack *q;
-
- if (qTfree_receive_ack == NULL)
- q = (struct sreceive_ack *) xmalloc (sizeof (struct sreceive_ack));
- else
- {
- q = qTfree_receive_ack;
- qTfree_receive_ack = q->qnext;
- }
-
- q->qnext = qTreceive_ack;
- q->zto = zbufcpy (qtrans->s.zto);
- q->ztemp = zbufcpy (qtrans->s.ztemp);
- q->fmarked = FALSE;
-
- qTreceive_ack = q;
- }
-
- /* This routine is called by the protocol code when either all
- outstanding data has been acknowledged or one complete window has
- passed. It may be called directly by the protocol, or it may be
- called via fgot_data. If one complete window has passed, then all
- unmarked receives are marked, and we know that all marked ones have
- been acked. */
-
- void
- uwindow_acked (qdaemon, fallacked)
- struct sdaemon *qdaemon;
- boolean fallacked;
- {
- register struct sreceive_ack **pq;
-
- pq = &qTreceive_ack;
- while (*pq != NULL)
- {
- if (fallacked || (*pq)->fmarked)
- {
- struct sreceive_ack *q;
-
- q = *pq;
- (void) fsysdep_forget_reception (qdaemon->qsys, q->zto,
- q->ztemp);
- ubuffree (q->zto);
- ubuffree (q->ztemp);
- *pq = q->qnext;
- q->qnext = qTfree_receive_ack;
- qTfree_receive_ack = q;
- }
- else
- {
- (*pq)->fmarked = TRUE;
- pq = &(*pq)->qnext;
- }
- }
- }
-
- /* This routine is called when an error occurred and we are crashing
- out of the connection. It is used to report statistics on failed
- transfers to the statistics file, and it also discards useless
- temporary files for file receptions. 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
- ufailed (qdaemon)
- struct sdaemon *qdaemon;
- {
- register struct stransfer *q;
-
- if (qTsend != NULL)
- {
- q = qTsend;
- do
- {
- if ((q->fsendfile || q->frecfile)
- && q->cbytes > 0)
- {
- ustats (FALSE, q->s.zuser, qdaemon->qsys->uuconf_zname,
- q->fsendfile, q->cbytes, q->isecs, q->imicros,
- qdaemon->fcaller);
- if (q->fsendfile)
- qdaemon->csent += q->cbytes;
- else
- qdaemon->creceived += q->cbytes;
- }
- if (q->frecfile)
- (void) frec_discard_temp (qdaemon, q);
- q = q->qnext;
- }
- while (q != qTsend);
- }
-
- if (qTreceive != NULL)
- {
- q = qTreceive;
- do
- {
- if ((q->fsendfile || q->frecfile)
- && q->cbytes > 0)
- {
- ustats (FALSE, q->s.zuser, qdaemon->qsys->uuconf_zname,
- q->fsendfile, q->cbytes, q->isecs, q->imicros,
- qdaemon->fcaller);
- if (q->fsendfile)
- qdaemon->csent += q->cbytes;
- else
- qdaemon->creceived += q->cbytes;
- }
- if (q->frecfile)
- (void) frec_discard_temp (qdaemon, q);
- q = q->qnext;
- }
- while (q != qTreceive);
- }
- }
-
- /* When a local poll file is found, it is entered on the queue like
- any other job. When it is pulled off the queue, this function is
- called. It just calls fsysdep_did_work, which will remove the poll
- file. This ensures that poll files are only removed if the system
- is actually called. */
-
- /*ARGSUSED*/
- static boolean
- flocal_poll_file (qtrans, qdaemon)
- struct stransfer *qtrans;
- struct sdaemon *qdaemon;
- {
- boolean fret;
-
- fret = fsysdep_did_work (qtrans->s.pseq);
- utransfree (qtrans);
- return fret;
- }
-