/* io_util.c * * Copyright (c) 1996-2005 Mike Gleason, NcFTP Software. * All rights reserved. * */ #include "syshdrs.h" #ifdef PRAGMA_HDRSTOP # pragma hdrstop #endif #ifndef NO_SIGNALS # define NO_SIGNALS 1 #endif #ifndef O_BINARY /* Needed for platforms using different EOLN sequence (i.e. DOS) */ # ifdef _O_BINARY # define O_BINARY _O_BINARY # else # define O_BINARY 0 # endif #endif double FTPDuration(struct timeval *t0) { struct timeval t1; double sec; (void) gettimeofday(&t1, NULL); if (t0->tv_usec > t1.tv_usec) { t1.tv_usec += 1000000; t1.tv_sec--; } sec = ((double) (t1.tv_usec - t0->tv_usec) * 0.000001) + (t1.tv_sec - t0->tv_sec); return (sec); } /* FTPDuration */ void FTPInitIOTimer(const FTPCIPtr cip) { cip->bytesTransferred = (longest_int) 0; cip->expectedSize = kSizeUnknown; cip->mdtm = kModTimeUnknown; cip->rname = NULL; cip->lname = NULL; cip->kBytesPerSec = -1.0; cip->percentCompleted = -1.0; cip->sec = -1.0; cip->secLeft = -1.0; cip->nextProgressUpdate = 0; cip->stalled = 0; cip->dataTimedOut = 0; cip->useProgressMeter = 1; (void) gettimeofday(&cip->t0, NULL); } /* FTPInitIOTimer */ void FTPStartIOTimer(const FTPCIPtr cip) { (void) gettimeofday(&cip->t0, NULL); if (cip->progress != (FTPProgressMeterProc) 0) (*cip->progress)(cip, kPrInitMsg); } /* FTPStartIOTimer */ void FTPUpdateIOTimer(const FTPCIPtr cip) { double sec; struct timeval *t0, t1; time_t now; (void) time(&now); if ((now < cip->nextProgressUpdate) && (cip->canceling == 0)) return; now += 1; cip->nextProgressUpdate = now; (void) gettimeofday(&t1, NULL); t0 = &cip->t0; if (t0->tv_usec > t1.tv_usec) { t1.tv_usec += 1000000; t1.tv_sec--; } sec = ((double) (t1.tv_usec - t0->tv_usec) * 0.000001) + (t1.tv_sec - t0->tv_sec); if (sec > 0.0) { cip->kBytesPerSec = ((double) cip->bytesTransferred) / (1024.0 * sec); } else { cip->kBytesPerSec = -1.0; } if (cip->expectedSize == kSizeUnknown) { cip->percentCompleted = -1.0; cip->secLeft = -1.0; } else if (cip->expectedSize <= 0) { cip->percentCompleted = 100.0; cip->secLeft = 0.0; } else { cip->percentCompleted = ((double) (100.0 * (cip->bytesTransferred + cip->startPoint))) / ((double) cip->expectedSize); if (cip->percentCompleted >= 100.0) { cip->percentCompleted = 100.0; cip->secLeft = 0.0; } else if (cip->percentCompleted <= 0.0) { cip->secLeft = 999.0; } if (cip->kBytesPerSec > 0.0) { cip->secLeft = ((cip->expectedSize - cip->bytesTransferred - cip->startPoint) / 1024.0) / cip->kBytesPerSec; if (cip->secLeft < 0.0) cip->secLeft = 0.0; } } cip->sec = sec; if ((cip->progress != (FTPProgressMeterProc) 0) && (cip->useProgressMeter != 0)) (*cip->progress)(cip, kPrUpdateMsg); } /* FTPUpdateIOTimer */ void FTPStopIOTimer(const FTPCIPtr cip) { cip->nextProgressUpdate = 0; /* force last update */ FTPUpdateIOTimer(cip); if (cip->progress != (FTPProgressMeterProc) 0) (*cip->progress)(cip, kPrEndMsg); } /* FTPStopIOTimer */ /* The purpose of this is to provide updates for the progress meters * during lags. Return zero if the operation timed-out. */ int WaitForRemoteInput(const FTPCIPtr cip) { fd_set ss, ss2; struct timeval tv; int result; int fd; int wsecs; int xferTimeout; int ocancelXfer; xferTimeout = cip->xferTimeout; if (xferTimeout < 1) return (1); fd = cip->dataSocket; if (fd < 0) return (1); if (cip->dataTimedOut > 0) { cip->dataTimedOut++; return (0); /* already timed-out */ } ocancelXfer = cip->cancelXfer; wsecs = 0; cip->stalled = 0; while ((xferTimeout <= 0) || (wsecs < xferTimeout)) { if ((cip->cancelXfer != 0) && (ocancelXfer == 0)) { /* leave cip->stalled -- could have been stalled and then canceled. */ return (1); } MY_FD_ZERO(&ss); #if defined(__DECC) || defined(__DECCXX) #pragma message save #pragma message disable trunclongint #endif MY_FD_SET(fd, &ss); #if defined(__DECC) || defined(__DECCXX) #pragma message restore #endif ss2 = ss; tv.tv_sec = 1; tv.tv_usec = 0; result = select(fd + 1, SELECT_TYPE_ARG234 &ss, NULL, SELECT_TYPE_ARG234 &ss2, &tv); if (result == 1) { /* ready */ cip->stalled = 0; return (1); } else if (result < 0) { if (errno != EINTR) { cip->stalled = 0; return (1); /* Ready to read error */ } } else { wsecs++; cip->stalled = wsecs; } FTPUpdateIOTimer(cip); } #if !defined(NO_SIGNALS) /* Shouldn't get here -- alarm() should have * went off by now. */ (void) kill(getpid(), SIGALRM); #endif /* NO_SIGNALS */ cip->dataTimedOut++; return (0); /* timed-out */ } /* WaitForRemoteInput */ /* The purpose of this is to provide updates for the progress meters * during lags. Return zero if the operation timed-out. */ int WaitForRemoteOutput(const FTPCIPtr cip) { fd_set ss, ss2; struct timeval tv; int result; int fd; int wsecs; int xferTimeout; int ocancelXfer; xferTimeout = cip->xferTimeout; if (xferTimeout < 1) return (1); fd = cip->dataSocket; if (fd < 0) return (1); if (cip->dataTimedOut > 0) { cip->dataTimedOut++; return (0); /* already timed-out */ } ocancelXfer = cip->cancelXfer; wsecs = 0; cip->stalled = 0; while ((xferTimeout <= 0) || (wsecs < xferTimeout)) { if ((cip->cancelXfer != 0) && (ocancelXfer == 0)) { /* leave cip->stalled -- could have been stalled and then canceled. */ return (1); } MY_FD_ZERO(&ss); #if defined(__DECC) || defined(__DECCXX) #pragma message save #pragma message disable trunclongint #endif MY_FD_SET(fd, &ss); #if defined(__DECC) || defined(__DECCXX) #pragma message restore #endif ss2 = ss; tv.tv_sec = 1; tv.tv_usec = 0; result = select(fd + 1, NULL, SELECT_TYPE_ARG234 &ss, SELECT_TYPE_ARG234 &ss2, &tv); if (result == 1) { /* ready */ cip->stalled = 0; return (1); } else if (result < 0) { if (errno != EINTR) { cip->stalled = 0; return (1); /* Ready to read error */ } } else { wsecs++; cip->stalled = wsecs; } FTPUpdateIOTimer(cip); } #if !defined(NO_SIGNALS) /* Shouldn't get here -- alarm() should have * went off by now. */ (void) kill(getpid(), SIGALRM); #endif /* NO_SIGNALS */ cip->dataTimedOut++; return (0); /* timed-out */ } /* WaitForRemoteOutput */ void AutomaticallyUseASCIIModeDependingOnExtension(const FTPCIPtr cip, const char *const pathName, int *const xtype) { if ((*xtype == kTypeBinary) && (cip->asciiFilenameExtensions != NULL)) { if (FilenameExtensionIndicatesASCII(pathName, cip->asciiFilenameExtensions)) { /* Matched -- send this file in ASCII mode * instead of binary since it's extension * appears to be that of a text file. */ *xtype = kTypeAscii; } } } /* AutomaticallyUseASCIIModeDependingOnExtension */ void FTPCheckForRestartModeAvailability(const FTPCIPtr cip) { if (cip->hasREST == kCommandAvailabilityUnknown) { (void) FTPSetTransferType(cip, kTypeBinary); if (FTPSetStartOffset(cip, (longest_int) 1) == kNoErr) { /* Now revert -- we still may not end up * doing it. */ FTPSetStartOffset(cip, (longest_int) -1); } } } /* FTPCheckForRestartModeAvailability */ void FTPSetUploadSocketBufferSize(const FTPCIPtr cip) { /* If dataSocketSBufSize is non-zero, it means you * want to explicitly try to set the size of the * socket's I/O buffer. * * If it is zero, it means you want to just use the * TCP stack's default value, which is typically * between 8 and 64 kB. * * If you try to set the buffer larger than 64 kB, * the TCP stack should try to use RFC 1323 to * negotiate "TCP Large Windows" which may yield * significant performance gains. */ if ((cip->numUploads == 0) && (cip->dataSocketSBufSize != 0)) { if (cip->hasSITE_STORBUFSIZE == kCommandAvailable) (void) FTPCmd(cip, "SITE STORBUFSIZE %lu", (unsigned long) cip->dataSocketSBufSize); else if (cip->hasSITE_SBUFSIZ == kCommandAvailable) (void) FTPCmd(cip, "SITE SBUFSIZ %lu", (unsigned long) cip->dataSocketSBufSize); else if (cip->hasSITE_SBUFSZ == kCommandAvailable) (void) FTPCmd(cip, "SITE SBUFSZ %lu", (unsigned long) cip->dataSocketSBufSize); /* At least one server implemenation has RBUFSZ but not * SBUFSZ and instead uses RBUFSZ for both. */ else if ((cip->hasSITE_SBUFSZ != kCommandAvailable) && (cip->hasSITE_RBUFSZ == kCommandAvailable)) (void) FTPCmd(cip, "SITE RBUFSZ %lu", (unsigned long) cip->dataSocketSBufSize); else if (cip->hasSITE_BUFSIZE == kCommandAvailable) (void) FTPCmd(cip, "SITE BUFSIZE %lu", (unsigned long) cip->dataSocketSBufSize); } } /* FTPSetUploadSocketBufferSize */