Tor  0.4.5.0-alpha-dev
unitparse.c
Go to the documentation of this file.
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-2020, 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 config_parse_units(const char *val, const unit_table_t *u, int *ok,
128  char **errmsg_out)
129 {
130  uint64_t v = 0;
131  double d = 0;
132  int use_float = 0;
133  char *cp;
134  char *errmsg = NULL;
135 
136  tor_assert(ok);
137 
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);
141  if (!*ok) {
142  tor_asprintf(&errmsg, "Unable to parse %s as a number", val);
143  goto done;
144  }
145  use_float = 1;
146  }
147 
148  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  cp = (char*) eat_whitespace(cp);
159 
160  for ( ;u->unit;++u) {
161  if (!strcasecmp(u->unit, cp)) {
162  if (use_float) {
163  d = u->multiplier * d;
164 
165  if (d < 0) {
166  tor_asprintf(&errmsg, "Got a negative value while parsing %s %s",
167  val, u->unit);
168  *ok = 0;
169  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  if (d >= 0 && (d > (double)INT64_MAX || (uint64_t)d > INT64_MAX)) {
175  tor_asprintf(&errmsg, "Overflow while parsing %s %s",
176  val, u->unit);
177  *ok = 0;
178  goto done;
179  }
180 
181  v = (uint64_t) d;
182  } else {
183  v = tor_mul_u64_nowrap(v, u->multiplier);
184 
185  if (v > INT64_MAX) {
186  tor_asprintf(&errmsg, "Overflow while parsing %s %s",
187  val, u->unit);
188  *ok = 0;
189  goto done;
190  }
191  }
192 
193  *ok = 1;
194  goto done;
195  }
196  }
197  tor_asprintf(&errmsg, "Unknown unit in %s", val);
198  *ok = 0;
199  done:
200 
201  if (errmsg) {
202  tor_assert_nonfatal(!*ok);
203  if (errmsg_out) {
204  *errmsg_out = errmsg;
205  } else {
206  log_warn(LD_CONFIG, "%s", errmsg);
207  tor_free(errmsg);
208  }
209  }
210 
211  if (*ok)
212  return v;
213  else
214  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 config_parse_memunit(const char *s, int *ok)
223 {
224  uint64_t u = config_parse_units(s, memory_units, ok, NULL);
225  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 config_parse_msec_interval(const char *s, int *ok)
234 {
235  uint64_t r;
236  r = config_parse_units(s, time_msec_units, ok, NULL);
237  if (r > INT_MAX) {
238  log_warn(LD_CONFIG, "Msec interval '%s' is too long", s);
239  *ok = 0;
240  return -1;
241  }
242  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 config_parse_interval(const char *s, int *ok)
251 {
252  uint64_t r;
253  r = config_parse_units(s, time_units, ok, NULL);
254  if (r > INT_MAX) {
255  log_warn(LD_CONFIG, "Interval '%s' is too long", s);
256  *ok = 0;
257  return -1;
258  }
259  return (int)r;
260 }
tor_free
#define tor_free(p)
Definition: malloc.h:52
config_parse_units
uint64_t config_parse_units(const char *val, const unit_table_t *u, int *ok, char **errmsg_out)
Definition: unitparse.c:127
config_parse_msec_interval
int config_parse_msec_interval(const char *s, int *ok)
Definition: unitparse.c:233
parse_int.h
Header for parse_int.c.
unit_table_t::multiplier
uint64_t multiplier
Definition: unitparse.h:21
tor_assert
#define tor_assert(expr)
Definition: util_bug.h:102
config_parse_interval
int config_parse_interval(const char *s, int *ok)
Definition: unitparse.c:250
tor_parse_uint64
uint64_t tor_parse_uint64(const char *s, int base, uint64_t min, uint64_t max, int *ok, char **next)
Definition: parse_int.c:110
tor_mul_u64_nowrap
uint64_t tor_mul_u64_nowrap(uint64_t a, uint64_t b)
Definition: muldiv.c:75
util_bug.h
Macros to manage assertions, fatal and non-fatal.
time_units
const struct unit_table_t time_units[]
Definition: unitparse.c:76
muldiv.h
Header for muldiv.c.
printf.h
Header for printf.c.
LD_CONFIG
#define LD_CONFIG
Definition: log.h:68
malloc.h
Headers for util_malloc.c.
unit_table_t::unit
const char * unit
Definition: unitparse.h:20
memory_units
const struct unit_table_t memory_units[]
Definition: unitparse.c:27
log.h
Headers for log.c.
tor_asprintf
int tor_asprintf(char **strp, const char *fmt,...)
Definition: printf.c:75
time_msec_units
const struct unit_table_t time_msec_units[]
Definition: unitparse.c:97
tor_parse_double
double tor_parse_double(const char *s, double min, double max, int *ok, char **next)
Definition: parse_int.c:97
unit_table_t
Definition: unitparse.h:19
unitparse.h
Header for lib/confmgt/unitparse.c.
config_parse_memunit
uint64_t config_parse_memunit(const char *s, int *ok)
Definition: unitparse.c:222
util_string.h
Header for util_string.c.
eat_whitespace
const char * eat_whitespace(const char *s)
Definition: util_string.c:277