LCOV - code coverage report
Current view: top level - lib/confmgt - unitparse.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 63 66 95.5 %
Date: 2021-11-24 03:28:48 Functions: 4 4 100.0 %

          Line data    Source code
       1             : /* Copyright (c) 2001 Matej Pfajfar.
       2             :  * Copyright (c) 2001-2004, Roger Dingledine.
       3             :  * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
       4             :  * Copyright (c) 2007-2021, The Tor Project, Inc. */
       5             : /* See LICENSE for licensing information */
       6             : 
       7             : /**
       8             :  * @file unitparse.c
       9             :  * @brief Functions for parsing values with units from a configuration file.
      10             :  **/
      11             : 
      12             : #include "orconfig.h"
      13             : #include "lib/confmgt/unitparse.h"
      14             : #include "lib/log/log.h"
      15             : #include "lib/log/util_bug.h"
      16             : #include "lib/malloc/malloc.h"
      17             : #include "lib/string/parse_int.h"
      18             : #include "lib/string/printf.h"
      19             : #include "lib/string/util_string.h"
      20             : #include "lib/intmath/muldiv.h"
      21             : 
      22             : #include <string.h>
      23             : 
      24             : /** Table to map the names of memory units to the number of bytes they
      25             :  * contain. */
      26             : // clang-format off
      27             : const struct unit_table_t memory_units[] = {
      28             :   { "",          1 },
      29             :   { "b",         1<< 0 },
      30             :   { "byte",      1<< 0 },
      31             :   { "bytes",     1<< 0 },
      32             :   { "kb",        1<<10 },
      33             :   { "kbyte",     1<<10 },
      34             :   { "kbytes",    1<<10 },
      35             :   { "kilobyte",  1<<10 },
      36             :   { "kilobytes", 1<<10 },
      37             :   { "kilobits",  1<<7  },
      38             :   { "kilobit",   1<<7  },
      39             :   { "kbits",     1<<7  },
      40             :   { "kbit",      1<<7  },
      41             :   { "m",         1<<20 },
      42             :   { "mb",        1<<20 },
      43             :   { "mbyte",     1<<20 },
      44             :   { "mbytes",    1<<20 },
      45             :   { "megabyte",  1<<20 },
      46             :   { "megabytes", 1<<20 },
      47             :   { "megabits",  1<<17 },
      48             :   { "megabit",   1<<17 },
      49             :   { "mbits",     1<<17 },
      50             :   { "mbit",      1<<17 },
      51             :   { "gb",        1<<30 },
      52             :   { "gbyte",     1<<30 },
      53             :   { "gbytes",    1<<30 },
      54             :   { "gigabyte",  1<<30 },
      55             :   { "gigabytes", 1<<30 },
      56             :   { "gigabits",  1<<27 },
      57             :   { "gigabit",   1<<27 },
      58             :   { "gbits",     1<<27 },
      59             :   { "gbit",      1<<27 },
      60             :   { "tb",        UINT64_C(1)<<40 },
      61             :   { "tbyte",     UINT64_C(1)<<40 },
      62             :   { "tbytes",    UINT64_C(1)<<40 },
      63             :   { "terabyte",  UINT64_C(1)<<40 },
      64             :   { "terabytes", UINT64_C(1)<<40 },
      65             :   { "terabits",  UINT64_C(1)<<37 },
      66             :   { "terabit",   UINT64_C(1)<<37 },
      67             :   { "tbits",     UINT64_C(1)<<37 },
      68             :   { "tbit",      UINT64_C(1)<<37 },
      69             :   { NULL, 0 },
      70             : };
      71             : // clang-format on
      72             : 
      73             : /** Table to map the names of time units to the number of seconds they
      74             :  * contain. */
      75             : // clang-format off
      76             : const struct unit_table_t time_units[] = {
      77             :   { "",         1 },
      78             :   { "second",   1 },
      79             :   { "seconds",  1 },
      80             :   { "minute",   60 },
      81             :   { "minutes",  60 },
      82             :   { "hour",     60*60 },
      83             :   { "hours",    60*60 },
      84             :   { "day",      24*60*60 },
      85             :   { "days",     24*60*60 },
      86             :   { "week",     7*24*60*60 },
      87             :   { "weeks",    7*24*60*60 },
      88             :   { "month",    2629728, }, /* about 30.437 days */
      89             :   { "months",   2629728, },
      90             :   { NULL, 0 },
      91             : };
      92             : // clang-format on
      93             : 
      94             : /** Table to map the names of time units to the number of milliseconds
      95             :  * they contain. */
      96             : // clang-format off
      97             : const struct unit_table_t time_msec_units[] = {
      98             :   { "",         1 },
      99             :   { "msec",     1 },
     100             :   { "millisecond", 1 },
     101             :   { "milliseconds", 1 },
     102             :   { "second",   1000 },
     103             :   { "seconds",  1000 },
     104             :   { "minute",   60*1000 },
     105             :   { "minutes",  60*1000 },
     106             :   { "hour",     60*60*1000 },
     107             :   { "hours",    60*60*1000 },
     108             :   { "day",      24*60*60*1000 },
     109             :   { "days",     24*60*60*1000 },
     110             :   { "week",     7*24*60*60*1000 },
     111             :   { "weeks",    7*24*60*60*1000 },
     112             :   { NULL, 0 },
     113             : };
     114             : // clang-format on
     115             : 
     116             : /** Parse a string <b>val</b> containing a number, zero or more
     117             :  * spaces, and an optional unit string.  If the unit appears in the
     118             :  * table <b>u</b>, then multiply the number by the unit multiplier.
     119             :  * On success, set *<b>ok</b> to 1 and return this product.
     120             :  * Otherwise, set *<b>ok</b> to 0.
     121             :  *
     122             :  * If an error (like overflow or a negative value is detected), put an error
     123             :  * message in *<b>errmsg_out</b> if that pointer is non-NULL, and otherwise
     124             :  * log a warning.
     125             :  */
     126             : uint64_t
     127       75739 : config_parse_units(const char *val, const unit_table_t *u, int *ok,
     128             :                    char **errmsg_out)
     129             : {
     130       75739 :   uint64_t v = 0;
     131       75739 :   double d = 0;
     132       75739 :   int use_float = 0;
     133       75739 :   char *cp;
     134       75739 :   char *errmsg = NULL;
     135             : 
     136       75739 :   tor_assert(ok);
     137             : 
     138       75739 :   v = tor_parse_uint64(val, 10, 0, UINT64_MAX, ok, &cp);
     139       75739 :   if (!*ok || (cp && *cp == '.')) {
     140           9 :     d = tor_parse_double(val, 0, (double)UINT64_MAX, ok, &cp);
     141           9 :     if (!*ok) {
     142           2 :       tor_asprintf(&errmsg, "Unable to parse %s as a number", val);
     143           2 :       goto done;
     144             :     }
     145             :     use_float = 1;
     146             :   }
     147             : 
     148       75737 :   if (BUG(!cp)) {
     149             :     // cp should always be non-NULL if the parse operation succeeds.
     150             : 
     151             :     // LCOV_EXCL_START
     152             :     *ok = 1;
     153             :     v = use_float ? ((uint64_t)d) :  v;
     154             :     goto done;
     155             :     // LCOV_EXCL_STOP
     156             :   }
     157             : 
     158       75737 :   cp = (char*) eat_whitespace(cp);
     159             : 
     160      313810 :   for ( ;u->unit;++u) {
     161      313803 :     if (!strcasecmp(u->unit, cp)) {
     162       75730 :       if (use_float) {
     163           7 :         d = u->multiplier * d;
     164             : 
     165           7 :         if (d < 0) {
     166           0 :           tor_asprintf(&errmsg, "Got a negative value while parsing %s %s",
     167             :                        val, u->unit);
     168           0 :           *ok = 0;
     169           0 :           goto done;
     170             :         }
     171             : 
     172             :         // Some compilers may warn about casting a double to an unsigned type
     173             :         // because they don't know if d is >= 0
     174           7 :         if (d >= 0 && (d > (double)INT64_MAX || (uint64_t)d > INT64_MAX)) {
     175           2 :           tor_asprintf(&errmsg, "Overflow while parsing %s %s",
     176             :                        val, u->unit);
     177           2 :           *ok = 0;
     178           2 :           goto done;
     179             :         }
     180             : 
     181           5 :         v = (uint64_t) d;
     182             :       } else {
     183       75723 :         v = tor_mul_u64_nowrap(v, u->multiplier);
     184             : 
     185       75723 :         if (v > INT64_MAX) {
     186           1 :           tor_asprintf(&errmsg, "Overflow while parsing %s %s",
     187           1 :                        val, u->unit);
     188           1 :           *ok = 0;
     189           1 :           goto done;
     190             :         }
     191             :       }
     192             : 
     193       75727 :       *ok = 1;
     194       75727 :       goto done;
     195             :     }
     196             :   }
     197           7 :   tor_asprintf(&errmsg, "Unknown unit in %s", val);
     198           7 :   *ok = 0;
     199       75739 :  done:
     200             : 
     201       75739 :   if (errmsg) {
     202          12 :     tor_assert_nonfatal(!*ok);
     203          12 :     if (errmsg_out) {
     204           5 :       *errmsg_out = errmsg;
     205             :     } else {
     206           7 :       log_warn(LD_CONFIG, "%s", errmsg);
     207           7 :       tor_free(errmsg);
     208             :     }
     209             :   }
     210             : 
     211       75739 :   if (*ok)
     212             :     return v;
     213             :   else
     214          12 :     return 0;
     215             : }
     216             : 
     217             : /** Parse a string in the format "number unit", where unit is a unit of
     218             :  * information (byte, KB, M, etc).  On success, set *<b>ok</b> to true
     219             :  * and return the number of bytes specified.  Otherwise, set
     220             :  * *<b>ok</b> to false and return 0. */
     221             : uint64_t
     222           8 : config_parse_memunit(const char *s, int *ok)
     223             : {
     224           8 :   uint64_t u = config_parse_units(s, memory_units, ok, NULL);
     225           8 :   return u;
     226             : }
     227             : 
     228             : /** Parse a string in the format "number unit", where unit is a unit of
     229             :  * time in milliseconds.  On success, set *<b>ok</b> to true and return
     230             :  * the number of milliseconds in the provided interval.  Otherwise, set
     231             :  * *<b>ok</b> to 0 and return -1. */
     232             : int
     233           4 : config_parse_msec_interval(const char *s, int *ok)
     234             : {
     235           4 :   uint64_t r;
     236           4 :   r = config_parse_units(s, time_msec_units, ok, NULL);
     237           4 :   if (r > INT_MAX) {
     238           1 :     log_warn(LD_CONFIG, "Msec interval '%s' is too long", s);
     239           1 :     *ok = 0;
     240           1 :     return -1;
     241             :   }
     242           3 :   return (int)r;
     243             : }
     244             : 
     245             : /** Parse a string in the format "number unit", where unit is a unit of time.
     246             :  * On success, set *<b>ok</b> to true and return the number of seconds in
     247             :  * the provided interval.  Otherwise, set *<b>ok</b> to 0 and return -1.
     248             :  */
     249             : int
     250           7 : config_parse_interval(const char *s, int *ok)
     251             : {
     252           7 :   uint64_t r;
     253           7 :   r = config_parse_units(s, time_units, ok, NULL);
     254           7 :   if (r > INT_MAX) {
     255           1 :     log_warn(LD_CONFIG, "Interval '%s' is too long", s);
     256           1 :     *ok = 0;
     257           1 :     return -1;
     258             :   }
     259           6 :   return (int)r;
     260             : }

Generated by: LCOV version 1.14