]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Add size printing/parsing
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Wed, 8 Jun 2022 16:09:54 +0000 (12:09 -0400)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Wed, 15 Jun 2022 16:15:33 +0000 (11:15 -0500)
src/bin/radiusd.c
src/lib/server/cf_parse.c
src/lib/util/libfreeradius-util.mk
src/lib/util/misc.c
src/lib/util/misc.h
src/lib/util/size.c [new file with mode: 0644]
src/lib/util/size.h [new file with mode: 0644]
src/lib/util/value.c

index a9e502a2d57f789d1982be175276b87404a9fe40..73927c5e4aca89f3823ba97391ba5657ae72f966 100644 (file)
@@ -33,10 +33,11 @@ RCSID("$Id$")
 #include <freeradius-devel/server/dependency.h>
 #include <freeradius-devel/server/map_proc.h>
 #include <freeradius-devel/server/module.h>
-#include <freeradius-devel/util/debug.h>
 #include <freeradius-devel/server/radmin.h>
 #include <freeradius-devel/server/state.h>
 #include <freeradius-devel/server/virtual_servers.h>
+#include <freeradius-devel/util/debug.h>
+#include <freeradius-devel/util/size.h>
 
 #include <freeradius-devel/tls/base.h>
 #include <freeradius-devel/tls/log.h>
@@ -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;
                        }
index 3a1f273b32dafbb6daa1475c43ea1da358e380f8..b1074d5c9b736659ab3dadba579e8472f8a08af0 100644 (file)
@@ -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;
                }
index bfd577066a2ff451e9c9fe1861fe99c1c2da2848..20924df0b3fa2fb5f8dd8b8168cb42c28ba0c258 100644 (file)
@@ -68,6 +68,7 @@ SOURCES               := \
                   sbuff.c \
                   sem.c \
                   sha1.c \
+                  size.c \
                   snprintf.c \
                   socket.c \
                   strerror.c \
index 3047358b8860780fa5c28392e9da9d3e752e6490..c00ffb382dbc4bc5bf88e5746b1282d6400d4c9d 100644 (file)
@@ -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
  *
index ed8cf29636b6a6e8e5868e09b1cfb369ddd0b017..0ccb4299e4b2329d146a2f1b9912b089d4204543 100644 (file)
@@ -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 (file)
index 0000000..2e599b7
--- /dev/null
@@ -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 <freeradius-devel/util/misc.h>
+#include <freeradius-devel/util/sbuff.h>
+
+#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 (file)
index 0000000..a337215
--- /dev/null
@@ -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);
index 26f0c40536dc91e2df26c18fb16b19b711170ee7..a38278ba75de1ce31312bb74005dafe4d0b19f46 100644 (file)
@@ -52,6 +52,7 @@ RCSID("$Id$")
 #include <freeradius-devel/util/atexit.h>
 #include <freeradius-devel/util/base16.h>
 #include <freeradius-devel/util/dcursor.h>
+#include <freeradius-devel/util/size.h>
 #include <freeradius-devel/util/time.h>
 
 #include <math.h>
@@ -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) {