iothrottle: More functions intercepted.

This commit is contained in:
Ole Tange 2024-12-30 22:15:17 +01:00
parent 1d41f696cc
commit 715304f395
14 changed files with 564 additions and 30 deletions

View file

@ -281,7 +281,7 @@ To solve this sort the input with B<LC_ALL=C sort ...>.
=head1 REPORTING BUGS
B<2search> and B<2grep> are part of tangetools. Report bugs on
https://gitlab.com/ole.tange/tangetools/-/issues
https://git.data.coop/tange/tangetools/issues
=head1 AUTHOR
@ -870,7 +870,7 @@ sub die_bug {
my $bugid = shift;
print STDERR
("$Global::progname: This should not happen. You have found a bug.\n",
"Please submit a bug at https://gitlab.com/ole.tange/tangetools/-/issues\n",
"Please submit a bug at https://git.data.coop/tange/tangetools/issues\n",
"and include:\n",
"* The version number: $Global::version\n",
"* The bugid: $bugid\n",
@ -891,7 +891,7 @@ sub version {
"This is free software: you are free to change and redistribute it.",
"$Global::progname comes with no warranty.",
"",
"Web site: https://gitlab.com/ole.tange/tangetools/\n",
"Web site: https://git.data.coop/tange/tangetools/\n",
);
}

View file

@ -281,7 +281,7 @@ To solve this sort the input with B<LC_ALL=C sort ...>.
=head1 REPORTING BUGS
B<2search> and B<2grep> are part of tangetools. Report bugs on
https://gitlab.com/ole.tange/tangetools/-/issues
https://git.data.coop/tange/tangetools/issues
=head1 AUTHOR
@ -870,7 +870,7 @@ sub die_bug {
my $bugid = shift;
print STDERR
("$Global::progname: This should not happen. You have found a bug.\n",
"Please submit a bug at https://gitlab.com/ole.tange/tangetools/-/issues\n",
"Please submit a bug at https://git.data.coop/tange/tangetools/issues\n",
"and include:\n",
"* The version number: $Global::version\n",
"* The bugid: $bugid\n",
@ -891,7 +891,7 @@ sub version {
"This is free software: you are free to change and redistribute it.",
"$Global::progname comes with no warranty.",
"",
"Web site: https://gitlab.com/ole.tange/tangetools/\n",
"Web site: https://git.data.coop/tange/tangetools/\n",
);
}

View file

@ -67,7 +67,7 @@ above 60C, let BIOS control fans.
=head1 REPORTING BUGS
Report bugs: https://gitlab.com/ole.tange/tangetools/-/issues
Report bugs: https://git.data.coop/tange/tangetools/issues
=head1 AUTHOR

View file

@ -178,7 +178,7 @@ however, minimize the one it finds.
=head1 REPORTING BUGS
Report bugs: https://gitlab.com/ole.tange/tangetools/-/issues
Report bugs: https://git.data.coop/tange/tangetools/issues
=head1 AUTHOR
@ -410,7 +410,7 @@ License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
find-first-fail comes with no warranty.
Web site: https://gitlab.com/ole.tange/tangetools/-/tree/master/find-first-fail
Web site: https://git.data.coop/tange/tangetools/src/branch/master/find-first-fail
EOF
}

View file

@ -1,8 +1,8 @@
all: iothrottle
true
iothrottle: iothrottle.hex iothrottle.in
perl -pe 's/hex=.*/"hex=".`cat iothrottle.hex`/e' iothrottle.in > iothrottle
iothrottle: iothrottle.hex iothrottle.sh
perl -pe 's/hex=.*/"hex=".`cat iothrottle.hex`/e' iothrottle.sh > iothrottle
chmod +x iothrottle
iothrottle.so: iothrottle.c
@ -15,7 +15,10 @@ iothrottle.hex: iothrottle.so
md5sum iothrottle.so iothrottle.bin
test: iothrottle
seq 10000000 | cat | pv > big
./iothrottle --io 1M ssh localhost seq 1000000 | pv >/dev/null
./iothrottle --io 3M curl -q https://ftp.nluug.nl/ftp/pub/os/Linux/distr/archlinux/pool/packages/dtkwidget-5.7.2-1-x86_64.pkg.tar.zst >/dev/null
./iothrottle --io 1M wget -qO- https://ftp.nluug.nl/ftp/pub/os/Linux/distr/archlinux/pool/packages/dtkwidget-5.7.2-1-x86_64.pkg.tar.zst | pv >/dev/null
./iothrottle -o 20000K seq 10000000 | pv > big
./iothrottle cat big | pv > /dev/null
./iothrottle -i 20M cat big | pv > /dev/null
./iothrottle -o 20000K cat big | pv > /dev/null

View file

@ -9,12 +9,48 @@
#include <sys/uio.h>
#include <sys/sendfile.h>
#include <linux/fs.h>
#include <string.h>
#include <stdarg.h>
#include <sys/socket.h>
// Function pointers for original read and write and other relevant calls
// Function pointers for standard I/O functions
int (*original_putchar_unlocked)(int);
int (*original_putc)(int, FILE *);
int (*original_putc_unlocked)(int, FILE *);
size_t (*original_fread)(void *, size_t, size_t, FILE *);
size_t (*original_fread_unlocked)(void *, size_t, size_t, FILE *);
int (*original_fgetc)(FILE *);
char *(*original_fgets)(char *, int, FILE *);
int (*original_getc)(FILE *);
int (*original_getc_unlocked)(FILE *);
// Function pointers for socket functions
ssize_t (*original_recv)(int, void *, size_t, int);
ssize_t (*original_recvfrom)(int, void *, size_t, int, struct sockaddr *, socklen_t *);
ssize_t (*original_recvmsg)(int, struct msghdr *, int);
// Function pointers for formatted input functions
int (*original_scanf)(const char *, ...);
int (*original_fscanf)(FILE *, const char *, ...);
// Additional low-level function pointers
ssize_t (*original_pread)(int fd, void *buf, size_t count, off_t offset);
ssize_t (*original_pwrite)(int fd, const void *buf, size_t count, off_t offset);
ssize_t (*original_readv)(int fd, const struct iovec *iov, int iovcnt);
ssize_t (*original_writev)(int fd, const struct iovec *iov, int iovcnt);
ssize_t (*original_read)(int fd, void *buf, size_t count);
ssize_t (*original_write)(int fd, const void *buf, size_t count);
size_t (*original_fwrite)(const void *ptr, size_t size, size_t nmemb, FILE *stream);
size_t (*original_fwrite_unlocked)(const void *ptr, size_t size, size_t nmemb, FILE *stream);
int (*original_fflush)(FILE *stream);
int (*original_fputs)(const char *str, FILE *stream);
int (*original_fputs_unlocked)(const char *str, FILE *stream);
int (*original_putchar)(int c);
int (*original_dprintf)(int fd, const char *format, ...);
ssize_t (*original_pread)(int fd, void *buf, size_t count, off_t offset);
ssize_t (*original_pwrite)(int fd, const void *buf, size_t count, off_t offset);
ssize_t (*original_readv)(int fd, const struct iovec *iov, int iovcnt);
@ -23,6 +59,20 @@ off_t (*original_sendfile)(int out_fd, int in_fd, off_t *offset, size_t count);
ssize_t (*original_splice)(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags);
ssize_t (*original_copy_file_range)(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags);
// Function pointer for original functions
ssize_t (*original_send)(int sockfd, const void *buf, size_t len, int flags);
ssize_t (*original_sendto)(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
ssize_t (*original_sendmsg)(int sockfd, const struct msghdr *msg, int flags);
ssize_t (*original_vmsplice)(int fd, const struct iovec *iov, unsigned long nr_segs,
unsigned int flags);
ssize_t (*original_process_vm_readv)(pid_t pid, const struct iovec *local_iov,
int local_iovcnt, const struct iovec *remote_iov,
int remote_iovcnt, unsigned long flags);
ssize_t (*original_process_vm_writev)(pid_t pid, const struct iovec *local_iov,
int local_iovcnt, const struct iovec *remote_iov,
int remote_iovcnt, unsigned long flags);
// Rate limits in bytes per second
static size_t read_limit = 0; // 0 = no limit
static size_t write_limit = 0;
@ -42,17 +92,19 @@ void throttle(size_t *bytes, size_t limit, struct timespec *start) {
clock_gettime(CLOCK_MONOTONIC, &now);
double elapsed = (now.tv_sec - start->tv_sec) + (now.tv_nsec - start->tv_nsec) / 1e9;
// If it is > 1 second since last write:
// treat as if it is only 1 second
elapsed = elapsed > 1 ? 1 : elapsed;
double allowed_bytes = limit * elapsed;
if (debug_mode) fprintf(stderr, "allowed: %ld written: %ld\n",
(long)allowed_bytes, *bytes);
(long)allowed_bytes, *bytes);
if (*bytes > allowed_bytes) {
*bytes -= allowed_bytes;
double sleep_time = *bytes / (double)limit;
*bytes -= allowed_bytes;
double sleep_time = *bytes / (double)limit;
if (debug_mode) fprintf(stderr, "sleep: %f\n", sleep_time);
usleep((useconds_t)(sleep_time * 1e6));
*start = now;
if (debug_mode) fprintf(stderr, "sleep: %f\n", sleep_time);
usleep((useconds_t)(sleep_time * 1e6));
*start = now;
}
}
@ -75,6 +127,105 @@ size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) {
return result;
}
#undef fwrite_unlocked
size_t fwrite_unlocked(const void *ptr, size_t size, size_t nmemb, FILE *stream) {
if (!original_fwrite_unlocked) {
original_fwrite_unlocked = dlsym(RTLD_NEXT, "fwrite_unlocked");
if (!original_fwrite_unlocked) {
if (debug_mode) fprintf(stderr, "Error loading original fwrite_unlocked: %s\n", dlerror());
exit(EXIT_FAILURE);
}
}
size_t total_bytes = size * nmemb;
throttle(&write_bytes, write_limit, &write_start);
size_t result = original_fwrite_unlocked(ptr, size, nmemb, stream);
if (result > 0) {
write_bytes += total_bytes;
}
return result;
}
int fputs(const char *str, FILE *stream) {
if (!original_fputs) {
original_fputs = dlsym(RTLD_NEXT, "fputs");
if (!original_fputs) {
if (debug_mode) fprintf(stderr, "Error loading original fputs: %s\n", dlerror());
exit(EXIT_FAILURE);
}
}
size_t total_bytes = strlen(str);
throttle(&write_bytes, write_limit, &write_start);
int result = original_fputs(str, stream);
if (result >= 0) {
write_bytes += total_bytes;
}
return result;
}
int fputs_unlocked(const char *str, FILE *stream) {
if (!original_fputs_unlocked) {
original_fputs_unlocked = dlsym(RTLD_NEXT, "fputs_unlocked");
if (!original_fputs_unlocked) {
if (debug_mode) fprintf(stderr, "Error loading original fputs_unlocked: %s\n", dlerror());
exit(EXIT_FAILURE);
}
}
size_t total_bytes = strlen(str);
throttle(&write_bytes, write_limit, &write_start);
int result = original_fputs_unlocked(str, stream);
if (result >= 0) {
write_bytes += total_bytes;
}
return result;
}
int putchar(int c) {
if (!original_putchar) {
original_putchar = dlsym(RTLD_NEXT, "putchar");
if (!original_putchar) {
if (debug_mode) fprintf(stderr, "Error loading original putchar: %s\n", dlerror());
exit(EXIT_FAILURE);
}
}
throttle(&write_bytes, write_limit, &write_start);
int result = original_putchar(c);
if (result != EOF) {
write_bytes += 1;
}
return result;
}
int dprintf(int fd, const char *format, ...) {
if (!original_dprintf) {
original_dprintf = dlsym(RTLD_NEXT, "dprintf");
if (!original_dprintf) {
if (debug_mode) fprintf(stderr, "Error loading original dprintf: %s\n", dlerror());
exit(EXIT_FAILURE);
}
}
va_list args;
va_start(args, format);
char buffer[1024];
int len = vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);
if (len > 0) {
throttle(&write_bytes, write_limit, &write_start);
write_bytes += len;
}
return original_dprintf(fd, "%s", buffer);
}
int fflush(FILE *stream) {
if (!original_fflush) {
original_fflush = dlsym(RTLD_NEXT, "fflush");
@ -257,6 +408,368 @@ ssize_t copy_file_range(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out,
return result;
}
int putchar_unlocked(int c) {
if (!original_putchar_unlocked) {
original_putchar_unlocked = dlsym(RTLD_NEXT, "putchar_unlocked");
if (!original_putchar_unlocked) {
fprintf(stderr, "Error loading original putchar_unlocked: %s\n", dlerror());
exit(EXIT_FAILURE);
}
}
throttle(&write_bytes, write_limit, &write_start);
int result = original_putchar_unlocked(c);
if (result != EOF) {
write_bytes += 1;
}
return result;
}
ssize_t recv(int sockfd, void *buf, size_t len, int flags) {
if (!original_recv) {
original_recv = dlsym(RTLD_NEXT, "recv");
if (!original_recv) {
fprintf(stderr, "Error loading original recv: %s\n", dlerror());
exit(EXIT_FAILURE);
}
}
throttle(&read_bytes, read_limit, &read_start);
ssize_t result = original_recv(sockfd, buf, len, flags);
if (result > 0) {
read_bytes += result;
}
return result;
}
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen) {
if (!original_recvfrom) {
original_recvfrom = dlsym(RTLD_NEXT, "recvfrom");
if (!original_recvfrom) {
fprintf(stderr, "Error loading original recvfrom: %s\n", dlerror());
exit(EXIT_FAILURE);
}
}
throttle(&read_bytes, read_limit, &read_start);
ssize_t result = original_recvfrom(sockfd, buf, len, flags, src_addr, addrlen);
if (result > 0) {
read_bytes += result;
}
return result;
}
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags) {
if (!original_recvmsg) {
original_recvmsg = dlsym(RTLD_NEXT, "recvmsg");
if (!original_recvmsg) {
fprintf(stderr, "Error loading original recvmsg: %s\n", dlerror());
exit(EXIT_FAILURE);
}
}
throttle(&read_bytes, read_limit, &read_start);
ssize_t result = original_recvmsg(sockfd, msg, flags);
if (result > 0) {
read_bytes += result;
}
return result;
}
int putc(int c, FILE *stream) {
if (!original_putc) {
original_putc = dlsym(RTLD_NEXT, "putc");
if (!original_putc) {
fprintf(stderr, "Error loading original putc: %s\n", dlerror());
exit(EXIT_FAILURE);
}
}
throttle(&write_bytes, write_limit, &write_start);
int result = original_putc(c, stream);
if (result != EOF) {
write_bytes += 1;
}
return result;
}
int putc_unlocked(int c, FILE *stream) {
if (!original_putc_unlocked) {
original_putc_unlocked = dlsym(RTLD_NEXT, "putc_unlocked");
if (!original_putc_unlocked) {
fprintf(stderr, "Error loading original putc_unlocked: %s\n", dlerror());
exit(EXIT_FAILURE);
}
}
throttle(&write_bytes, write_limit, &write_start);
int result = original_putc_unlocked(c, stream);
if (result != EOF) {
write_bytes += 1;
}
return result;
}
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) {
if (!original_fread) {
original_fread = dlsym(RTLD_NEXT, "fread");
if (!original_fread) {
fprintf(stderr, "Error loading original fread: %s\n", dlerror());
exit(EXIT_FAILURE);
}
}
throttle(&read_bytes, read_limit, &read_start);
size_t result = original_fread(ptr, size, nmemb, stream);
if (result > 0) {
read_bytes += result * size;
}
return result;
}
size_t fread_unlocked(void *ptr, size_t size, size_t nmemb, FILE *stream) {
if (!original_fread_unlocked) {
original_fread_unlocked = dlsym(RTLD_NEXT, "fread_unlocked");
if (!original_fread_unlocked) {
fprintf(stderr, "Error loading original fread_unlocked: %s\n", dlerror());
exit(EXIT_FAILURE);
}
}
throttle(&read_bytes, read_limit, &read_start);
size_t result = original_fread_unlocked(ptr, size, nmemb, stream);
if (result > 0) {
read_bytes += result * size;
}
return result;
}
int fgetc(FILE *stream) {
if (!original_fgetc) {
original_fgetc = dlsym(RTLD_NEXT, "fgetc");
if (!original_fgetc) {
fprintf(stderr, "Error loading original fgetc: %s\n", dlerror());
exit(EXIT_FAILURE);
}
}
throttle(&read_bytes, read_limit, &read_start);
int result = original_fgetc(stream);
if (result != EOF) {
read_bytes += 1;
}
return result;
}
char *fgets(char *s, int size, FILE *stream) {
if (!original_fgets) {
original_fgets = dlsym(RTLD_NEXT, "fgets");
if (!original_fgets) {
fprintf(stderr, "Error loading original fgets: %s\n", dlerror());
exit(EXIT_FAILURE);
}
}
throttle(&read_bytes, read_limit, &read_start);
char *result = original_fgets(s, size, stream);
if (result != NULL) {
read_bytes += strlen(result);
}
return result;
}
int getc(FILE *stream) {
if (!original_getc) {
original_getc = dlsym(RTLD_NEXT, "getc");
if (!original_getc) {
fprintf(stderr, "Error loading original getc: %s\n", dlerror());
exit(EXIT_FAILURE);
}
}
throttle(&read_bytes, read_limit, &read_start);
int result = original_getc(stream);
if (result != EOF) {
read_bytes += 1;
}
return result;
}
int getc_unlocked(FILE *stream) {
if (!original_getc_unlocked) {
original_getc_unlocked = dlsym(RTLD_NEXT, "getc_unlocked");
if (!original_getc_unlocked) {
fprintf(stderr, "Error loading original getc_unlocked: %s\n", dlerror());
exit(EXIT_FAILURE);
}
}
throttle(&read_bytes, read_limit, &read_start);
int result = original_getc_unlocked(stream);
if (result != EOF) {
read_bytes += 1;
}
return result;
}
int scanf(const char *format, ...) {
if (!original_scanf) {
original_scanf = dlsym(RTLD_NEXT, "scanf");
if (!original_scanf) {
fprintf(stderr, "Error loading original scanf: %s\n", dlerror());
exit(EXIT_FAILURE);
}
}
throttle(&read_bytes, read_limit, &read_start); // Throttle before reading
va_list args;
va_start(args, format);
int result = vscanf(format, args);
va_end(args);
if (result > 0) {
// Note: Accurate byte count for scanf is difficult; this is an estimate
read_bytes += result;
}
return result;
}
int fscanf(FILE *stream, const char *format, ...) {
if (!original_fscanf) {
original_fscanf = dlsym(RTLD_NEXT, "fscanf");
if (!original_fscanf) {
fprintf(stderr, "Error loading original fscanf: %s\n", dlerror());
exit(EXIT_FAILURE);
}
}
throttle(&read_bytes, read_limit, &read_start);
va_list args;
va_start(args, format);
int result = vfscanf(stream, format, args);
va_end(args);
if (result > 0) {
read_bytes += result;
}
return result;
}
ssize_t send(int sockfd, const void *buf, size_t len, int flags) {
if (!original_send) {
original_send = dlsym(RTLD_NEXT, "send");
if (!original_send) {
fprintf(stderr, "Error loading original send: %s\n", dlerror());
exit(EXIT_FAILURE);
}
}
throttle(&write_bytes, write_limit, &write_start);
ssize_t result = original_send(sockfd, buf, len, flags);
if (result > 0) {
write_bytes += result;
}
return result;
}
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen) {
if (!original_sendto) {
original_sendto = dlsym(RTLD_NEXT, "sendto");
if (!original_sendto) {
fprintf(stderr, "Error loading original sendto: %s\n", dlerror());
exit(EXIT_FAILURE);
}
}
throttle(&write_bytes, write_limit, &write_start);
ssize_t result = original_sendto(sockfd, buf, len, flags, dest_addr, addrlen);
if (result > 0) {
write_bytes += result;
}
return result;
}
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags) {
if (!original_sendmsg) {
original_sendmsg = dlsym(RTLD_NEXT, "sendmsg");
if (!original_sendmsg) {
fprintf(stderr, "Error loading original sendmsg: %s\n", dlerror());
exit(EXIT_FAILURE);
}
}
size_t total_bytes = 0;
for (int i = 0; i < msg->msg_iovlen; i++) {
total_bytes += msg->msg_iov[i].iov_len;
}
throttle(&write_bytes, write_limit, &write_start);
ssize_t result = original_sendmsg(sockfd, msg, flags);
if (result > 0) {
write_bytes += total_bytes;
}
return result;
}
ssize_t vmsplice(int fd, const struct iovec *iov, unsigned long nr_segs,
unsigned int flags) {
if (!original_vmsplice) {
original_vmsplice = dlsym(RTLD_NEXT, "vmsplice");
if (!original_vmsplice) {
fprintf(stderr, "Error loading original vmsplice: %s\n", dlerror());
exit(EXIT_FAILURE);
}
}
size_t total_bytes = 0;
for (unsigned long i = 0; i < nr_segs; i++) {
total_bytes += iov[i].iov_len;
}
throttle(&write_bytes, write_limit, &write_start);
ssize_t result = original_vmsplice(fd, iov, nr_segs, flags);
if (result > 0) {
write_bytes += total_bytes;
}
return result;
}
ssize_t process_vm_readv(pid_t pid, const struct iovec *local_iov, int local_iovcnt,
const struct iovec *remote_iov, int remote_iovcnt,
unsigned long flags) {
if (!original_process_vm_readv) {
original_process_vm_readv = dlsym(RTLD_NEXT, "process_vm_readv");
if (!original_process_vm_readv) {
fprintf(stderr, "Error loading original process_vm_readv: %s\n", dlerror());
exit(EXIT_FAILURE);
}
}
size_t total_bytes = 0;
for (int i = 0; i < local_iovcnt; i++) {
total_bytes += local_iov[i].iov_len;
}
throttle(&read_bytes, read_limit, &read_start);
ssize_t result = original_process_vm_readv(pid, local_iov, local_iovcnt,
remote_iov, remote_iovcnt, flags);
if (result > 0) {
read_bytes += total_bytes;
}
return result;
}
ssize_t process_vm_writev(pid_t pid, const struct iovec *local_iov, int local_iovcnt,
const struct iovec *remote_iov, int remote_iovcnt,
unsigned long flags) {
if (!original_process_vm_writev) {
original_process_vm_writev = dlsym(RTLD_NEXT, "process_vm_writev");
if (!original_process_vm_writev) {
fprintf(stderr, "Error loading original process_vm_writev: %s\n", dlerror());
exit(EXIT_FAILURE);
}
}
size_t total_bytes = 0;
for (int i = 0; i < local_iovcnt; i++) {
total_bytes += local_iov[i].iov_len;
}
throttle(&write_bytes, write_limit, &write_start);
ssize_t result = original_process_vm_writev(pid, local_iov, local_iovcnt,
remote_iov, remote_iovcnt, flags);
if (result > 0) {
write_bytes += total_bytes;
}
return result;
}
__attribute__((constructor))
void init() {
clock_gettime(CLOCK_MONOTONIC, &read_start);

View file

@ -1,5 +1,7 @@
#!/bin/bash
# Edit iothrottle.sh - not iothrottle
: <<=cut
=pod
@ -30,6 +32,11 @@ To I<input-speed> you can append k, K, m, M, g, G to multiply by 1000,
1024, 1000000, 1048576 respectively.
=item B<--io> I<speed>
Shorthand for B<-i> I<speed> B<-o> I<speed>
=item B<-o> I<output-speed>
Throttle B<write>(2) to I<output-speed> bytes per second.
@ -55,13 +62,19 @@ speed can go over the limit for a single call. Thus if the limit is
400 bytes/sec, but the call moves 4096 bytes, there will be a pause of
10 secs.
Not all program use B<read>(2) and B<write>(2) for I/O.
If the program spawns processes, each child will get the limit. In
other words: two children each with a limit of 1M may produce 2MB/s.
These all use other methods: cp (to same filesystem), seq
Not all programs use B<read>(2) and B<write>(2) for I/O, so
B<iothrottle> tries to intercept other relevant calls, too.
Tested: curl, wget, ssh, ffmpeg, cat, cp
These programs use other methods: cp (to same filesystem)
Statically linked programs will also not work.
File a bug report when you find other programs.
File a bug report when you find programs that fail.
=head1 BUGS
@ -148,6 +161,11 @@ while [[ $# -gt 0 ]]; do
OUTPUT_LIMIT="$2"
shift 2
;;
--io)
INPUT_LIMIT="$2"
OUTPUT_LIMIT="$2"
shift 2
;;
*)
break
;;

View file

@ -4,7 +4,7 @@
- plot data from a pipe -
URL: https://gitlab.com/ole.tange/tangetools/-/tree/master/plotpipe
URL: https://git.data.coop/tange/tangetools/src/branch/master/plotpipe
We have all been there: You have a bunch of data from a pipe that you
would like to get a better understanding of.

View file

@ -235,7 +235,7 @@ sub version() {
"This is free software: you are free to change and redistribute it.",
"$Global::progname comes with no warranty.",
"",
"Web site: https://gitlab.com/ole.tange/tangetools/-/tree/master/${Global::progname}\n",
"Web site: https://git.data.coop/tange/tangetools/src/branch/master/${Global::progname}\n",
"",
);
}

View file

@ -110,7 +110,7 @@ sub die_bug($) {
my $bugid = shift;
print STDERR
("$Global::progname: This should not happen. You have found a bug.\n",
"Please file a bugreport https://gitlab.com/ole.tange/tangetools/issues\n",
"Please file a bugreport https://git.data.coop/tange/tangetools/issues\n",
"\n",
"Include this in the report:\n",
"* The version number: $Global::version\n",

View file

@ -269,7 +269,7 @@ sub version() {
"This is free software: you are free to change and redistribute it.",
"GNU $Global::progname comes with no warranty.",
"",
"Web site: https://gitlab.com/ole.tange/tangetools/-/tree/master/${Global::progname}\n",
"Web site: https://git.data.coop/tange/tangetools/src/branch/master/${Global::progname}\n",
);
}

View file

@ -540,7 +540,7 @@ sub version() {
"This is free software: you are free to change and redistribute it.",
"$Global::progname comes with no warranty.",
"",
"Web site: https://gitlab.com/ole.tange/tangetools/-/tree/master/${Global::progname}\n",
"Web site: https://git.data.coop/tange/tangetools/src/branch/master/${Global::progname}\n",
);
}

View file

@ -108,7 +108,7 @@ cleaned up, if B<transpose> is stopped abnormally (e.g. killed).
=head1 REPORTING BUGS
Report bugs: https://gitlab.com/ole.tange/tangetools/-/issues
Report bugs: https://git.data.coop/tange/tangetools/issues
=head1 AUTHOR
@ -495,7 +495,7 @@ License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
transpose comes with no warranty.
Web site: https://gitlab.com/ole.tange/tangetools/-/tree/master/transpose
Web site: https://git.data.coop/tange/tangetools/src/branch/master/transpose
EOF
}

View file

@ -24,7 +24,7 @@ function descriptor()
.. "<br>This will NOT change your playlist, it will move the file itself. "
.. "<br>Wastebasket will search for a dir called .waste "
.. "in the dir of the file and all parent dirs of that.";
url = "https://gitlab.com/ole.tange/tangetools/tree/master/wastebasket"
url = "https://git.data.coop/tange/tangetools/src/branch/master/wastebasket"
}
end