Code

data: Let sdb_data_parse() accept a const string and copy it if necessary.
[sysdb.git] / src / utils / proto.c
1 /*
2  * SysDB - src/utils/proto.c
3  * Copyright (C) 2013 Sebastian 'tokkee' Harl <sh@tokkee.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
28 #if HAVE_CONFIG_H
29 #       include "config.h"
30 #endif
32 #include "core/data.h"
33 #include "core/store.h"
34 #include "core/time.h"
35 #include "utils/error.h"
36 #include "utils/proto.h"
38 #include <assert.h>
39 #include <errno.h>
41 #include <arpa/inet.h>
42 #include <limits.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
48 #include <sys/select.h>
50 /*
51  * private helper functions
52  */
54 /* swap endianess of the specified 64-bit value */
55 static uint64_t
56 endian_swap64(uint64_t v)
57 {
58         return
59                   (((v & 0xff00000000000000LL) >> 56) & 0x00000000000000ffLL)
60                 | (((v & 0x00ff000000000000LL) >> 40) & 0x000000000000ff00LL)
61                 | (((v & 0x0000ff0000000000LL) >> 24) & 0x0000000000ff0000LL)
62                 | (((v & 0x000000ff00000000LL) >> 8)  & 0x00000000ff000000LL)
63                 | (((v & 0x00000000ff000000LL) << 8)  & 0x000000ff00000000LL)
64                 | (((v & 0x0000000000ff0000LL) << 24) & 0x0000ff0000000000LL)
65                 | (((v & 0x000000000000ff00LL) << 40) & 0x00ff000000000000LL)
66                 | (((v & 0x00000000000000ffLL) << 56) & 0xff00000000000000LL);
67 } /* endian_swap64 */
69 /* In case there's not enough buffer space, the marshal functions have to
70  * return the number of bytes that would have been written if enough space had
71  * been available. */
73 static ssize_t
74 marshal_int32(char *buf, size_t buf_len, uint32_t v)
75 {
76         if (buf_len >= sizeof(v)) {
77                 v = htonl(v);
78                 memcpy(buf, &v, sizeof(v));
79         }
80         return sizeof(v);
81 } /* marshal_int32 */
83 static ssize_t
84 marshal_int64(char *buf, size_t buf_len, int64_t v)
85 {
86         if (buf_len >= sizeof(v)) {
87 #if __BYTE_ORDER != __BIG_ENDIAN
88                 v = endian_swap64(v);
89 #endif
90                 memcpy(buf, &v, sizeof(v));
91         }
92         return sizeof(v);
93 } /* marshal_int64 */
95 static ssize_t
96 unmarshal_int64(const char *buf, size_t buf_len, int64_t *v)
97 {
98         if (buf_len < sizeof(*v))
99                 return -1;
100         if (v) {
101                 memcpy(v, buf, sizeof(*v));
102 #if __BYTE_ORDER != __BIG_ENDIAN
103                 *v = endian_swap64(*v);
104 #endif
105         }
106         return sizeof(*v);
107 } /* unmarshal_int64 */
109 static ssize_t
110 marshal_double(char *buf, size_t buf_len, double v)
112         uint64_t t = 0;
113         assert(sizeof(v) == sizeof(t));
114         if (buf_len >= sizeof(t)) {
115                 memcpy(&t, &v, sizeof(v));
116 #if IEEE754_DOUBLE_BYTE_ORDER != IEEE754_DOUBLE_BIG_ENDIAN
117                 t = endian_swap64(t);
118 #endif
119                 memcpy(buf, &t, sizeof(t));
120         }
121         return sizeof(t);
122 } /* marshal_double */
124 static ssize_t
125 unmarshal_double(const char *buf, size_t len, double *v)
127         uint64_t t;
128         assert(sizeof(*v) == sizeof(t));
129         if (len < sizeof(*v))
130                 return -1;
131         if (v) {
132                 memcpy(&t, buf, sizeof(t));
133 #if IEEE754_DOUBLE_BYTE_ORDER != IEEE754_DOUBLE_BIG_ENDIAN
134                 t = endian_swap64(t);
135 #endif
136                 memcpy(v, &t, sizeof(t));
137         }
138         return sizeof(*v);
139 } /* unmarshal_double */
141 static ssize_t
142 marshal_datetime(char *buf, size_t buf_len, sdb_time_t v)
144         return marshal_int64(buf, buf_len, (int64_t)v);
145 } /* marshal_datetime */
147 static ssize_t
148 unmarshal_datetime(const char *buf, size_t len, sdb_time_t *v)
150         return unmarshal_int64(buf, len, (int64_t *)v);
151 } /* unmarshal_datetime */
153 static ssize_t
154 marshal_binary(char *buf, size_t buf_len, size_t len, const unsigned char *v)
156         uint32_t tmp;
157         if (buf_len >= sizeof(tmp) + len) {
158                 tmp = htonl((uint32_t)len);
159                 memcpy(buf, &tmp, sizeof(tmp));
160                 memcpy(buf + sizeof(tmp), v, len);
161         }
162         return sizeof(tmp) + len;
163 } /* marshal_binary */
165 static ssize_t
166 unmarshal_binary(const char *buf, size_t len, size_t *v_len, unsigned char **v)
168         uint32_t l;
169         ssize_t n;
171         if ((n = sdb_proto_unmarshal_int32(buf, len, &l)) < 0)
172                 return -1;
173         buf += n; len -= n;
174         if (len < (size_t)l)
175                 return -1;
177         if (v_len)
178                 *v_len = (size_t)l;
179         if (v && (l > 0)) {
180                 *v = malloc((size_t)l);
181                 if (! *v)
182                         return -1;
183                 memcpy(*v, buf, (size_t)l);
184         }
185         else if (v)
186                 *v = NULL;
187         return sizeof(l) + (ssize_t)l;
188 } /* unmarshal_binary */
190 static ssize_t
191 marshal_string(char *buf, size_t buf_len, const char *v)
193         /* The actual string including the terminating null byte. */
194         size_t len = strlen(v) + 1;
195         if (buf_len >= len)
196                 memcpy(buf, v, len);
197         return len;
198 } /* marshal_string */
200 static ssize_t
201 unmarshal_string(const char *buf, size_t len, char **v)
203         size_t l = 0;
205         for (l = 0; l < len; ++l)
206                 if (buf[l] == '\0')
207                         break;
208         if ((! len) || (buf[l] != '\0'))
209                 return -1;
210         if (v) {
211                 *v = malloc(l + 1);
212                 if (! *v)
213                         return -1;
214                 memcpy(*v, buf, l + 1);
215         }
216         return l + 1;
217 } /* unmarshal_string */
219 #define OBJ_HEADER_LEN (sizeof(uint32_t) + sizeof(sdb_time_t))
220 static ssize_t
221 marshal_obj_header(char *buf, size_t buf_len,
222                 int type, sdb_time_t last_update)
224         ssize_t n;
226         if (buf_len < OBJ_HEADER_LEN)
227                 return OBJ_HEADER_LEN;
229         n = marshal_int32(buf, buf_len, (uint32_t)type);
230         buf += n; buf_len -= n;
231         marshal_datetime(buf, buf_len, last_update);
232         return OBJ_HEADER_LEN;
233 } /* marshal_obj_header */
235 /*
236  * public API
237  */
239 ssize_t
240 sdb_proto_marshal(char *buf, size_t buf_len, uint32_t code,
241                 uint32_t msg_len, const char *msg)
243         size_t len = 2 * sizeof(uint32_t) + msg_len;
244         uint32_t tmp;
246         if (buf_len < 2 * sizeof(uint32_t))
247                 return -1;
248         if (buf_len < len) /* crop message */
249                 msg_len -= (uint32_t)(len - buf_len);
251         tmp = htonl(code);
252         memcpy(buf, &tmp, sizeof(tmp));
253         tmp = htonl(msg_len);
254         memcpy(buf + sizeof(tmp), &tmp, sizeof(tmp));
256         if (msg_len)
257                 memcpy(buf + 2 * sizeof(tmp), msg, msg_len);
258         return len;
259 } /* sdb_proto_marshal */
261 ssize_t
262 sdb_proto_marshal_data(char *buf, size_t buf_len, const sdb_data_t *datum)
264         ssize_t len = 0, n = 0;
265         uint32_t tmp;
266         size_t i;
267         int type;
269         if (buf_len >= sizeof(tmp)) {
270                 tmp = htonl((uint32_t)datum->type);
271                 memcpy(buf, &tmp, sizeof(tmp));
272                 buf += sizeof(tmp);
273                 buf_len -= sizeof(tmp);
274         }
275         else
276                 buf_len = 0;
277         len += sizeof(tmp);
279         if (datum->type == SDB_TYPE_NULL)
280                 return len;
282         if (datum->type == SDB_TYPE_INTEGER)
283                 n = marshal_int64(buf, buf_len, datum->data.integer);
284         else if (datum->type == SDB_TYPE_DECIMAL)
285                 n = marshal_double(buf, buf_len, datum->data.decimal);
286         else if (datum->type == SDB_TYPE_STRING)
287                 n = marshal_string(buf, buf_len, datum->data.string);
288         else if (datum->type == SDB_TYPE_DATETIME)
289                 n = marshal_datetime(buf, buf_len, datum->data.datetime);
290         else if (datum->type == SDB_TYPE_BINARY)
291                 n = marshal_binary(buf, buf_len,
292                                 datum->data.binary.length, datum->data.binary.datum);
293         else if (datum->type == SDB_TYPE_REGEX)
294                 n = marshal_string(buf, buf_len, datum->data.re.raw);
296         if (n < 0)
297                 return n;
298         else if (n > 0)
299                 return len + n;
301         if (! (datum->type & SDB_TYPE_ARRAY)) {
302                 errno = EINVAL;
303                 return -1;
304         }
306         /* arrays */
307         if (buf_len >= sizeof(tmp)) {
308                 tmp = htonl((uint32_t)datum->data.array.length);
309                 memcpy(buf, &tmp, sizeof(tmp));
310                 buf += sizeof(tmp);
311                 buf_len -= sizeof(tmp);
312         }
313         else
314                 buf_len = 0;
315         len += sizeof(tmp);
317         type = datum->type & 0xff;
318         for (i = 0; i < datum->data.array.length; ++i) {
319                 if (type == SDB_TYPE_INTEGER) {
320                         int64_t *v = datum->data.array.values;
321                         n = marshal_int64(buf, buf_len, v[i]);
322                 }
323                 else if (type == SDB_TYPE_DECIMAL) {
324                         double *v = datum->data.array.values;
325                         n = marshal_double(buf, buf_len, v[i]);
326                 }
327                 else if (type == SDB_TYPE_STRING) {
328                         char **v = datum->data.array.values;
329                         n = marshal_string(buf, buf_len, v[i]);
330                 }
331                 else if (type == SDB_TYPE_DATETIME) {
332                         sdb_time_t *v = datum->data.array.values;
333                         n = marshal_datetime(buf, buf_len, v[i]);
334                 }
335                 else if (type == SDB_TYPE_BINARY) {
336                         struct {
337                                 size_t length;
338                                 unsigned char *datum;
339                         } *v = datum->data.array.values;
340                         n = marshal_binary(buf, buf_len, v[i].length, v[i].datum);
341                 }
342                 else if (type == SDB_TYPE_REGEX) {
343                         struct {
344                                 char *raw;
345                                 regex_t regex;
346                         } *v = datum->data.array.values;
347                         n = marshal_string(buf, buf_len, v[i].raw);
348                 }
349                 else {
350                         errno = EINVAL;
351                         return -1;
352                 }
354                 if (n < 0)
355                         return -1;
356                 if (buf_len >= (size_t)n) {
357                         buf += n;
358                         buf_len -= n;
359                 }
360                 else
361                         buf_len = 0;
362                 len += n;
363         }
364         return len;
365 } /* sdb_proto_marshal_data */
367 ssize_t
368 sdb_proto_marshal_host(char *buf, size_t buf_len,
369                 const sdb_proto_host_t *host)
371         size_t len;
372         ssize_t n;
374         if ((! host) || (! host->name))
375                 return -1;
377         len = OBJ_HEADER_LEN + strlen(host->name) + 1;
378         if (buf_len < len)
379                 return len;
381         n = marshal_obj_header(buf, buf_len, SDB_HOST, host->last_update);
382         buf += n; buf_len -= n;
383         marshal_string(buf, buf_len, host->name);
384         return len;
385 } /* sdb_proto_marshal_host */
387 ssize_t
388 sdb_proto_marshal_service(char *buf, size_t buf_len,
389                 const sdb_proto_service_t *svc)
391         size_t len;
392         ssize_t n;
394         if ((! svc) || (! svc->hostname) || (! svc->name))
395                 return -1;
397         len = OBJ_HEADER_LEN + strlen(svc->hostname) + strlen(svc->name) + 2;
398         if (buf_len < len)
399                 return len;
401         n = marshal_obj_header(buf, buf_len, SDB_SERVICE, svc->last_update);
402         buf += n; buf_len -= n;
403         n = marshal_string(buf, buf_len, svc->hostname);
404         buf += n; buf_len -= n;
405         marshal_string(buf, buf_len, svc->name);
406         return len;
407 } /* sdb_proto_marshal_service */
409 ssize_t
410 sdb_proto_marshal_metric(char *buf, size_t buf_len,
411                 const sdb_proto_metric_t *metric)
413         size_t len;
414         ssize_t n;
416         if ((! metric) || (! metric->hostname) || (! metric->name))
417                 return -1;
419         len = OBJ_HEADER_LEN + strlen(metric->hostname) + strlen(metric->name) + 2;
420         if (metric->store_type && metric->store_id)
421                 len += strlen(metric->store_type) + strlen(metric->store_id) + 2;
422         if (buf_len < len)
423                 return len;
425         n = marshal_obj_header(buf, buf_len, SDB_METRIC, metric->last_update);
426         buf += n; buf_len -= n;
427         n = marshal_string(buf, buf_len, metric->hostname);
428         buf += n; buf_len -= n;
429         n = marshal_string(buf, buf_len, metric->name);
430         buf += n; buf_len -= n;
431         if (metric->store_type && metric->store_id) {
432                 n = marshal_string(buf, buf_len, metric->store_type);
433                 buf += n; buf_len -= n;
434                 marshal_string(buf, buf_len, metric->store_id);
435         }
436         return len;
437 } /* sdb_proto_marshal_metric */
439 ssize_t
440 sdb_proto_marshal_attribute(char *buf, size_t buf_len,
441                 const sdb_proto_attribute_t *attr)
443         size_t len;
444         ssize_t n;
446         if ((! attr) || (! attr->parent) || (! attr->key) || (! attr->value)
447                         || ((attr->parent_type != SDB_HOST) && (! attr->hostname))
448                         || ((attr->parent_type != SDB_HOST)
449                                 && (attr->parent_type != SDB_SERVICE)
450                                 && (attr->parent_type != SDB_METRIC)))
451                 return -1;
453         n = sdb_proto_marshal_data(NULL, 0, attr->value);
454         if (n < 0)
455                 return -1;
457         len = OBJ_HEADER_LEN
458                 + strlen(attr->parent) + strlen(attr->key) + 2 + (size_t)n;
459         if (attr->parent_type != SDB_HOST)
460                 len += strlen(attr->hostname) + 1;
461         if (buf_len < len)
462                 return len;
464         n = marshal_obj_header(buf, buf_len,
465                         attr->parent_type | SDB_ATTRIBUTE, attr->last_update);
466         buf += n; buf_len -= n;
467         if (attr->parent_type != SDB_HOST) {
468                 n = marshal_string(buf, buf_len, attr->hostname);
469                 buf += n; buf_len -= n;
470         }
471         n = marshal_string(buf, buf_len, attr->parent);
472         buf += n; buf_len -= n;
473         n = marshal_string(buf, buf_len, attr->key);
474         buf += n; buf_len -= n;
475         sdb_proto_marshal_data(buf, buf_len, attr->value);
476         return len;
477 } /* sdb_proto_marshal_attribute */
479 ssize_t
480 sdb_proto_unmarshal_header(const char *buf, size_t buf_len,
481                 uint32_t *code, uint32_t *msg_len)
483         uint32_t tmp;
484         ssize_t n;
486         if (buf_len < 2 * sizeof(uint32_t))
487                 return -1;
489         n = sdb_proto_unmarshal_int32(buf, buf_len, &tmp);
490         if (code)
491                 *code = tmp;
492         buf += n; buf_len -= n;
493         sdb_proto_unmarshal_int32(buf, buf_len, &tmp);
494         if (msg_len)
495                 *msg_len = tmp;
496         return 2 * sizeof(uint32_t);
497 } /* sdb_proto_unmarshal_header */
499 ssize_t
500 sdb_proto_unmarshal_int32(const char *buf, size_t buf_len, uint32_t *v)
502         uint32_t n;
504         /* not enough data to read */
505         if (buf_len < sizeof(n))
506                 return -1;
508         memcpy(&n, buf, sizeof(n));
509         if (v)
510                 *v = ntohl(n);
511         return sizeof(n);
512 } /* sdb_proto_unmarshal_int32 */
514 ssize_t
515 sdb_proto_unmarshal_data(const char *buf, size_t len, sdb_data_t *datum)
517         sdb_data_t d = SDB_DATA_INIT;
518         ssize_t l = 0, n;
519         uint32_t tmp;
520         size_t i;
522         if ((n = sdb_proto_unmarshal_int32(buf, len, &tmp)) < 0)
523                 return -1;
524         d.type = (int)tmp;
525         if (d.type == SDB_TYPE_NULL)
526                 return sizeof(tmp);
527         buf += n; len -= n; l += n;
529 /* Don't populate 'd' if 'datum' is NULL. */
530 #define D(field) (datum ? &d.data.field : NULL)
531         if (d.type == SDB_TYPE_INTEGER)
532                 n = unmarshal_int64(buf, len, D(integer));
533         else if (d.type == SDB_TYPE_DECIMAL)
534                 n = unmarshal_double(buf, len, D(decimal));
535         else if (d.type == SDB_TYPE_STRING)
536                 n = unmarshal_string(buf, len, D(string));
537         else if (d.type == SDB_TYPE_DATETIME)
538                 n = unmarshal_datetime(buf, len, D(datetime));
539         else if (d.type == SDB_TYPE_BINARY)
540                 n = unmarshal_binary(buf, len, D(binary.length), D(binary.datum));
541         else if (d.type == SDB_TYPE_REGEX) {
542                 if (datum) {
543                         char *str = NULL;
544                         n = unmarshal_string(buf, len, &str);
545                         if (sdb_data_parse(str, SDB_TYPE_REGEX, &d))
546                                 n = -1;
547                         free(str);
548                 }
549                 else
550                         n = unmarshal_string(buf, len, NULL);
551         }
552         else
553                 n = 0;
554 #undef D
556         if (n < 0)
557                 return n;
558         else if (n > 0) {
559                 if (datum)
560                         *datum = d;
561                 return l + n;
562         }
564         if (! (d.type & SDB_TYPE_ARRAY)) {
565                 errno = EINVAL;
566                 return -1;
567         }
569         /* arrays */
570         if ((n = sdb_proto_unmarshal_int32(buf, len, &tmp)) < 0)
571                 return -1;
572         buf += n; len -= n; l += n;
573         d.data.array.length = (size_t)tmp;
575 #define V(field) (datum ? &v[i]field : NULL)
576         if (datum)
577                 d.data.array.values = calloc(d.data.array.length,
578                                 sdb_data_sizeof(d.type & 0xff));
579         for (i = 0; i < d.data.array.length; ++i) {
580                 if ((d.type & 0xff) == SDB_TYPE_INTEGER) {
581                         int64_t *v = d.data.array.values;
582                         n = unmarshal_int64(buf, len, V());
583                 }
584                 else if ((d.type & 0xff) == SDB_TYPE_DECIMAL) {
585                         double *v = d.data.array.values;
586                         n = unmarshal_double(buf, len, V());
587                 }
588                 else if ((d.type & 0xff) == SDB_TYPE_STRING) {
589                         char **v = d.data.array.values;
590                         n = unmarshal_string(buf, len, V());
591                 }
592                 else if ((d.type & 0xff) == SDB_TYPE_DATETIME) {
593                         sdb_time_t *v = d.data.array.values;
594                         n = unmarshal_datetime(buf, len, V());
595                 }
596                 else if ((d.type & 0xff) == SDB_TYPE_BINARY) {
597                         struct {
598                                 size_t length;
599                                 unsigned char *datum;
600                         } *v = d.data.array.values;
601                         n = unmarshal_binary(buf, len, V(.length), V(.datum));
602                 }
603                 else if ((d.type & 0xff) == SDB_TYPE_REGEX) {
604                         struct {
605                                 char *raw;
606                                 regex_t regex;
607                         } *v = d.data.array.values;
608                         if (datum) {
609                                 sdb_data_t t = SDB_DATA_INIT;
610                                 char *str = NULL;
611                                 n = unmarshal_string(buf, len, &str);
612                                 if (! sdb_data_parse(str, SDB_TYPE_REGEX, &t)) {
613                                         v[i].raw = t.data.re.raw;
614                                         v[i].regex = t.data.re.regex;
615                                 }
616                                 else
617                                         n = -1;
618                                 free(str);
619                         }
620                         else
621                                 n = unmarshal_string(buf, len, NULL);
622                 }
623                 else {
624                         if (datum)
625                                 sdb_data_free_datum(&d);
626                         errno = EINVAL;
627                         return -1;
628                 }
630                 if (n < 0) {
631                         if (datum)
632                                 sdb_data_free_datum(&d);
633                         return -1;
634                 }
635                 if (len >= (size_t)n) {
636                         buf += n;
637                         len -= n;
638                         l += n;
639                 }
640                 else
641                         return -1;
642         }
643 #undef V
645         if (datum)
646                 *datum = d;
647         return l;
648 } /* sdb_proto_unmarshal_data */
650 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */