iothrottle: More functions intercepted.
This commit is contained in:
parent
1d41f696cc
commit
715304f395
|
@ -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",
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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",
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
;;
|
|
@ -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.
|
||||
|
|
|
@ -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",
|
||||
"",
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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",
|
||||
);
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in a new issue