Piperate

From Wikitech

Piperate is a handy tool written by Michael Lyle to limit the bandwidth of a unix pipe. Adapted to use nanosleep() instead of sleep() by Tim Starling.

Download: http://wikimedia.org/~tstarling/piperate.c

Or just copy and paste:

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>

#include <time.h>
#include <errno.h>
#include <unistd.h>

/* piperate - v0.3, by Michael Lyle <mlyle@lyle.org> and Tim Starling.
** Last updated February 2006.
** This software is in the public domain.
**
** usage:  piperate [-b blocksize] [-d delay] [-a] [-p]
**
** reads from stdin and writes to stdout;  aggregates writes to
** size blocksize; ensures at least delay seconds between writes.
** Delay may be a floating point number, e.g. 2e-3 for 2ms.
**
** if -a is set, it always sleeps delay seconds before a write,
** even if more than delay seconds elapsed since the prev write
**
** if -p is set, it will print out a '.' after each block read to
** standard error for progress notification.
**
** To build: gcc -o piperate piperate.c
*/

int readFullBuf(int fd, char *buf, size_t nBytes) {
  int numRead=0;

  while (numRead < nBytes) {
    int curRead;

    curRead=read(fd, buf+numRead, nBytes-numRead);

    if (curRead == -1) {
      switch (errno) {
	case EINTR:
	case EAGAIN:
	  break;
      }

      perror("read");
      return numRead;
    }

    if (curRead == 0) {
      /* eof */
      return numRead;
    }

    numRead += curRead;
  }

  return numRead;	/* success */
}

/* Based on the function given in the glibc manual */
int timespec_subtract (struct timespec *result, struct timespec *x, 
    struct timespec *y)
{
  /* Perform the carry for the later subtraction by updating y. */
  if (x->tv_nsec < y->tv_nsec) {
    int nsec = (y->tv_nsec - x->tv_nsec) / 1000000000 + 1;
    y->tv_nsec -= 1000000000 * nsec;
    y->tv_sec += nsec;
  }
  if (x->tv_nsec - y->tv_nsec > 1000000000) {
    int nsec = (x->tv_nsec - y->tv_nsec) / 1000000000;
    y->tv_nsec += 1000000000 * nsec;
    y->tv_sec -= nsec;
  }

  /* Compute the time remaining to wait.
     tv_nsec is certainly positive. */
  result->tv_sec = x->tv_sec - y->tv_sec;
  result->tv_nsec = x->tv_nsec - y->tv_nsec;

  /* Return 1 if result is negative. */
  return x->tv_sec < y->tv_sec;
}

int main(int argc, char *argv[]) {
  int bufSize=1048576;
  double delay=5;
  int alwaysSleepFull=0;
  struct timespec lastWriteTime, toSleep, tsDelay, currentTime, remaining;
  int progress=0;

  char *buf;

  int ch;

  gettimeofday(&lastWriteTime, NULL);

  while ((ch = getopt(argc, argv, "b:d:ap")) != -1) {
    switch(ch) {
      case 'a':
        alwaysSleepFull=1;
	break;
      case 'b':
        bufSize=atoi(optarg);
	break;
      case 'd':
	delay=atof(optarg);
	break;
      case 'p':
	progress=1;
	break;
      default:
	fprintf(stderr, "usage: %s [-b blocksize] [-d delay] [-a] [-p]\n", 
		argv[0]);
	exit(1);
    }
  }
  tsDelay.tv_sec = (int)delay;
  tsDelay.tv_nsec = (int)((delay - (int)delay) * 1e9); 

  buf=malloc(bufSize);

  if (!buf) {
    perror("malloc");
    return 1;			/* unable to allocate the buffer */
  }

  while (1) {
    int numRead;

    numRead=readFullBuf(0, buf, bufSize);

    if (progress) {
      fprintf(stderr, ".");
    }

    if (alwaysSleepFull) {
      nanosleep(&tsDelay, &remaining);
    } else {
      gettimeofday(&currentTime, NULL);
      timespec_subtract(&toSleep, &currentTime, &lastWriteTime);
      timespec_subtract(&toSleep, &tsDelay, &toSleep);

      /* fprintf(stderr, "delaying %d.%09d seconds...\n", toSleep.tv_sec, toSleep.tv_nsec); */

      if (toSleep.tv_sec > 0 || (toSleep.tv_sec == 0 && toSleep.tv_nsec > 0)) {
	nanosleep(&toSleep, NULL);
      }
    }

    gettimeofday(&lastWriteTime, NULL);

    write(1, buf, numRead);

    if (numRead < bufSize)
      break;			/* we're done here */
  }
}