215 lines
4.4 KiB
Bash
Executable file
215 lines
4.4 KiB
Bash
Executable file
#!/bin/bash
|
|
|
|
# Edit iothrottle.sh - not iothrottle
|
|
|
|
: <<=cut
|
|
=pod
|
|
|
|
=head1 NAME
|
|
|
|
iothrottle - Limit IO to a given speed
|
|
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
B<iothrottle> [-i read-speed] [-o write-speed] I<command>
|
|
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
B<iothrottle> limits B<read>(2) and B<write>(2) to a given speed.
|
|
|
|
|
|
=head1 OPTIONS
|
|
|
|
=over 4
|
|
|
|
=item B<-i> I<input-speed>
|
|
|
|
Throttle B<read>(2) to I<input-speed> bytes per second.
|
|
|
|
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.
|
|
|
|
To I<output-speed> you can append k, K, m, M, g, G to multiply by 1000,
|
|
1024, 1000000, 1048576 respectively.
|
|
|
|
=back
|
|
|
|
|
|
=head1 EXAMPLE
|
|
|
|
Copy mydir at 1MB/s:
|
|
|
|
iothrottle -i 1M cp -a mydir/ /other/filesystem/
|
|
|
|
|
|
=head1 LIMITATIONS
|
|
|
|
B<iothrottle> intercepts calls to B<read>(2) and B<write>(2). If too
|
|
much is read/written it inserts a pause for each call. This means the
|
|
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.
|
|
|
|
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.
|
|
|
|
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 programs that fail.
|
|
|
|
|
|
=head1 BUGS
|
|
|
|
File bugs at: https://git.data.coop/tange/tangetools/issues
|
|
|
|
|
|
=head1 ENVIRONMENT VARIABLES
|
|
|
|
=over 9
|
|
|
|
=item $IOTHROTTLE_DEBUG=1
|
|
|
|
Turn on debugging.
|
|
|
|
=back
|
|
|
|
|
|
=head1 AUTHOR
|
|
|
|
Copyright (C) 2024 Ole Tange,
|
|
http://ole.tange.dk and Free Software Foundation, Inc.
|
|
|
|
|
|
=head1 LICENSE
|
|
|
|
Copyright (C) 2012 Free Software Foundation, Inc.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
at your option any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
=head1 DEPENDENCIES
|
|
|
|
B<iothrottle> uses B<bash>.
|
|
|
|
|
|
=head1 SEE ALSO
|
|
|
|
B<ionice>, B<bwlimit>
|
|
|
|
|
|
=cut
|
|
|
|
|
|
_hex() {
|
|
# hex encoded iothrottle.so
|
|
local hex=dummy
|
|
echo $hex
|
|
}
|
|
|
|
_hex_to_bin() {
|
|
LC_ALL=C awk '{for (i=1; i<=length($0); i+=2) printf "%c", strtonum("0x" substr($0, i, 2))}'
|
|
}
|
|
|
|
_iothrottle.so() {
|
|
local iothrottleso=$(mktemp -t iotrottle.XXXXX)
|
|
_hex | _hex_to_bin > "$iothrottleso"
|
|
echo "$iothrottleso"
|
|
}
|
|
|
|
# Default values for input and output limits
|
|
INPUT_LIMIT=""
|
|
OUTPUT_LIMIT=""
|
|
|
|
# Parse command-line arguments
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
-i)
|
|
INPUT_LIMIT="$2"
|
|
shift 2
|
|
;;
|
|
-o)
|
|
OUTPUT_LIMIT="$2"
|
|
shift 2
|
|
;;
|
|
--io)
|
|
INPUT_LIMIT="$2"
|
|
OUTPUT_LIMIT="$2"
|
|
shift 2
|
|
;;
|
|
*)
|
|
break
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Convert limits to bytes
|
|
convert_to_bytes() {
|
|
local value="$1"
|
|
if [[ "$value" =~ ^[0-9]+K$ ]]; then
|
|
value=$(( ${value%K*} * 1024 ))
|
|
fi
|
|
if [[ "$value" =~ ^[0-9]+k$ ]]; then
|
|
value=$(( ${value%k*} * 1000 ))
|
|
fi
|
|
if [[ "$value" =~ ^[0-9]+M$ ]]; then
|
|
value=$(( ${value%M*} * 1024 * 1024 ))
|
|
fi
|
|
if [[ "$value" =~ ^[0-9]+m$ ]]; then
|
|
value=$(( ${value%m*} * 1000 * 1000 ))
|
|
fi
|
|
echo "$value"
|
|
}
|
|
|
|
IOTHROTTLE_READ=$(convert_to_bytes "$INPUT_LIMIT")
|
|
IOTHROTTLE_WRITE=$(convert_to_bytes "$OUTPUT_LIMIT")
|
|
|
|
# Export environment variables
|
|
export IOTHROTTLE_READ=${IOTHROTTLE_READ:-0}
|
|
export IOTHROTTLE_WRITE=${IOTHROTTLE_WRITE:-0}
|
|
|
|
# Preload the library and execute the remaining command
|
|
_so=$(_iothrottle.so)
|
|
if [ IOTHROTTLE_DEBUG = 1 ] ; then
|
|
echo LD_PRELOAD=$_so
|
|
LD_PRELOAD=$_so "$@"
|
|
else
|
|
cleanup() {
|
|
# if parent is dead: remove tmpfile
|
|
ppid=$1
|
|
while kill -0 $ppid 2>/dev/null; do sleep 1; done
|
|
rm $_so
|
|
}
|
|
cleanup $$ &
|
|
LD_PRELOAD=$_so "$@"
|
|
fi
|