]> git.ipfire.org Git - thirdparty/chrony.git/blame - util.c
cmdmon: save NTS cookies and server keys on dump command
[thirdparty/chrony.git] / util.c
CommitLineData
88840341 1/*
88840341
RC
2 chronyd/chronyc - Programs for keeping computer clocks accurate.
3
4 **********************************************************************
6672f045 5 * Copyright (C) Richard P. Curnow 1997-2003
5dc86c23 6 * Copyright (C) Miroslav Lichvar 2009, 2012-2018
88840341
RC
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of version 2 of the GNU General Public License as
10 * published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
8e23110a 19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
88840341
RC
20 *
21 **********************************************************************
22
23 =======================================================================
24
25 Various utility functions
26 */
27
da2c8d90
ML
28#include "config.h"
29
88840341
RC
30#include "sysincl.h"
31
236576c1 32#include "logging.h"
92706b15 33#include "memory.h"
88840341 34#include "util.h"
777303f1 35#include "hash.h"
88840341 36
d0dfa1de
ML
37#define NSEC_PER_SEC 1000000000
38
39/* ================================================== */
40
41void
42UTI_ZeroTimespec(struct timespec *ts)
43{
44 ts->tv_sec = 0;
45 ts->tv_nsec = 0;
46}
47
48/* ================================================== */
49
c8373f16
ML
50int
51UTI_IsZeroTimespec(struct timespec *ts)
52{
53 return !ts->tv_sec && !ts->tv_nsec;
54}
55
56/* ================================================== */
57
d0dfa1de 58void
d3f42929 59UTI_TimevalToTimespec(const struct timeval *tv, struct timespec *ts)
d0dfa1de
ML
60{
61 ts->tv_sec = tv->tv_sec;
62 ts->tv_nsec = 1000 * tv->tv_usec;
63}
64
65/* ================================================== */
66
67void
d3f42929 68UTI_TimespecToTimeval(const struct timespec *ts, struct timeval *tv)
d0dfa1de
ML
69{
70 tv->tv_sec = ts->tv_sec;
71 tv->tv_usec = ts->tv_nsec / 1000;
72}
73
74/* ================================================== */
75
cfe706f0 76double
d3f42929 77UTI_TimespecToDouble(const struct timespec *ts)
d0dfa1de 78{
cfe706f0 79 return ts->tv_sec + 1.0e-9 * ts->tv_nsec;
d0dfa1de
ML
80}
81
82/* ================================================== */
83
84void
85UTI_DoubleToTimespec(double d, struct timespec *ts)
86{
87 ts->tv_sec = d;
88 ts->tv_nsec = 1.0e9 * (d - ts->tv_sec);
89 UTI_NormaliseTimespec(ts);
90}
91
92/* ================================================== */
93
94void
95UTI_NormaliseTimespec(struct timespec *ts)
96{
97 if (ts->tv_nsec >= NSEC_PER_SEC || ts->tv_nsec < 0) {
98 ts->tv_sec += ts->tv_nsec / NSEC_PER_SEC;
99 ts->tv_nsec = ts->tv_nsec % NSEC_PER_SEC;
100
101 /* If seconds are negative nanoseconds would end up negative too */
102 if (ts->tv_nsec < 0) {
103 ts->tv_sec--;
104 ts->tv_nsec += NSEC_PER_SEC;
105 }
106 }
107}
108
88840341
RC
109/* ================================================== */
110
cfe706f0 111double
d3f42929 112UTI_TimevalToDouble(const struct timeval *tv)
88840341 113{
cfe706f0 114 return tv->tv_sec + 1.0e-6 * tv->tv_usec;
88840341
RC
115}
116
117/* ================================================== */
118
82f7fa38 119void
88840341
RC
120UTI_DoubleToTimeval(double a, struct timeval *b)
121{
0fc0f906 122 double frac_part;
4bbc7686
ML
123
124 b->tv_sec = a;
125 frac_part = 1.0e6 * (a - b->tv_sec);
126 b->tv_usec = frac_part > 0 ? frac_part + 0.5 : frac_part - 0.5;
88840341
RC
127 UTI_NormaliseTimeval(b);
128}
129
130/* ================================================== */
131
82f7fa38 132void
88840341
RC
133UTI_NormaliseTimeval(struct timeval *x)
134{
bc0aaa92
JH
135 /* Reduce tv_usec to within +-1000000 of zero. JGH */
136 if ((x->tv_usec >= 1000000) || (x->tv_usec <= -1000000)) {
137 x->tv_sec += x->tv_usec/1000000;
138 x->tv_usec = x->tv_usec%1000000;
88840341
RC
139 }
140
bc0aaa92
JH
141 /* Make tv_usec positive. JGH */
142 if (x->tv_usec < 0) {
88840341
RC
143 --x->tv_sec;
144 x->tv_usec += 1000000;
bc0aaa92 145 }
88840341
RC
146
147}
148
149/* ================================================== */
150
d0dfa1de 151int
d3f42929 152UTI_CompareTimespecs(const struct timespec *a, const struct timespec *b)
88840341 153{
d0dfa1de
ML
154 if (a->tv_sec < b->tv_sec)
155 return -1;
156 if (a->tv_sec > b->tv_sec)
157 return 1;
158 if (a->tv_nsec < b->tv_nsec)
159 return -1;
160 if (a->tv_nsec > b->tv_nsec)
161 return 1;
162 return 0;
163}
88840341 164
d0dfa1de 165/* ================================================== */
88840341 166
d0dfa1de 167void
d3f42929 168UTI_DiffTimespecs(struct timespec *result, const struct timespec *a, const struct timespec *b)
d0dfa1de
ML
169{
170 result->tv_sec = a->tv_sec - b->tv_sec;
171 result->tv_nsec = a->tv_nsec - b->tv_nsec;
172 UTI_NormaliseTimespec(result);
88840341
RC
173}
174
175/* ================================================== */
176
177/* Calculate result = a - b and return as a double */
cfe706f0 178double
d3f42929 179UTI_DiffTimespecsToDouble(const struct timespec *a, const struct timespec *b)
88840341 180{
0c54cf31 181 return ((double)a->tv_sec - (double)b->tv_sec) + 1.0e-9 * (a->tv_nsec - b->tv_nsec);
88840341
RC
182}
183
184/* ================================================== */
185
82f7fa38 186void
d3f42929 187UTI_AddDoubleToTimespec(const struct timespec *start, double increment, struct timespec *end)
88840341 188{
d0dfa1de 189 time_t int_part;
88840341 190
d0dfa1de
ML
191 int_part = increment;
192 end->tv_sec = start->tv_sec + int_part;
193 end->tv_nsec = start->tv_nsec + 1.0e9 * (increment - int_part);
194 UTI_NormaliseTimespec(end);
88840341
RC
195}
196
197/* ================================================== */
198
d0dfa1de 199/* Calculate the average and difference (as a double) of two timespecs */
82f7fa38 200void
d3f42929 201UTI_AverageDiffTimespecs(const struct timespec *earlier, const struct timespec *later,
d0dfa1de 202 struct timespec *average, double *diff)
88840341 203{
cfe706f0 204 *diff = UTI_DiffTimespecsToDouble(later, earlier);
d0dfa1de
ML
205 UTI_AddDoubleToTimespec(earlier, *diff / 2.0, average);
206}
88840341
RC
207
208/* ================================================== */
209
8aa9eb19 210void
d3f42929
ML
211UTI_AddDiffToTimespec(const struct timespec *a, const struct timespec *b,
212 const struct timespec *c, struct timespec *result)
8aa9eb19
ML
213{
214 double diff;
215
cfe706f0 216 diff = UTI_DiffTimespecsToDouble(a, b);
d0dfa1de 217 UTI_AddDoubleToTimespec(c, diff, result);
8aa9eb19
ML
218}
219
220/* ================================================== */
221
88840341
RC
222#define POOL_ENTRIES 16
223#define BUFFER_LENGTH 64
224static char buffer_pool[POOL_ENTRIES][BUFFER_LENGTH];
225static int pool_ptr = 0;
226
227#define NEXT_BUFFER (buffer_pool[pool_ptr = ((pool_ptr + 1) % POOL_ENTRIES)])
228
229/* ================================================== */
d0dfa1de 230/* Convert a timespec into a temporary string, largely for diagnostic display */
88840341
RC
231
232char *
d3f42929 233UTI_TimespecToString(const struct timespec *ts)
88840341 234{
77a71623
ML
235 char *result;
236
88840341 237 result = NEXT_BUFFER;
fc2892fb 238#ifdef HAVE_LONG_TIME_T
d0dfa1de
ML
239 snprintf(result, BUFFER_LENGTH, "%"PRId64".%09lu",
240 (int64_t)ts->tv_sec, (unsigned long)ts->tv_nsec);
fc2892fb 241#else
d0dfa1de
ML
242 snprintf(result, BUFFER_LENGTH, "%ld.%09lu",
243 (long)ts->tv_sec, (unsigned long)ts->tv_nsec);
fc2892fb 244#endif
88840341
RC
245 return result;
246}
247
88840341
RC
248/* ================================================== */
249/* Convert an NTP timestamp into a temporary string, largely
250 for diagnostic display */
251
252char *
d3f42929 253UTI_Ntp64ToString(const NTP_int64 *ntp_ts)
88840341 254{
d0dfa1de 255 struct timespec ts;
99cc9452 256 UTI_Ntp64ToTimespec(ntp_ts, &ts);
d0dfa1de 257 return UTI_TimespecToString(&ts);
88840341
RC
258}
259
260/* ================================================== */
261
ac30bb06 262char *
91279a0f 263UTI_RefidToString(uint32_t ref_id)
ac30bb06 264{
a3288d42 265 unsigned int i, j, c;
078f0f51
ML
266 char *result;
267
268 result = NEXT_BUFFER;
a3288d42 269
078f0f51 270 for (i = j = 0; i < 4 && i < BUFFER_LENGTH - 1; i++) {
a3288d42
ML
271 c = (ref_id >> (24 - i * 8)) & 0xff;
272 if (isprint(c))
078f0f51 273 result[j++] = c;
a3288d42
ML
274 }
275
078f0f51 276 result[j] = '\0';
a3288d42 277
ac30bb06
ML
278 return result;
279}
280
281/* ================================================== */
282
8265ff28 283char *
d3f42929 284UTI_IPToString(const IPAddr *addr)
8265ff28
ML
285{
286 unsigned long a, b, c, d, ip;
d3f42929 287 const uint8_t *ip6;
8265ff28
ML
288 char *result;
289
290 result = NEXT_BUFFER;
291 switch (addr->family) {
292 case IPADDR_UNSPEC:
293 snprintf(result, BUFFER_LENGTH, "[UNSPEC]");
294 break;
295 case IPADDR_INET4:
296 ip = addr->addr.in4;
297 a = (ip>>24) & 0xff;
298 b = (ip>>16) & 0xff;
299 c = (ip>> 8) & 0xff;
300 d = (ip>> 0) & 0xff;
9bc774d6 301 snprintf(result, BUFFER_LENGTH, "%lu.%lu.%lu.%lu", a, b, c, d);
8265ff28
ML
302 break;
303 case IPADDR_INET6:
304 ip6 = addr->addr.in6;
285fae85 305#ifdef FEAT_IPV6
8265ff28
ML
306 inet_ntop(AF_INET6, ip6, result, BUFFER_LENGTH);
307#else
9bc774d6
ML
308 assert(BUFFER_LENGTH >= 40);
309 for (a = 0; a < 8; a++)
310 snprintf(result + a * 5, 40 - a * 5, "%04x:",
311 (unsigned int)(ip6[2 * a] << 8 | ip6[2 * a + 1]));
8265ff28
ML
312#endif
313 break;
84902d0e
ML
314 case IPADDR_ID:
315 snprintf(result, BUFFER_LENGTH, "ID#%010"PRIu32, addr->addr.id);
316 break;
8265ff28
ML
317 default:
318 snprintf(result, BUFFER_LENGTH, "[UNKNOWN]");
319 }
320 return result;
321}
322
323/* ================================================== */
324
325int
326UTI_StringToIP(const char *addr, IPAddr *ip)
327{
285fae85 328#ifdef FEAT_IPV6
8265ff28
ML
329 struct in_addr in4;
330 struct in6_addr in6;
331
332 if (inet_pton(AF_INET, addr, &in4) > 0) {
333 ip->family = IPADDR_INET4;
510b22e9 334 ip->_pad = 0;
8265ff28
ML
335 ip->addr.in4 = ntohl(in4.s_addr);
336 return 1;
337 }
338
339 if (inet_pton(AF_INET6, addr, &in6) > 0) {
340 ip->family = IPADDR_INET6;
510b22e9 341 ip->_pad = 0;
8265ff28
ML
342 memcpy(ip->addr.in6, in6.s6_addr, sizeof (ip->addr.in6));
343 return 1;
344 }
345#else
346 unsigned long a, b, c, d, n;
347
348 n = sscanf(addr, "%lu.%lu.%lu.%lu", &a, &b, &c, &d);
349 if (n == 4) {
350 ip->family = IPADDR_INET4;
510b22e9 351 ip->_pad = 0;
8265ff28
ML
352 ip->addr.in4 = ((a & 0xff) << 24) | ((b & 0xff) << 16) |
353 ((c & 0xff) << 8) | (d & 0xff);
354 return 1;
355 }
356#endif
357
358 return 0;
359}
360
361/* ================================================== */
362
84902d0e
ML
363int
364UTI_StringToIdIP(const char *addr, IPAddr *ip)
365{
366 if (sscanf(addr, "ID#%"SCNu32, &ip->addr.id) == 1) {
367 ip->family = IPADDR_ID;
368 ip->_pad = 0;
369 return 1;
370 }
371
372 return 0;
373}
374
375/* ================================================== */
376
377int
d3f42929 378UTI_IsIPReal(const IPAddr *ip)
84902d0e
ML
379{
380 switch (ip->family) {
381 case IPADDR_INET4:
382 case IPADDR_INET6:
383 return 1;
384 default:
385 return 0;
386 }
387}
388
389/* ================================================== */
390
91279a0f 391uint32_t
d3f42929 392UTI_IPToRefid(const IPAddr *ip)
8265ff28 393{
777303f1
ML
394 static int MD5_hash = -1;
395 unsigned char buf[16];
8265ff28
ML
396
397 switch (ip->family) {
398 case IPADDR_INET4:
399 return ip->addr.in4;
400 case IPADDR_INET6:
4da9f74d 401 if (MD5_hash < 0)
777303f1 402 MD5_hash = HSH_GetHashId("MD5");
4da9f74d
ML
403
404 if (MD5_hash < 0 ||
405 HSH_Hash(MD5_hash, (const unsigned char *)ip->addr.in6, sizeof (ip->addr.in6),
406 NULL, 0, buf, sizeof (buf)) != sizeof (buf))
407 LOG_FATAL("Could not get MD5");
408
8e71a461 409 return (uint32_t)buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
8265ff28
ML
410 }
411 return 0;
412}
413
414/* ================================================== */
415
8b235297 416uint32_t
d3f42929 417UTI_IPToHash(const IPAddr *ip)
8b235297 418{
b45f53dd 419 static uint32_t seed = 0;
d3f42929 420 const unsigned char *addr;
8b235297
ML
421 unsigned int i, len;
422 uint32_t hash;
423
424 switch (ip->family) {
425 case IPADDR_INET4:
426 addr = (unsigned char *)&ip->addr.in4;
427 len = sizeof (ip->addr.in4);
428 break;
429 case IPADDR_INET6:
430 addr = ip->addr.in6;
431 len = sizeof (ip->addr.in6);
432 break;
84902d0e
ML
433 case IPADDR_ID:
434 addr = (unsigned char *)&ip->addr.id;
435 len = sizeof (ip->addr.id);
436 break;
8b235297
ML
437 default:
438 return 0;
439 }
440
b45f53dd
ML
441 /* Include a random seed in the hash to randomize collisions
442 and order of addresses in hash tables */
443 while (!seed)
444 UTI_GetRandomBytes(&seed, sizeof (seed));
445
446 for (i = 0, hash = seed; i < len; i++)
8b235297
ML
447 hash = 71 * hash + addr[i];
448
b45f53dd 449 return hash + seed;
8b235297
ML
450}
451
452/* ================================================== */
453
8265ff28 454void
d3f42929 455UTI_IPHostToNetwork(const IPAddr *src, IPAddr *dest)
8265ff28
ML
456{
457 /* Don't send uninitialized bytes over network */
458 memset(dest, 0, sizeof (IPAddr));
459
460 dest->family = htons(src->family);
461
462 switch (src->family) {
463 case IPADDR_INET4:
464 dest->addr.in4 = htonl(src->addr.in4);
465 break;
466 case IPADDR_INET6:
467 memcpy(dest->addr.in6, src->addr.in6, sizeof (dest->addr.in6));
468 break;
84902d0e
ML
469 case IPADDR_ID:
470 dest->addr.id = htonl(src->addr.id);
471 break;
23cf74d5
ML
472 default:
473 dest->family = htons(IPADDR_UNSPEC);
8265ff28
ML
474 }
475}
476
477/* ================================================== */
478
479void
d3f42929 480UTI_IPNetworkToHost(const IPAddr *src, IPAddr *dest)
8265ff28
ML
481{
482 dest->family = ntohs(src->family);
510b22e9 483 dest->_pad = 0;
8265ff28
ML
484
485 switch (dest->family) {
486 case IPADDR_INET4:
487 dest->addr.in4 = ntohl(src->addr.in4);
488 break;
489 case IPADDR_INET6:
490 memcpy(dest->addr.in6, src->addr.in6, sizeof (dest->addr.in6));
491 break;
84902d0e
ML
492 case IPADDR_ID:
493 dest->addr.id = ntohl(src->addr.id);
494 break;
23cf74d5
ML
495 default:
496 dest->family = IPADDR_UNSPEC;
8265ff28
ML
497 }
498}
499
500/* ================================================== */
501
502int
d3f42929 503UTI_CompareIPs(const IPAddr *a, const IPAddr *b, const IPAddr *mask)
8265ff28
ML
504{
505 int i, d;
506
507 if (a->family != b->family)
508 return a->family - b->family;
509
510 if (mask && mask->family != b->family)
511 mask = NULL;
512
513 switch (a->family) {
514 case IPADDR_UNSPEC:
515 return 0;
516 case IPADDR_INET4:
517 if (mask)
518 return (a->addr.in4 & mask->addr.in4) - (b->addr.in4 & mask->addr.in4);
519 else
520 return a->addr.in4 - b->addr.in4;
521 case IPADDR_INET6:
522 for (i = 0, d = 0; !d && i < 16; i++) {
523 if (mask)
524 d = (a->addr.in6[i] & mask->addr.in6[i]) -
525 (b->addr.in6[i] & mask->addr.in6[i]);
526 else
527 d = a->addr.in6[i] - b->addr.in6[i];
528 }
529 return d;
84902d0e
ML
530 case IPADDR_ID:
531 return a->addr.id - b->addr.id;
8265ff28
ML
532 }
533 return 0;
534}
535
536/* ================================================== */
537
3f8c57c8 538char *
d3f42929 539UTI_IPSockAddrToString(const IPSockAddr *sa)
3f8c57c8
ML
540{
541 char *result;
542
543 result = NEXT_BUFFER;
544 snprintf(result, BUFFER_LENGTH,
545 sa->ip_addr.family != IPADDR_INET6 ? "%s:%hu" : "[%s]:%hu",
546 UTI_IPToString(&sa->ip_addr), sa->port);
547
548 return result;
549}
550
551/* ================================================== */
552
88840341
RC
553char *
554UTI_TimeToLogForm(time_t t)
555{
f40b0024 556 struct tm *stm;
88840341
RC
557 char *result;
558
559 result = NEXT_BUFFER;
560
f40b0024
ML
561 stm = gmtime(&t);
562
563 if (stm)
564 strftime(result, BUFFER_LENGTH, "%Y-%m-%d %H:%M:%S", stm);
565 else
566 snprintf(result, BUFFER_LENGTH, "INVALID INVALID ");
88840341
RC
567
568 return result;
569}
570
571/* ================================================== */
572
573void
d3f42929
ML
574UTI_AdjustTimespec(const struct timespec *old_ts, const struct timespec *when,
575 struct timespec *new_ts, double *delta_time, double dfreq, double doffset)
88840341 576{
40d82675 577 double elapsed;
88840341 578
cfe706f0 579 elapsed = UTI_DiffTimespecsToDouble(when, old_ts);
40d82675 580 *delta_time = elapsed * dfreq - doffset;
d0dfa1de 581 UTI_AddDoubleToTimespec(old_ts, *delta_time, new_ts);
88840341
RC
582}
583
584/* ================================================== */
585
116c6972 586void
99cc9452 587UTI_GetNtp64Fuzz(NTP_int64 *ts, int precision)
df6c2a43 588{
116c6972
ML
589 int start, bits;
590
591 assert(precision >= -32 && precision <= 32);
702db726 592 assert(sizeof (*ts) == 8);
116c6972
ML
593
594 start = sizeof (*ts) - (precision + 32 + 7) / 8;
595 ts->hi = ts->lo = 0;
df6c2a43 596
116c6972
ML
597 UTI_GetRandomBytes((unsigned char *)ts + start, sizeof (*ts) - start);
598
599 bits = (precision + 32) % 8;
600 if (bits)
601 ((unsigned char *)ts)[start] %= 1U << bits;
df6c2a43
ML
602}
603
604/* ================================================== */
605
2ceb3c89 606double
99cc9452 607UTI_Ntp32ToDouble(NTP_int32 x)
2ceb3c89
ML
608{
609 return (double) ntohl(x) / 65536.0;
610}
611
612/* ================================================== */
613
8eb7ce85
ML
614#define MAX_NTP_INT32 (4294967295.0 / 65536.0)
615
2ceb3c89 616NTP_int32
99cc9452 617UTI_DoubleToNtp32(double x)
2ceb3c89 618{
7ffe59a7
ML
619 NTP_int32 r;
620
621 if (x >= MAX_NTP_INT32) {
622 r = 0xffffffff;
623 } else if (x <= 0.0) {
624 r = 0;
625 } else {
626 x *= 65536.0;
627 r = x;
628
629 /* Round up */
630 if (r < x)
631 r++;
632 }
633
634 return htonl(r);
2ceb3c89
ML
635}
636
637/* ================================================== */
638
6e9c0489
ML
639void
640UTI_ZeroNtp64(NTP_int64 *ts)
641{
642 ts->hi = ts->lo = htonl(0);
643}
644
645/* ================================================== */
646
647int
d3f42929 648UTI_IsZeroNtp64(const NTP_int64 *ts)
6e9c0489
ML
649{
650 return !ts->hi && !ts->lo;
651}
652
653/* ================================================== */
654
655int
d3f42929 656UTI_CompareNtp64(const NTP_int64 *a, const NTP_int64 *b)
6e9c0489
ML
657{
658 int32_t diff;
659
660 if (a->hi == b->hi && a->lo == b->lo)
661 return 0;
662
663 diff = ntohl(a->hi) - ntohl(b->hi);
664
665 if (diff < 0)
666 return -1;
667 if (diff > 0)
668 return 1;
669
670 return ntohl(a->lo) < ntohl(b->lo) ? -1 : 1;
671}
672
673/* ================================================== */
674
2c7ab983 675int
d3f42929
ML
676UTI_IsEqualAnyNtp64(const NTP_int64 *a, const NTP_int64 *b1, const NTP_int64 *b2,
677 const NTP_int64 *b3)
2c7ab983
ML
678{
679 if (b1 && a->lo == b1->lo && a->hi == b1->hi)
680 return 1;
681
682 if (b2 && a->lo == b2->lo && a->hi == b2->hi)
683 return 1;
684
685 if (b3 && a->lo == b3->lo && a->hi == b3->hi)
686 return 1;
687
688 return 0;
689}
690
691/* ================================================== */
692
d0dfa1de 693/* Seconds part of NTP timestamp correponding to the origin of the time_t format */
88840341
RC
694#define JAN_1970 0x83aa7e80UL
695
d0dfa1de
ML
696#define NSEC_PER_NTP64 4.294967296
697
88840341 698void
d3f42929 699UTI_TimespecToNtp64(const struct timespec *src, NTP_int64 *dest, const NTP_int64 *fuzz)
88840341 700{
d0dfa1de 701 uint32_t hi, lo, sec, nsec;
474b2af1
ML
702
703 sec = (uint32_t)src->tv_sec;
d0dfa1de 704 nsec = (uint32_t)src->tv_nsec;
88840341
RC
705
706 /* Recognize zero as a special case - it always signifies
707 an 'unknown' value */
d0dfa1de 708 if (!nsec && !sec) {
116c6972 709 hi = lo = 0;
88840341 710 } else {
116c6972 711 hi = htonl(sec + JAN_1970);
d0dfa1de 712 lo = htonl(NSEC_PER_NTP64 * nsec);
df6c2a43
ML
713
714 /* Add the fuzz */
116c6972
ML
715 if (fuzz) {
716 hi ^= fuzz->hi;
717 lo ^= fuzz->lo;
718 }
88840341 719 }
116c6972
ML
720
721 dest->hi = hi;
722 dest->lo = lo;
88840341
RC
723}
724
725/* ================================================== */
726
727void
d3f42929 728UTI_Ntp64ToTimespec(const NTP_int64 *src, struct timespec *dest)
88840341 729{
474b2af1
ML
730 uint32_t ntp_sec, ntp_frac;
731
b0f7efd5
ML
732 /* Zero is a special value */
733 if (UTI_IsZeroNtp64(src)) {
734 UTI_ZeroTimespec(dest);
735 return;
736 }
88840341 737
474b2af1
ML
738 ntp_sec = ntohl(src->hi);
739 ntp_frac = ntohl(src->lo);
740
741#ifdef HAVE_LONG_TIME_T
742 dest->tv_sec = ntp_sec - (uint32_t)(NTP_ERA_SPLIT + JAN_1970) +
743 (time_t)NTP_ERA_SPLIT;
744#else
745 dest->tv_sec = ntp_sec - JAN_1970;
746#endif
0899ab52 747
0c54cf31 748 dest->tv_nsec = ntp_frac / NSEC_PER_NTP64;
88840341
RC
749}
750
a7892a1a
ML
751/* ================================================== */
752
aec97397
ML
753/* Maximum offset between two sane times */
754#define MAX_OFFSET 4294967296.0
755
39c2bcd4
ML
756/* Minimum allowed distance from maximum 32-bit time_t */
757#define MIN_ENDOFTIME_DISTANCE (365 * 24 * 3600)
758
aec97397 759int
d3f42929 760UTI_IsTimeOffsetSane(const struct timespec *ts, double offset)
aec97397
ML
761{
762 double t;
763
764 /* Handle nan correctly here */
765 if (!(offset > -MAX_OFFSET && offset < MAX_OFFSET))
766 return 0;
767
cfe706f0 768 t = UTI_TimespecToDouble(ts) + offset;
aec97397
ML
769
770 /* Time before 1970 is not considered valid */
771 if (t < 0.0)
772 return 0;
773
774#ifdef HAVE_LONG_TIME_T
775 /* Check if it's in the interval to which NTP time is mapped */
776 if (t < (double)NTP_ERA_SPLIT || t > (double)(NTP_ERA_SPLIT + (1LL << 32)))
777 return 0;
39c2bcd4
ML
778#else
779 /* Don't get too close to 32-bit time_t overflow */
780 if (t > (double)(0x7fffffff - MIN_ENDOFTIME_DISTANCE))
781 return 0;
aec97397
ML
782#endif
783
784 return 1;
785}
786
787/* ================================================== */
788
3c217a9e
ML
789double
790UTI_Log2ToDouble(int l)
791{
792 if (l >= 0) {
793 if (l > 31)
794 l = 31;
7f58852e 795 return (uint32_t)1 << l;
3c217a9e
ML
796 } else {
797 if (l < -31)
798 l = -31;
7f58852e 799 return 1.0 / ((uint32_t)1 << -l);
3c217a9e
ML
800 }
801}
802
803/* ================================================== */
804
a7892a1a 805void
d3f42929 806UTI_TimespecNetworkToHost(const Timespec *src, struct timespec *dest)
a7892a1a 807{
0c54cf31 808 uint32_t sec_low, nsec;
1a795b04
ML
809#ifdef HAVE_LONG_TIME_T
810 uint32_t sec_high;
811#endif
a7892a1a 812
a7892a1a 813 sec_low = ntohl(src->tv_sec_low);
713153b6 814#ifdef HAVE_LONG_TIME_T
1a795b04 815 sec_high = ntohl(src->tv_sec_high);
713153b6
ML
816 if (sec_high == TV_NOHIGHSEC)
817 sec_high = 0;
a7892a1a 818
713153b6
ML
819 dest->tv_sec = (uint64_t)sec_high << 32 | sec_low;
820#else
821 dest->tv_sec = sec_low;
822#endif
0899ab52 823
0c54cf31 824 nsec = ntohl(src->tv_nsec);
a83f0d3c 825 dest->tv_nsec = MIN(nsec, 999999999U);
a7892a1a
ML
826}
827
828/* ================================================== */
829
830void
d3f42929 831UTI_TimespecHostToNetwork(const struct timespec *src, Timespec *dest)
a7892a1a 832{
d0dfa1de 833 dest->tv_nsec = htonl(src->tv_nsec);
713153b6
ML
834#ifdef HAVE_LONG_TIME_T
835 dest->tv_sec_high = htonl((uint64_t)src->tv_sec >> 32);
836#else
837 dest->tv_sec_high = htonl(TV_NOHIGHSEC);
838#endif
a7892a1a
ML
839 dest->tv_sec_low = htonl(src->tv_sec);
840}
841
b4947011
ML
842/* ================================================== */
843
844#define FLOAT_EXP_BITS 7
845#define FLOAT_EXP_MIN (-(1 << (FLOAT_EXP_BITS - 1)))
846#define FLOAT_EXP_MAX (-FLOAT_EXP_MIN - 1)
847#define FLOAT_COEF_BITS ((int)sizeof (int32_t) * 8 - FLOAT_EXP_BITS)
848#define FLOAT_COEF_MIN (-(1 << (FLOAT_COEF_BITS - 1)))
849#define FLOAT_COEF_MAX (-FLOAT_COEF_MIN - 1)
850
851double
852UTI_FloatNetworkToHost(Float f)
853{
8e71a461
ML
854 int32_t exp, coef;
855 uint32_t x;
b4947011
ML
856
857 x = ntohl(f.f);
8e71a461 858
5833be6c 859 exp = x >> FLOAT_COEF_BITS;
8e71a461
ML
860 if (exp >= 1 << (FLOAT_EXP_BITS - 1))
861 exp -= 1 << FLOAT_EXP_BITS;
5833be6c 862 exp -= FLOAT_COEF_BITS;
8e71a461
ML
863
864 coef = x % (1U << FLOAT_COEF_BITS);
865 if (coef >= 1 << (FLOAT_COEF_BITS - 1))
866 coef -= 1 << FLOAT_COEF_BITS;
867
b4947011
ML
868 return coef * pow(2.0, exp);
869}
870
871Float
872UTI_FloatHostToNetwork(double x)
873{
874 int32_t exp, coef, neg;
875 Float f;
876
877 if (x < 0.0) {
878 x = -x;
879 neg = 1;
cde3a003
ML
880 } else if (x >= 0.0) {
881 neg = 0;
b4947011 882 } else {
cde3a003
ML
883 /* Save NaN as zero */
884 x = 0.0;
b4947011
ML
885 neg = 0;
886 }
887
888 if (x < 1.0e-100) {
889 exp = coef = 0;
890 } else if (x > 1.0e100) {
891 exp = FLOAT_EXP_MAX;
892 coef = FLOAT_COEF_MAX + neg;
893 } else {
894 exp = log(x) / log(2) + 1;
895 coef = x * pow(2.0, -exp + FLOAT_COEF_BITS) + 0.5;
896
897 assert(coef > 0);
898
899 /* we may need to shift up to two bits down */
900 while (coef > FLOAT_COEF_MAX + neg) {
901 coef >>= 1;
902 exp++;
903 }
904
905 if (exp > FLOAT_EXP_MAX) {
906 /* overflow */
907 exp = FLOAT_EXP_MAX;
908 coef = FLOAT_COEF_MAX + neg;
909 } else if (exp < FLOAT_EXP_MIN) {
910 /* underflow */
911 if (exp + FLOAT_COEF_BITS >= FLOAT_EXP_MIN) {
912 coef >>= FLOAT_EXP_MIN - exp;
913 exp = FLOAT_EXP_MIN;
914 } else {
915 exp = coef = 0;
916 }
917 }
918 }
919
920 /* negate back */
921 if (neg)
922 coef = (uint32_t)-coef << FLOAT_EXP_BITS >> FLOAT_EXP_BITS;
923
8e71a461 924 f.f = htonl((uint32_t)exp << FLOAT_COEF_BITS | coef);
b4947011
ML
925 return f;
926}
a7892a1a 927
88840341 928/* ================================================== */
9d35b5de 929
a8693a21 930int
9d35b5de
ML
931UTI_FdSetCloexec(int fd)
932{
933 int flags;
934
935 flags = fcntl(fd, F_GETFD);
622769cd
ML
936 if (flags == -1) {
937 DEBUG_LOG("fcntl() failed : %s", strerror(errno));
938 return 0;
9d35b5de 939 }
a8693a21 940
622769cd
ML
941 flags |= FD_CLOEXEC;
942
943 if (fcntl(fd, F_SETFD, flags) < 0) {
944 DEBUG_LOG("fcntl() failed : %s", strerror(errno));
945 return 0;
946 }
947
948 return 1;
9d35b5de
ML
949}
950
951/* ================================================== */
777303f1 952
5bb2bf93 953void
879d9362 954UTI_SetQuitSignalsHandler(void (*handler)(int), int ignore_sigpipe)
ceef8ad2 955{
206e597b
ML
956 struct sigaction sa;
957
958 sa.sa_handler = handler;
959 sa.sa_flags = SA_RESTART;
960 if (sigemptyset(&sa.sa_mask) < 0)
5bb2bf93 961 LOG_FATAL("sigemptyset() failed");
206e597b
ML
962
963#ifdef SIGINT
964 if (sigaction(SIGINT, &sa, NULL) < 0)
5bb2bf93 965 LOG_FATAL("sigaction(%d) failed", SIGINT);
206e597b
ML
966#endif
967#ifdef SIGTERM
968 if (sigaction(SIGTERM, &sa, NULL) < 0)
5bb2bf93 969 LOG_FATAL("sigaction(%d) failed", SIGTERM);
206e597b
ML
970#endif
971#ifdef SIGQUIT
972 if (sigaction(SIGQUIT, &sa, NULL) < 0)
5bb2bf93 973 LOG_FATAL("sigaction(%d) failed", SIGQUIT);
206e597b
ML
974#endif
975#ifdef SIGHUP
976 if (sigaction(SIGHUP, &sa, NULL) < 0)
5bb2bf93 977 LOG_FATAL("sigaction(%d) failed", SIGHUP);
206e597b 978#endif
879d9362
ML
979
980 if (ignore_sigpipe)
981 sa.sa_handler = SIG_IGN;
982
983 if (sigaction(SIGPIPE, &sa, NULL) < 0)
984 LOG_FATAL("sigaction(%d) failed", SIGPIPE);
ceef8ad2 985}
92706b15
ML
986
987/* ================================================== */
988
f1ed08ab
ML
989char *
990UTI_PathToDir(const char *path)
991{
992 char *dir, *slash;
993
994 slash = strrchr(path, '/');
995
996 if (!slash)
997 return Strdup(".");
998
999 if (slash == path)
1000 return Strdup("/");
1001
1002 dir = Malloc(slash - path + 1);
1003 snprintf(dir, slash - path + 1, "%s", path);
1004
1005 return dir;
1006}
1007
1008/* ================================================== */
1009
92706b15 1010static int
236576c1 1011create_dir(char *p, mode_t mode, uid_t uid, gid_t gid)
92706b15
ML
1012{
1013 int status;
1014 struct stat buf;
1015
1016 /* See if directory exists */
1017 status = stat(p, &buf);
1018
1019 if (status < 0) {
236576c1 1020 if (errno != ENOENT) {
f282856c 1021 LOG(LOGS_ERR, "Could not access %s : %s", p, strerror(errno));
236576c1 1022 return 0;
92706b15 1023 }
236576c1
ML
1024 } else {
1025 if (S_ISDIR(buf.st_mode))
1026 return 1;
f282856c 1027 LOG(LOGS_ERR, "%s is not directory", p);
236576c1 1028 return 0;
92706b15
ML
1029 }
1030
236576c1
ML
1031 /* Create the directory */
1032 if (mkdir(p, mode) < 0) {
f282856c 1033 LOG(LOGS_ERR, "Could not create directory %s : %s", p, strerror(errno));
236576c1 1034 return 0;
92706b15
ML
1035 }
1036
30b62139
ML
1037 /* Set its owner */
1038 if (chown(p, uid, gid) < 0) {
f282856c 1039 LOG(LOGS_ERR, "Could not change ownership of %s : %s", p, strerror(errno));
236576c1
ML
1040 /* Don't leave it there with incorrect ownership */
1041 rmdir(p);
1042 return 0;
1043 }
1044
1045 return 1;
92706b15
ML
1046}
1047
1048/* ================================================== */
1049/* Return 0 if the directory couldn't be created, 1 if it could (or
1050 already existed) */
1051int
236576c1 1052UTI_CreateDirAndParents(const char *path, mode_t mode, uid_t uid, gid_t gid)
92706b15
ML
1053{
1054 char *p;
1055 int i, j, k, last;
1056
9a83cab2
ML
1057 /* Don't try to create current directory */
1058 if (!strcmp(path, "."))
1059 return 1;
1060
92706b15
ML
1061 p = (char *)Malloc(1 + strlen(path));
1062
1063 i = k = 0;
1064 while (1) {
1065 p[i++] = path[k++];
1066
1067 if (path[k] == '/' || !path[k]) {
236576c1 1068 /* Check whether its end of string, a trailing / or group of / */
92706b15 1069 last = 1;
236576c1 1070 j = k;
92706b15
ML
1071 while (path[j]) {
1072 if (path[j] != '/') {
236576c1
ML
1073 /* Pick up a / into p[] thru the assignment at the top of the loop */
1074 k = j - 1;
92706b15
ML
1075 last = 0;
1076 break;
1077 }
1078 j++;
1079 }
1080
236576c1
ML
1081 p[i] = 0;
1082
1083 if (!create_dir(p, last ? mode : 0755, last ? uid : 0, last ? gid : 0)) {
1084 Free(p);
1085 return 0;
1086 }
1087
92706b15
ML
1088 if (last)
1089 break;
1090 }
1091
1092 if (!path[k])
1093 break;
1094 }
1095
1096 Free(p);
1097 return 1;
1098}
f1ed08ab
ML
1099
1100/* ================================================== */
1101
1102int
1103UTI_CheckDirPermissions(const char *path, mode_t perm, uid_t uid, gid_t gid)
1104{
1105 struct stat buf;
1106
1107 if (stat(path, &buf)) {
f282856c 1108 LOG(LOGS_ERR, "Could not access %s : %s", path, strerror(errno));
f1ed08ab
ML
1109 return 0;
1110 }
1111
1112 if (!S_ISDIR(buf.st_mode)) {
f282856c 1113 LOG(LOGS_ERR, "%s is not directory", path);
f1ed08ab
ML
1114 return 0;
1115 }
1116
1117 if ((buf.st_mode & 0777) & ~perm) {
f282856c 1118 LOG(LOGS_ERR, "Wrong permissions on %s", path);
f1ed08ab
ML
1119 return 0;
1120 }
1121
06486f31 1122 if (buf.st_uid != uid) {
9bc774d6 1123 LOG(LOGS_ERR, "Wrong owner of %s (%s != %u)", path, "UID", uid);
06486f31
ML
1124 return 0;
1125 }
1126
1127 if (buf.st_gid != gid) {
9bc774d6 1128 LOG(LOGS_ERR, "Wrong owner of %s (%s != %u)", path, "GID", gid);
f1ed08ab
ML
1129 return 0;
1130 }
1131
1132 return 1;
1133}
6199a891
ML
1134
1135/* ================================================== */
1136
7a4c396b
ML
1137static int
1138join_path(const char *basedir, const char *name, const char *suffix,
1139 char *buffer, size_t length, LOG_Severity severity)
1140{
1141 const char *sep;
1142
1143 if (!basedir) {
1144 basedir = "";
1145 sep = "";
1146 } else {
1147 sep = "/";
1148 }
1149
1150 if (!suffix)
1151 suffix = "";
1152
1153 if (snprintf(buffer, length, "%s%s%s%s", basedir, sep, name, suffix) >= length) {
1154 LOG(severity, "File path %s%s%s%s too long", basedir, sep, name, suffix);
1155 return 0;
1156 }
1157
1158 return 1;
1159}
1160
1161/* ================================================== */
1162
1163FILE *
1164UTI_OpenFile(const char *basedir, const char *name, const char *suffix,
1165 char mode, mode_t perm)
1166{
1167 const char *file_mode;
1168 char path[PATH_MAX];
1169 LOG_Severity severity;
1170 int fd, flags;
1171 FILE *file;
1172
1173 severity = mode >= 'A' && mode <= 'Z' ? LOGS_FATAL : LOGS_ERR;
1174
1175 if (!join_path(basedir, name, suffix, path, sizeof (path), severity))
1176 return NULL;
1177
1178 switch (mode) {
1179 case 'r':
1180 case 'R':
1181 flags = O_RDONLY;
1182 file_mode = "r";
1183 if (severity != LOGS_FATAL)
1184 severity = LOGS_DEBUG;
1185 break;
1186 case 'w':
1187 case 'W':
1188 flags = O_WRONLY | O_CREAT | O_EXCL;
1189 file_mode = "w";
1190 break;
1191 case 'a':
1192 case 'A':
1193 flags = O_WRONLY | O_CREAT | O_APPEND;
1194 file_mode = "a";
1195 break;
1196 default:
1197 assert(0);
1198 return NULL;
1199 }
1200
1201try_again:
1202 fd = open(path, flags, perm);
1203 if (fd < 0) {
1204 if (errno == EEXIST) {
1205 if (unlink(path) < 0) {
1206 LOG(severity, "Could not remove %s : %s", path, strerror(errno));
1207 return NULL;
1208 }
1209 DEBUG_LOG("Removed %s", path);
1210 goto try_again;
1211 }
1212 LOG(severity, "Could not open %s : %s", path, strerror(errno));
1213 return NULL;
1214 }
1215
1216 UTI_FdSetCloexec(fd);
1217
1218 file = fdopen(fd, file_mode);
1219 if (!file) {
1220 LOG(severity, "Could not open %s : %s", path, strerror(errno));
1221 close(fd);
1222 return NULL;
1223 }
1224
1225 DEBUG_LOG("Opened %s fd=%d mode=%c", path, fd, mode);
1226
1227 return file;
1228}
1229
1230/* ================================================== */
1231
1232int
1233UTI_RenameTempFile(const char *basedir, const char *name,
1234 const char *old_suffix, const char *new_suffix)
1235{
1236 char old_path[PATH_MAX], new_path[PATH_MAX];
1237
1238 if (!join_path(basedir, name, old_suffix, old_path, sizeof (old_path), LOGS_ERR))
1239 return 0;
1240
1241 if (!join_path(basedir, name, new_suffix, new_path, sizeof (new_path), LOGS_ERR))
1242 goto error;
1243
1244 if (rename(old_path, new_path) < 0) {
1245 LOG(LOGS_ERR, "Could not replace %s with %s : %s", new_path, old_path, strerror(errno));
1246 goto error;
1247 }
1248
1249 DEBUG_LOG("Renamed %s to %s", old_path, new_path);
1250
1251 return 1;
1252
1253error:
1254 if (unlink(old_path) < 0)
1255 LOG(LOGS_ERR, "Could not remove %s : %s", old_path, strerror(errno));
1256
1257 return 0;
1258}
1259
1260/* ================================================== */
1261
1262int
1263UTI_RemoveFile(const char *basedir, const char *name, const char *suffix)
1264{
1265 char path[PATH_MAX];
1858104b 1266 struct stat buf;
7a4c396b
ML
1267
1268 if (!join_path(basedir, name, suffix, path, sizeof (path), LOGS_ERR))
1269 return 0;
1270
1858104b
ML
1271 /* Avoid logging an error message if the file is not accessible */
1272 if (stat(path, &buf) < 0) {
1273 DEBUG_LOG("Could not remove %s : %s", path, strerror(errno));
1274 return 0;
1275 }
1276
7a4c396b 1277 if (unlink(path) < 0) {
1858104b 1278 LOG(LOGS_ERR, "Could not remove %s : %s", path, strerror(errno));
7a4c396b
ML
1279 return 0;
1280 }
1281
1282 DEBUG_LOG("Removed %s", path);
1283
1284 return 1;
1285}
1286
1287/* ================================================== */
1288
3cf6acdf
ML
1289void
1290UTI_DropRoot(uid_t uid, gid_t gid)
1291{
1292 /* Drop supplementary groups */
1293 if (setgroups(0, NULL))
f282856c 1294 LOG_FATAL("setgroups() failed : %s", strerror(errno));
3cf6acdf
ML
1295
1296 /* Set effective, saved and real group ID */
1297 if (setgid(gid))
9bc774d6 1298 LOG_FATAL("setgid(%u) failed : %s", gid, strerror(errno));
3cf6acdf
ML
1299
1300 /* Set effective, saved and real user ID */
1301 if (setuid(uid))
9bc774d6 1302 LOG_FATAL("setuid(%u) failed : %s", uid, strerror(errno));
3cf6acdf 1303
9bc774d6 1304 DEBUG_LOG("Dropped root privileges: UID %u GID %u", uid, gid);
3cf6acdf
ML
1305}
1306
1307/* ================================================== */
1308
6199a891
ML
1309#define DEV_URANDOM "/dev/urandom"
1310
1311void
32ac6ffa 1312UTI_GetRandomBytesUrandom(void *buf, unsigned int len)
6199a891 1313{
6199a891 1314 static FILE *f = NULL;
32ac6ffa 1315
6199a891 1316 if (!f)
e18903a6 1317 f = UTI_OpenFile(NULL, DEV_URANDOM, NULL, 'R', 0);
6199a891 1318 if (fread(buf, 1, len, f) != len)
f282856c 1319 LOG_FATAL("Can't read from %s", DEV_URANDOM);
32ac6ffa
ML
1320}
1321
1322/* ================================================== */
1323
c5735ebf
ML
1324#ifdef HAVE_GETRANDOM
1325static void
1326get_random_bytes_getrandom(char *buf, unsigned int len)
1327{
1328 static char rand_buf[256];
1329 static unsigned int available = 0, disabled = 0;
1330 unsigned int i;
1331
1332 for (i = 0; i < len; i++) {
1333 if (!available) {
1334 if (disabled)
1335 break;
1336
7c5bd948 1337 if (getrandom(rand_buf, sizeof (rand_buf), GRND_NONBLOCK) != sizeof (rand_buf)) {
c5735ebf
ML
1338 disabled = 1;
1339 break;
1340 }
1341
1342 available = sizeof (rand_buf);
1343 }
1344
1345 buf[i] = rand_buf[--available];
1346 }
1347
1348 if (i < len)
1349 UTI_GetRandomBytesUrandom(buf, len);
1350}
1351#endif
1352
1353/* ================================================== */
1354
32ac6ffa
ML
1355void
1356UTI_GetRandomBytes(void *buf, unsigned int len)
1357{
1358#ifdef HAVE_ARC4RANDOM
1359 arc4random_buf(buf, len);
c5735ebf
ML
1360#elif defined(HAVE_GETRANDOM)
1361 get_random_bytes_getrandom(buf, len);
32ac6ffa
ML
1362#else
1363 UTI_GetRandomBytesUrandom(buf, len);
6199a891
ML
1364#endif
1365}
e43d6999
ML
1366
1367/* ================================================== */
1368
1369int
1370UTI_BytesToHex(const void *buf, unsigned int buf_len, char *hex, unsigned int hex_len)
1371{
1372 unsigned int i, l;
1373
1374 for (i = l = 0; i < buf_len; i++, l += 2) {
1375 if (l + 2 >= hex_len ||
1376 snprintf(hex + l, hex_len - l, "%02hhX", ((const char *)buf)[i]) != 2)
1377 return 0;
1378 }
1379
1380 return 1;
1381}
1382
1383/* ================================================== */
1384
1385unsigned int
1386UTI_HexToBytes(const char *hex, void *buf, unsigned int len)
1387{
1388 char *p, byte[3];
1389 unsigned int i;
1390
1391 for (i = 0; i < len && *hex != '\0'; i++) {
1392 byte[0] = *hex++;
1393 if (*hex == '\0')
1394 return 0;
1395 byte[1] = *hex++;
1396 byte[2] = '\0';
1397 ((char *)buf)[i] = strtol(byte, &p, 16);
1398
1399 if (p != byte + 2)
1400 return 0;
1401 }
1402
1403 return *hex == '\0' ? i : 0;
1404}
d6034263
ML
1405
1406/* ================================================== */
1407
1408int
1409UTI_SplitString(char *string, char **words, int max_saved_words)
1410{
1411 char *s = string;
1412 int i;
1413
1414 for (i = 0; i < max_saved_words; i++)
1415 words[i] = NULL;
1416
1417 for (i = 0; ; i++) {
1418 /* Zero white-space characters before the word */
1419 while (*s != '\0' && isspace((unsigned char)*s))
1420 *s++ = '\0';
1421
1422 if (*s == '\0')
1423 break;
1424
1425 if (i < max_saved_words)
1426 words[i] = s;
1427
1428 /* Find the next word */
1429 while (*s != '\0' && !isspace((unsigned char)*s))
1430 s++;
1431 }
1432
1433 return i;
1434}