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 */
9 * @brief Functions for parsing values with units from a configuration file.
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"
24 /** Table to map the names of memory units to the number of bytes they
27 const struct unit_table_t memory_units
[] = {
35 { "kilobyte", 1<<10 },
36 { "kilobytes", 1<<10 },
45 { "megabyte", 1<<20 },
46 { "megabytes", 1<<20 },
47 { "megabits", 1<<17 },
54 { "gigabyte", 1<<30 },
55 { "gigabytes", 1<<30 },
56 { "gigabits", 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 },
73 /** Table to map the names of time units to the number of seconds they
76 const struct unit_table_t time_units
[] = {
86 { "week", 7*24*60*60 },
87 { "weeks", 7*24*60*60 },
88 { "month", 2629728, }, /* about 30.437 days */
89 { "months", 2629728, },
94 /** Table to map the names of time units to the number of milliseconds
97 const struct unit_table_t time_msec_units
[] = {
100 { "millisecond", 1 },
101 { "milliseconds", 1 },
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 },
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.
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
127 config_parse_units(const char *val
, const unit_table_t
*u
, int *ok
,
138 v
= tor_parse_uint64(val
, 10, 0, UINT64_MAX
, ok
, &cp
);
139 if (!*ok
|| (cp
&& *cp
== '.')) {
140 d
= tor_parse_double(val
, 0, (double)UINT64_MAX
, ok
, &cp
);
142 tor_asprintf(&errmsg
, "Unable to parse %s as a number", val
);
149 // cp should always be non-NULL if the parse operation succeeds.
153 v
= use_float
? ((uint64_t)d
) : v
;
158 cp
= (char*) eat_whitespace(cp
);
160 for ( ;u
->unit
;++u
) {
161 if (!strcasecmp(u
->unit
, cp
)) {
163 d
= u
->multiplier
* d
;
166 tor_asprintf(&errmsg
, "Got a negative value while parsing %s %s",
172 // Some compilers may warn about casting a double to an unsigned type
173 // because they don't know if d is >= 0
174 if (d
>= 0 && (d
> (double)INT64_MAX
|| (uint64_t)d
> INT64_MAX
)) {
175 tor_asprintf(&errmsg
, "Overflow while parsing %s %s",
183 v
= tor_mul_u64_nowrap(v
, u
->multiplier
);
186 tor_asprintf(&errmsg
, "Overflow while parsing %s %s",
197 tor_asprintf(&errmsg
, "Unknown unit in %s", val
);
202 tor_assert_nonfatal(!*ok
);
204 *errmsg_out
= errmsg
;
206 log_warn(LD_CONFIG
, "%s", errmsg
);
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. */
222 config_parse_memunit(const char *s
, int *ok
)
224 uint64_t u
= config_parse_units(s
, memory_units
, ok
, NULL
);
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. */
233 config_parse_msec_interval(const char *s
, int *ok
)
236 r
= config_parse_units(s
, time_msec_units
, ok
, NULL
);
238 log_warn(LD_CONFIG
, "Msec interval '%s' is too long", s
);
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.
250 config_parse_interval(const char *s
, int *ok
)
253 r
= config_parse_units(s
, time_units
, ok
, NULL
);
255 log_warn(LD_CONFIG
, "Interval '%s' is too long", s
);