Piperate
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(¤tTime, NULL);
timespec_subtract(&toSleep, ¤tTime, &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 */
}
}