df: Introduce new b and f options which will allow easier scripting when checking if there is enough diskspace * doc/coreutils.texi: Documentation about new -b and -f options * src/df.c (exit_status, percentage_free_requirement) (quit_on_low_percentage, bytes_free_requirement) (quit_on_low_bytes, long_options, get_dev, usage, main): Handle new -b and -f options by suppressing output and return after first compare diff --git a/doc/coreutils.texi b/doc/coreutils.texi index 4d7d9439d..aac63cff3 100644 --- a/doc/coreutils.texi +++ b/doc/coreutils.texi @@ -12100,6 +12100,16 @@ Inaccessible file systems are those which are mounted but subsequently over-mounted by another file system at that point, or otherwise inaccessible due to permissions of the mount point etc. +@item -b @var{size} +@itemx --bytes-free=@var{size} +@opindex -b +@opindex --bytes-free +@cindex free file system size +This option requires the user to specify exactly one @var{file} argument. df +will then determine if the corresponding filesystem has atleast @var{size} bytes +of available space. If so df will return 0 and produce no output, otherwise the +return code will be 1 and produce no output. + @item -B @var{size} @itemx --block-size=@var{size} @opindex -B @@ -12108,6 +12118,14 @@ due to permissions of the mount point etc. Scale sizes by @var{size} before printing them (@pxref{Block size}). For example, @option{-BG} prints sizes in units of 1,073,741,824 bytes. +@item -f @var{percentage} +@itemx --free=@var{percentage} +@opindex -f +@opindex --free +@cindex free file system size +This option works exactly as the @option{-b} with the difference that it checks +if atleast @var{percentage} of the filesystem is available. + @optHumanReadable @item -H @@ -12343,6 +12361,10 @@ inspect the exit status of a command like @samp{df -t ext3 -t reiserfs @var{dir}} to test whether @var{dir} is on a file system of type @samp{ext3} or @samp{reiserfs}. +With the @option{-b} and @option{-f} options one can use the exit status to +verify the available space (in bytes or percentage) with a command like +@samp{if df -b 640000 /var/log ; then echo "That is big enough for everyone"; fi} + Since the list of file systems (@var{mtab}) is needed to determine the file system type, failure includes the cases when that list cannot be read and one or more of the options @option{-a}, @option{-l}, @option{-t} diff --git a/src/df.c b/src/df.c index f3d8e2e2b..6aeee11d2 100644 --- a/src/df.c +++ b/src/df.c @@ -90,6 +90,18 @@ static bool require_sync; /* Desired exit status. */ static int exit_status; +/* Required percentage disk free on all examined filesystems*/ +static uintmax_t percentage_free_requirement; + +/* Should we quit if the percentage is below percentage_free_requirement?*/ +static bool quit_on_low_percentage; + +/* Required number of bytes disk free on all examined filesystems*/ +static uintmax_t bytes_free_requirement; + +/* Should we quit if the number of bytes is below bytes_free_requirement?*/ +static bool quit_on_low_bytes; + /* A file system type to display. */ struct fs_type_list @@ -271,6 +283,8 @@ static struct option const long_options[] = {"total", no_argument, NULL, TOTAL_OPTION}, {"type", required_argument, NULL, 't'}, {"exclude-type", required_argument, NULL, 'x'}, + {"free", required_argument, NULL, 'f'}, + {"free-bytes", required_argument, NULL, 'b'}, {GETOPT_HELP_OPTION_DECL}, {GETOPT_VERSION_OPTION_DECL}, {NULL, 0, NULL, 0} @@ -1125,6 +1139,72 @@ get_dev (char const *device, char const *mount_point, char const *file, struct field_values_t inode_values; get_field_values (&block_values, &inode_values, &fsu); + /* If this is set, then we either fail directly or just return from the function since it fullfilled the space requirements */ + if (quit_on_low_bytes) + { + /* This code is basically a copy of the code for the percentage calculation below */ + struct field_values_t *v; + + v = &block_values; + if (! known_value (v->used) || ! known_value (v->available)) + main_exit (1); + + if (bytes_free_requirement <= v->available * v->input_units) + return; + else + main_exit (1); + } + else if (quit_on_low_percentage) + { + /* This code is basically a copy of the code for the percentage calculation below */ + double pct = -1; + struct field_values_t *v; + + v = &block_values; + if (! known_value (v->used) || ! known_value (v->available)) + main_exit (1); + else if (!v->negate_used + && v->used <= TYPE_MAXIMUM (uintmax_t) / 100 + && v->used + v->available != 0 + && (v->used + v->available < v->used) + == v->negate_available) + { + uintmax_t u100 = v->used * 100; + uintmax_t nonroot_total = v->used + v->available; + pct = u100 / nonroot_total + (u100 % nonroot_total != 0); + } + else + { + /* The calculation cannot be done easily with integer + arithmetic. Fall back on floating point. This can suffer + from minor rounding errors, but doing it exactly requires + multiple precision arithmetic, and it's not worth the + aggravation. */ + double u = v->negate_used ? - (double) - v->used : v->used; + double a = v->negate_available + ? - (double) - v->available : v->available; + double nonroot_total = u + a; + if (nonroot_total) + { + long int lipct = pct = u * 100 / nonroot_total; + double ipct = lipct; + + /* Like 'pct = ceil (dpct);', but avoid ceil so that + the math library needn't be linked. */ + if (ipct - 1 < pct && pct <= ipct + 1) + pct = ipct + (ipct < pct); + } + } + + if (percentage_free_requirement <= 100 - pct) + { + free (dev_name); + return; + } + else + main_exit (1); + } + /* Add to grand total unless processing grand total line. */ if (print_grand_total && ! force_fsu) add_to_grand_total (&block_values, &inode_values); @@ -1565,6 +1645,16 @@ or all file systems by default.\n\ fputs (_("\ --total elide all entries insignificant to available space,\n\ and produce a grand total\n\ +"), stdout); + fputs (_("\ + -f, --free=PERCENTAGE Produce no output and return 1 if there is any specified device with\n\ + less than PERCENTAGE % bytes free, else return 0. You must specify\n\ + exactly one FILE with this option and no other options\n\ +"), stdout); + fputs (_("\ + -b, --free-bytes=BYTES Produce no output and return 1 if there is any specified device with\n\ + less than BYTES bytes free, else return 0. You must specify\n\ + exactly one FILE with this option and no other options\n\ "), stdout); fputs (_("\ -t, --type=TYPE limit listing to file systems of type TYPE\n\ @@ -1599,6 +1689,10 @@ main (int argc, char **argv) atexit (close_stdout); + percentage_free_requirement = 0; + bytes_free_requirement = 0; + quit_on_low_percentage = false; + quit_on_low_bytes = false; fs_select_list = NULL; fs_exclude_list = NULL; show_all_fs = false; @@ -1618,7 +1712,7 @@ main (int argc, char **argv) while (true) { int oi = -1; - int c = getopt_long (argc, argv, "aB:iF:hHklmPTt:vx:", long_options, + int c = getopt_long (argc, argv, "aB:iF:f:b:hHklmPTt:vx:", long_options, &oi); if (c == -1) break; @@ -1636,6 +1730,24 @@ main (int argc, char **argv) xstrtol_fatal (e, oi, c, long_options, optarg); } break; + case 'f': + { + enum strtol_error e = human_options (optarg, &human_output_opts, + &percentage_free_requirement); + if (e != LONGINT_OK) + xstrtol_fatal (e, oi, c, long_options, optarg); + quit_on_low_percentage = true; + break; + } + case 'b': + { + enum strtol_error e = human_options (optarg, &human_output_opts, + &bytes_free_requirement); + if (e != LONGINT_OK) + xstrtol_fatal (e, oi, c, long_options, optarg); + quit_on_low_bytes = true; + break; + } case 'i': if (header_mode == OUTPUT_MODE) { @@ -1732,24 +1844,31 @@ main (int argc, char **argv) } } - if (human_output_opts == -1) + if ( (quit_on_low_percentage || quit_on_low_bytes) && (argc != 4 )) { + usage (EXIT_FAILURE); + } + + if (!quit_on_low_percentage && !quit_on_low_bytes) { - if (posix_format) + if (human_output_opts == -1) { - human_output_opts = 0; - output_block_size = (getenv ("POSIXLY_CORRECT") ? 512 : 1024); + if (posix_format) + { + human_output_opts = 0; + output_block_size = (getenv ("POSIXLY_CORRECT") ? 512 : 1024); + } + else + human_options (getenv ("DF_BLOCK_SIZE"), + &human_output_opts, &output_block_size); } - else - human_options (getenv ("DF_BLOCK_SIZE"), - &human_output_opts, &output_block_size); - } - if (header_mode == INODES_MODE || header_mode == OUTPUT_MODE) - ; - else if (human_output_opts & human_autoscale) - header_mode = HUMAN_MODE; - else if (posix_format) - header_mode = POSIX_MODE; + if (header_mode == INODES_MODE || header_mode == OUTPUT_MODE) + ; + else if (human_output_opts & human_autoscale) + header_mode = HUMAN_MODE; + else if (posix_format) + header_mode = POSIX_MODE; + } /* Fail if the same file system type was both selected and excluded. */ { @@ -1824,8 +1943,12 @@ main (int argc, char **argv) if (require_sync) sync (); - get_field_list (); - get_header (); + + if (!quit_on_low_percentage && !quit_on_low_bytes) + { + get_field_list (); + get_header (); + } if (stats) { @@ -1846,7 +1969,8 @@ main (int argc, char **argv) (field_data[SOURCE_FIELD].used ? "-" : "total"), NULL, NULL, NULL, false, false, &grand_fsu, false); - print_table (); + if ( ! quit_on_low_percentage && ! quit_on_low_bytes) + print_table (); } else {