From: Arran Cudbard-Bell Date: Wed, 8 Jun 2022 16:09:54 +0000 (-0400) Subject: Add size printing/parsing X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=cd59058558275715ca46761ae96c652f3cb0a8d8;p=thirdparty%2Ffreeradius-server.git Add size printing/parsing --- diff --git a/src/bin/radiusd.c b/src/bin/radiusd.c index a9e502a2d57..73927c5e4ac 100644 --- a/src/bin/radiusd.c +++ b/src/bin/radiusd.c @@ -33,10 +33,11 @@ RCSID("$Id$") #include #include #include -#include #include #include #include +#include +#include #include #include @@ -272,7 +273,7 @@ int main(int argc, char *argv[]) */ env = getenv("FR_GLOBAL_POOL"); if (env) { - if (fr_size_from_str(&pool_size, env) < 0) { + if (fr_size_from_str(&pool_size, &FR_SBUFF_IN(env, strlen(env))) < 0) { fr_perror("Invalid pool size string \"%s\"", env); EXIT_WITH_FAILURE; } diff --git a/src/lib/server/cf_parse.c b/src/lib/server/cf_parse.c index 3a1f273b32d..b1074d5c9b7 100644 --- a/src/lib/server/cf_parse.c +++ b/src/lib/server/cf_parse.c @@ -298,6 +298,7 @@ int cf_pair_parse_value(TALLOC_CTX *ctx, void *out, UNUSED void *base, CONF_ITEM if (cf_pair_to_value_box(ctx, &vb, cf_item_to_pair(ci), rule) < 0) goto error; if (fr_value_box_memcpy_out(out, &vb) < 0) { + cf_log_perr(cp, "Failed unboxing parsed configuration item value"); fr_value_box_clear_value(&vb); goto error; } diff --git a/src/lib/util/libfreeradius-util.mk b/src/lib/util/libfreeradius-util.mk index bfd577066a2..20924df0b3f 100644 --- a/src/lib/util/libfreeradius-util.mk +++ b/src/lib/util/libfreeradius-util.mk @@ -68,6 +68,7 @@ SOURCES := \ sbuff.c \ sem.c \ sha1.c \ + size.c \ snprintf.c \ socket.c \ strerror.c \ diff --git a/src/lib/util/misc.c b/src/lib/util/misc.c index 3047358b886..c00ffb382db 100644 --- a/src/lib/util/misc.c +++ b/src/lib/util/misc.c @@ -491,68 +491,6 @@ size_t fr_snprint_uint128(char *out, size_t outlen, uint128_t const num) return strlcpy(out, p, outlen); } -int fr_size_from_str(size_t *out, char const *str) -{ - char *q = NULL; - uint64_t size; - - *out = 0; - - size = strtoull(str, &q, 10); - switch (tolower(q[0])) { - case 'n': /* nibble */ - if (size & 0x01) { - fr_strerror_const("Sizes specified in nibbles must be an even number"); - return -1; - } - size /= 2; - break; - - case '\0': - case 'b': /* byte */ - break; - - case 'k': /* kilobyte */ - if (!fr_multiply(&size, size, 1024)) { - overflow: - fr_strerror_printf("Value must be less than %zu", (size_t)SIZE_MAX); - return -1; - } - break; - - case 'm': /* megabyte */ - if (!fr_multiply(&size, size, (1024 * 1024))) goto overflow; - break; - - case 'g': /* gigabyte */ - if (!fr_multiply(&size, size, (1024 * 1024 * 1024))) goto overflow; - break; - - case 't': /* terabyte */ - if (!fr_multiply(&size, size, ((uint64_t)1024 * 1024 * 1024 * 1024))) goto overflow; - break; - - default: - fr_strerror_printf("Unknown unit '%c'", *q); - return -1; - } - - if ((q[0] != '\0') && (q[1] != '\0')) { - fr_strerror_printf("Trailing garbage in size string \"%s\"", str); - return -1; - } - - if (size > SIZE_MAX) { - fr_strerror_printf("Value %" PRIu64 " is greater than the maximum " - "file/memory size of this system (%zu)", size, (size_t)SIZE_MAX); - - goto overflow; - } - - *out = (size_t)size; - - return 0; -} /** Multiply with modulo wrap * diff --git a/src/lib/util/misc.h b/src/lib/util/misc.h index ed8cf29636b..0ccb4299e4b 100644 --- a/src/lib/util/misc.h +++ b/src/lib/util/misc.h @@ -198,7 +198,7 @@ static inline bool is_zero(char const *value) return true; } -/** Find the highest order bit in an unsigned 64 bit integer +/** Find the highest order high bit in an unsigned 64 bit integer * * @return 0-64 indicating the position of the highest bit, * with 0 indicating no high bits, 1 indicating the 1st @@ -217,6 +217,30 @@ static inline uint8_t fr_high_bit_pos(uint64_t num) #endif } +/** Find the lowest order high bit in an unsigned 64 bit integer + * + * @return 0-64 indicating the position of the lowest bit, + * with 0 indicating no high bits, 1 indicating the 1st + * bit and 64 indicating the last bit. + */ +static inline uint8_t fr_low_bit_pos(uint64_t num) +{ + if (num == 0) return 0; + +#ifdef HAVE_BUILTIN_CLZLL + return __builtin_ctzll(num) + 1; +#else + uint8_t ret = 1; + + do { + if (num & 0x01) break; + ret++; + } while (num >>= 1); + + return ret; +#endif +} + int fr_set_signal(int sig, sig_t func); int fr_unset_signal(int sig); int rad_lockfd(int fd, int lock_len); @@ -235,7 +259,6 @@ size_t fr_snprint_uint128(char *out, size_t outlen, uint128_t const num); uint64_t fr_multiply_mod(uint64_t lhs, uint64_t rhs, uint64_t mod); -int fr_size_from_str(size_t *out, char const *str); int8_t fr_pointer_cmp(void const *a, void const *b); void fr_quick_sort(void const *to_sort[], int min_idx, int max_idx, fr_cmp_t cmp); int fr_digest_cmp(uint8_t const *a, uint8_t const *b, size_t length) CC_HINT(nonnull); diff --git a/src/lib/util/size.c b/src/lib/util/size.c new file mode 100644 index 00000000000..2e599b77311 --- /dev/null +++ b/src/lib/util/size.c @@ -0,0 +1,170 @@ +/* + * This program is 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** Size printing and parsing functions + * + * @file src/lib/util/size.c + * + * @copyright 2022 Arran Cudbard-Bell (a.cudbardb@freeradius.org) + */ +RCSID("$Id$") + +#include +#include + +#include "size.h" + +/** Parse a size string with optional unit + * + * Default scale with no suffix is bytes. + * + * @param[out] out Parsed and scaled size + * @param[in] in sbuff to parse. + * @return + * - >0 on success. + * - <0 on error. + */ +fr_slen_t fr_size_from_str(size_t *out, fr_sbuff_t *in) +{ + fr_sbuff_t our_in = FR_SBUFF(in); + char c = '\0'; + uint64_t size; + + *out = 0; + + if (fr_sbuff_out(NULL, &size, &our_in) < 0) return fr_sbuff_error(&our_in); + c = tolower(*fr_sbuff_current(&our_in)); + switch (c) { + case 'n': /* nibble */ + if (size & 0x01) { + fr_strerror_const("Sizes specified in nibbles must be an even number"); + fr_sbuff_set_to_start(&our_in); + return fr_sbuff_error(&our_in); + } + size /= 2; + break; + + case '\0': + case 'b': /* byte */ + break; + + case 'k': /* kibibyte */ + if (!fr_multiply(&size, size, 1024)) { + overflow: + fr_strerror_printf("Value must be less than %zu", (size_t)SIZE_MAX); + fr_sbuff_set_to_start(&our_in); + return fr_sbuff_error(&our_in); + } + (void)fr_sbuff_next_if_char(&our_in, 'b'); + break; + + case 'm': /* mebibyte */ + if (!fr_multiply(&size, size, ((uint64_t)1024 * 1024))) goto overflow; + (void)fr_sbuff_next_if_char(&our_in, 'b'); + break; + + case 'g': /* gibibyte */ + if (!fr_multiply(&size, size, ((uint64_t)1024 * 1024 * 1024))) goto overflow; + (void)fr_sbuff_next_if_char(&our_in, 'b'); + break; + + case 't': /* tebibyte */ + if (!fr_multiply(&size, size, ((uint64_t)1024 * 1024 * 1024 * 1024))) goto overflow; + (void)fr_sbuff_next_if_char(&our_in, 'b'); + break; + + case 'p': /* pebibyte */ + if (!fr_multiply(&size, size, ((uint64_t)1024 * 1024 * 1024 * 1024 * 1024))) goto overflow; + (void)fr_sbuff_next_if_char(&our_in, 'b'); + break; + + case 'e': /* ebibyte */ + if (!fr_multiply(&size, size, ((uint64_t)1024 * 1024 * 1024 * 1024 * 1024 * 1024))) goto overflow; + (void)fr_sbuff_next_if_char(&our_in, 'b'); + break; + + default: + fr_strerror_printf("Unknown unit '%c'", c); + return fr_sbuff_error(&our_in); + } + + if (size > SIZE_MAX) { + fr_strerror_printf("Value %" PRIu64 " is greater than the maximum " + "file/memory size of this system (%zu)", size, (size_t)SIZE_MAX); + + goto overflow; + } + + *out = (size_t)size; + + return fr_sbuff_set(in, &our_in); +} + +/** Print a size string with unit + * + * Suffix is the largest unit possible without losing precision. + * + * @param[out] out To write size to. + * @param[in] in size to print. + * @return + * - >0 on success. + * - <0 on error. + */ +fr_slen_t fr_size_to_str(fr_sbuff_t *out, size_t in) +{ + fr_sbuff_t our_out = FR_SBUFF(out); + uint8_t pos = fr_low_bit_pos((uint64_t)in); + + /* + * Precision is greater than a kb (byte) + */ + if (pos <= 10) { + FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%zub", in); + /* + * Precision is greater than a mb (kibibyte) + */ + } else if (pos <= 20) { + FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%zuk", in / 1024); + /* + * Precision is greater than a gb (mebibyte) + */ + } else if (pos <= 30) { + FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%zum", in / ((uint64_t)1024 * 1024)); + /* + * Precision is greater than a tb (gibibyte) + */ + } else if (pos <= 40) { + FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%zug", in / ((uint64_t)1024 * 1024 * 1024)); + /* + * Precision is greater than a pb (tebibyte) + */ + } else if (pos <= 50) { + FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%zut", in / ((uint64_t)1024 * 1024 * 1024 * 1024)); + /* + * Precision is greater than a eb (pebibyte) + */ + } else if (pos <= 60) { + FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%zup", in / ((uint64_t)1024 * 1024 * 1024 * 1024 * 1024)); + + /* + * Precision is greater than a zb (exibyte) + */ + } else { + FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%zue", in / ((uint64_t)1024 * 1024 * 1024 * 1024 * 1024 * 1024)); + } + + return fr_sbuff_set(out, &our_out); +} diff --git a/src/lib/util/size.h b/src/lib/util/size.h new file mode 100644 index 00000000000..a337215ea47 --- /dev/null +++ b/src/lib/util/size.h @@ -0,0 +1,29 @@ +#pragma once +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** Boxed value structures and functions to manipulate them + * + * @file src/lib/util/size.h + * + * @copyright 2022 Arran Cudbard-Bell (a.cudbardb@freeradius.org) + */ +RCSIDH(size_h, "$Id$") + + +fr_slen_t fr_size_from_str(size_t *out, fr_sbuff_t *in); + +fr_slen_t fr_size_to_str(fr_sbuff_t *out, size_t in); diff --git a/src/lib/util/value.c b/src/lib/util/value.c index 26f0c40536d..a38278ba75d 100644 --- a/src/lib/util/value.c +++ b/src/lib/util/value.c @@ -52,6 +52,7 @@ RCSID("$Id$") #include #include #include +#include #include #include @@ -4808,6 +4809,10 @@ parse: case FR_TYPE_FLOAT64: return fr_value_box_from_numeric_substr(dst, dst_type, dst_enumv, in, rules, tainted); + case FR_TYPE_SIZE: + if (fr_size_from_str(&dst->datum.size, &our_in) < 0) return -1; + goto finish; + case FR_TYPE_BOOL: fr_value_box_init(dst, dst_type, dst_enumv, tainted); @@ -4960,10 +4965,6 @@ parse: buffer[fr_sbuff_remaining(in)] = '\0'; switch (dst_type) { - case FR_TYPE_SIZE: - if (fr_size_from_str(&dst->datum.size, buffer) < 0) return -1; - break; - case FR_TYPE_DATE: { if (dst_enumv) {