Tor  0.4.4.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/string/parse_int.h"
17 #include "lib/string/util_string.h"
18 #include "lib/intmath/muldiv.h"
19 
20 #include <string.h>
21 
22 /** Table to map the names of memory units to the number of bytes they
23  * contain. */
24 const struct unit_table_t memory_units[] = {
25  { "", 1 },
26  { "b", 1<< 0 },
27  { "byte", 1<< 0 },
28  { "bytes", 1<< 0 },
29  { "kb", 1<<10 },
30  { "kbyte", 1<<10 },
31  { "kbytes", 1<<10 },
32  { "kilobyte", 1<<10 },
33  { "kilobytes", 1<<10 },
34  { "kilobits", 1<<7 },
35  { "kilobit", 1<<7 },
36  { "kbits", 1<<7 },
37  { "kbit", 1<<7 },
38  { "m", 1<<20 },
39  { "mb", 1<<20 },
40  { "mbyte", 1<<20 },
41  { "mbytes", 1<<20 },
42  { "megabyte", 1<<20 },
43  { "megabytes", 1<<20 },
44  { "megabits", 1<<17 },
45  { "megabit", 1<<17 },
46  { "mbits", 1<<17 },
47  { "mbit", 1<<17 },
48  { "gb", 1<<30 },
49  { "gbyte", 1<<30 },
50  { "gbytes", 1<<30 },
51  { "gigabyte", 1<<30 },
52  { "gigabytes", 1<<30 },
53  { "gigabits", 1<<27 },
54  { "gigabit", 1<<27 },
55  { "gbits", 1<<27 },
56  { "gbit", 1<<27 },
57  { "tb", UINT64_C(1)<<40 },
58  { "tbyte", UINT64_C(1)<<40 },
59  { "tbytes", UINT64_C(1)<<40 },
60  { "terabyte", UINT64_C(1)<<40 },
61  { "terabytes", UINT64_C(1)<<40 },
62  { "terabits", UINT64_C(1)<<37 },
63  { "terabit", UINT64_C(1)<<37 },
64  { "tbits", UINT64_C(1)<<37 },
65  { "tbit", UINT64_C(1)<<37 },
66  { NULL, 0 },
67 };
68 
69 /** Table to map the names of time units to the number of seconds they
70  * contain. */
71 const struct unit_table_t time_units[] = {
72  { "", 1 },
73  { "second", 1 },
74  { "seconds", 1 },
75  { "minute", 60 },
76  { "minutes", 60 },
77  { "hour", 60*60 },
78  { "hours", 60*60 },
79  { "day", 24*60*60 },
80  { "days", 24*60*60 },
81  { "week", 7*24*60*60 },
82  { "weeks", 7*24*60*60 },
83  { "month", 2629728, }, /* about 30.437 days */
84  { "months", 2629728, },
85  { NULL, 0 },
86 };
87 
88 /** Table to map the names of time units to the number of milliseconds
89  * they contain. */
90 const struct unit_table_t time_msec_units[] = {
91  { "", 1 },
92  { "msec", 1 },
93  { "millisecond", 1 },
94  { "milliseconds", 1 },
95  { "second", 1000 },
96  { "seconds", 1000 },
97  { "minute", 60*1000 },
98  { "minutes", 60*1000 },
99  { "hour", 60*60*1000 },
100  { "hours", 60*60*1000 },
101  { "day", 24*60*60*1000 },
102  { "days", 24*60*60*1000 },
103  { "week", 7*24*60*60*1000 },
104  { "weeks", 7*24*60*60*1000 },
105  { NULL, 0 },
106 };
107 
108 /** Parse a string <b>val</b> containing a number, zero or more
109  * spaces, and an optional unit string. If the unit appears in the
110  * table <b>u</b>, then multiply the number by the unit multiplier.
111  * On success, set *<b>ok</b> to 1 and return this product.
112  * Otherwise, set *<b>ok</b> to 0.
113  * Warns user when overflow or a negative value is detected.
114  */
115 uint64_t
116 config_parse_units(const char *val, const unit_table_t *u, int *ok)
117 {
118  uint64_t v = 0;
119  double d = 0;
120  int use_float = 0;
121  char *cp;
122 
123  tor_assert(ok);
124 
125  v = tor_parse_uint64(val, 10, 0, UINT64_MAX, ok, &cp);
126  if (!*ok || (cp && *cp == '.')) {
127  d = tor_parse_double(val, 0, (double)UINT64_MAX, ok, &cp);
128  if (!*ok)
129  goto done;
130  use_float = 1;
131  }
132 
133  if (BUG(!cp)) {
134  // cp should always be non-NULL if the parse operation succeeds.
135 
136  // LCOV_EXCL_START
137  *ok = 1;
138  v = use_float ? ((uint64_t)d) : v;
139  goto done;
140  // LCOV_EXCL_STOP
141  }
142 
143  cp = (char*) eat_whitespace(cp);
144 
145  for ( ;u->unit;++u) {
146  if (!strcasecmp(u->unit, cp)) {
147  if (use_float) {
148  d = u->multiplier * d;
149 
150  if (d < 0) {
151  log_warn(LD_CONFIG, "Got a negative value while parsing %s %s",
152  val, u->unit);
153  *ok = 0;
154  goto done;
155  }
156 
157  // Some compilers may warn about casting a double to an unsigned type
158  // because they don't know if d is >= 0
159  if (d >= 0 && (d > (double)INT64_MAX || (uint64_t)d > INT64_MAX)) {
160  log_warn(LD_CONFIG, "Overflow detected while parsing %s %s",
161  val, u->unit);
162  *ok = 0;
163  goto done;
164  }
165 
166  v = (uint64_t) d;
167  } else {
168  v = tor_mul_u64_nowrap(v, u->multiplier);
169 
170  if (v > INT64_MAX) {
171  log_warn(LD_CONFIG, "Overflow detected while parsing %s %s",
172  val, u->unit);
173  *ok = 0;
174  goto done;
175  }
176  }
177 
178  *ok = 1;
179  goto done;
180  }
181  }
182  log_warn(LD_CONFIG, "Unknown unit '%s'.", cp);
183  *ok = 0;
184  done:
185 
186  if (*ok)
187  return v;
188  else
189  return 0;
190 }
191 
192 /** Parse a string in the format "number unit", where unit is a unit of
193  * information (byte, KB, M, etc). On success, set *<b>ok</b> to true
194  * and return the number of bytes specified. Otherwise, set
195  * *<b>ok</b> to false and return 0. */
196 uint64_t
197 config_parse_memunit(const char *s, int *ok)
198 {
199  uint64_t u = config_parse_units(s, memory_units, ok);
200  return u;
201 }
202 
203 /** Parse a string in the format "number unit", where unit is a unit of
204  * time in milliseconds. On success, set *<b>ok</b> to true and return
205  * the number of milliseconds in the provided interval. Otherwise, set
206  * *<b>ok</b> to 0 and return -1. */
207 int
208 config_parse_msec_interval(const char *s, int *ok)
209 {
210  uint64_t r;
212  if (r > INT_MAX) {
213  log_warn(LD_CONFIG, "Msec interval '%s' is too long", s);
214  *ok = 0;
215  return -1;
216  }
217  return (int)r;
218 }
219 
220 /** Parse a string in the format "number unit", where unit is a unit of time.
221  * On success, set *<b>ok</b> to true and return the number of seconds in
222  * the provided interval. Otherwise, set *<b>ok</b> to 0 and return -1.
223  */
224 int
225 config_parse_interval(const char *s, int *ok)
226 {
227  uint64_t r;
228  r = config_parse_units(s, time_units, ok);
229  if (r > INT_MAX) {
230  log_warn(LD_CONFIG, "Interval '%s' is too long", s);
231  *ok = 0;
232  return -1;
233  }
234  return (int)r;
235 }
Header for lib/confmgt/unitparse.c.
const struct unit_table_t memory_units[]
Definition: unitparse.c:24
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
int config_parse_interval(const char *s, int *ok)
Definition: unitparse.c:225
const struct unit_table_t time_msec_units[]
Definition: unitparse.c:90
#define tor_assert(expr)
Definition: util_bug.h:102
const char * unit
Definition: unitparse.h:20
Header for util_string.c.
int config_parse_msec_interval(const char *s, int *ok)
Definition: unitparse.c:208
const char * eat_whitespace(const char *s)
Definition: util_string.c:268
Header for muldiv.c.
uint64_t config_parse_units(const char *val, const unit_table_t *u, int *ok)
Definition: unitparse.c:116
double tor_parse_double(const char *s, double min, double max, int *ok, char **next)
Definition: parse_int.c:97
uint64_t config_parse_memunit(const char *s, int *ok)
Definition: unitparse.c:197
uint64_t multiplier
Definition: unitparse.h:21
const struct unit_table_t time_units[]
Definition: unitparse.c:71
uint64_t tor_mul_u64_nowrap(uint64_t a, uint64_t b)
Definition: muldiv.c:75
Headers for log.c.
Macros to manage assertions, fatal and non-fatal.
Header for parse_int.c.
#define LD_CONFIG
Definition: log.h:68