2012-07-17 07:00:18 +04:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
2012-07-18 13:04:00 +04:00
|
|
|
#include <google/protobuf-c/protobuf-c.h>
|
|
|
|
|
2012-07-17 07:00:18 +04:00
|
|
|
#include "compiler.h"
|
|
|
|
#include "types.h"
|
|
|
|
#include "log.h"
|
|
|
|
#include "util.h"
|
|
|
|
|
|
|
|
#include "protobuf.h"
|
|
|
|
|
|
|
|
/*
|
|
|
|
* To speed up reading of packed objects
|
|
|
|
* by providing space on stack, this should
|
|
|
|
* be more than enough for most objects.
|
|
|
|
*/
|
|
|
|
#define PB_PKOBJ_LOCAL_SIZE 1024
|
|
|
|
|
2012-07-25 18:28:12 +04:00
|
|
|
struct pb_pr_ctrl_s {
|
|
|
|
void *arg;
|
2012-07-25 18:28:14 +04:00
|
|
|
int single_entry;
|
2012-07-25 18:28:15 +04:00
|
|
|
int depth;
|
2012-07-25 18:28:12 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct pb_pr_ctrl_s pb_pr_ctl_t;
|
|
|
|
typedef void (pb_pr_field_t)(void *obj, pb_pr_ctl_t *ctl);
|
2012-07-18 13:04:00 +04:00
|
|
|
|
2012-07-25 18:28:12 +04:00
|
|
|
static void pb_msg_int32x(void *obj, pb_pr_ctl_t *ctl)
|
2012-07-18 13:04:00 +04:00
|
|
|
{
|
|
|
|
pr_msg("0x%08x", *(int *)obj);
|
|
|
|
}
|
|
|
|
|
2012-07-25 18:28:12 +04:00
|
|
|
static void pb_msg_int64x(void *obj, pb_pr_ctl_t *ctl)
|
2012-07-18 13:04:00 +04:00
|
|
|
{
|
|
|
|
pr_msg("0x%016lx", *(long *)obj);
|
|
|
|
}
|
|
|
|
|
2012-07-25 18:28:12 +04:00
|
|
|
static void pb_msg_string(void *obj, pb_pr_ctl_t *ctl)
|
2012-07-18 13:04:00 +04:00
|
|
|
{
|
|
|
|
pr_msg("\"%s\"", *(char **)obj);
|
|
|
|
}
|
|
|
|
|
2012-07-25 18:28:12 +04:00
|
|
|
static void pb_msg_unk(void *obj, pb_pr_ctl_t *ctl)
|
2012-07-18 13:04:00 +04:00
|
|
|
{
|
|
|
|
pr_msg("unknown object %p\n", obj);
|
|
|
|
}
|
|
|
|
|
2012-07-25 18:28:15 +04:00
|
|
|
static inline void print_tabs(pb_pr_ctl_t *ctl)
|
2012-07-25 18:28:14 +04:00
|
|
|
{
|
2012-07-25 18:28:15 +04:00
|
|
|
int counter = ctl->depth;
|
|
|
|
|
|
|
|
if (!ctl->single_entry)
|
|
|
|
return;
|
|
|
|
|
|
|
|
while (counter--)
|
|
|
|
pr_msg("\t");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void print_nested_message_braces(pb_pr_ctl_t *ctl, int right_brace)
|
|
|
|
{
|
|
|
|
if (right_brace)
|
|
|
|
print_tabs(ctl);
|
2012-07-25 18:28:17 +04:00
|
|
|
pr_msg("%s%s", (right_brace) ? "}" : "{", (ctl->single_entry) ? "\n" : " ");
|
2012-07-25 18:28:14 +04:00
|
|
|
}
|
|
|
|
|
2012-07-25 18:28:12 +04:00
|
|
|
static void pb_show_msg(const void *msg, pb_pr_ctl_t *ctl);
|
2012-07-18 13:04:00 +04:00
|
|
|
|
2012-07-25 18:28:12 +04:00
|
|
|
static void show_nested_message(void *msg, pb_pr_ctl_t *ctl)
|
2012-07-18 13:04:00 +04:00
|
|
|
{
|
2012-07-25 18:28:14 +04:00
|
|
|
print_nested_message_braces(ctl, 0);
|
2012-07-25 18:28:15 +04:00
|
|
|
ctl->depth++;
|
2012-07-25 18:28:12 +04:00
|
|
|
pb_show_msg(msg, ctl);
|
2012-07-25 18:28:15 +04:00
|
|
|
ctl->depth--;
|
2012-07-25 18:28:14 +04:00
|
|
|
print_nested_message_braces(ctl, 1);
|
2012-07-18 13:04:00 +04:00
|
|
|
}
|
|
|
|
|
2012-07-25 18:28:12 +04:00
|
|
|
static void show_enum(void *msg, pb_pr_ctl_t *ctl)
|
2012-07-19 10:17:40 +04:00
|
|
|
{
|
2012-07-25 18:28:12 +04:00
|
|
|
ProtobufCEnumDescriptor *d = ctl->arg;
|
2012-07-19 10:17:40 +04:00
|
|
|
const char *val_name = NULL;
|
|
|
|
int val, i;
|
|
|
|
|
|
|
|
val = *(int *)msg;
|
|
|
|
for (i = 0; i < d->n_values; i++)
|
|
|
|
if (d->values[i].value == val) {
|
|
|
|
val_name = d->values[i].name;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (val_name != NULL)
|
|
|
|
pr_msg("%s", val_name);
|
|
|
|
else
|
|
|
|
pr_msg("%d", val);
|
|
|
|
}
|
2012-07-18 13:04:00 +04:00
|
|
|
|
|
|
|
static void pb_show_field(const ProtobufCFieldDescriptor *fd, void *where,
|
2012-07-25 18:28:12 +04:00
|
|
|
unsigned long nr_fields, pb_pr_ctl_t *ctl)
|
2012-07-18 13:04:00 +04:00
|
|
|
{
|
|
|
|
pb_pr_field_t *show;
|
|
|
|
unsigned long counter;
|
|
|
|
size_t fsize;
|
|
|
|
|
2012-07-25 18:28:15 +04:00
|
|
|
print_tabs(ctl);
|
|
|
|
|
2012-07-18 13:04:00 +04:00
|
|
|
pr_msg("%s: ", fd->name);
|
|
|
|
|
|
|
|
switch (fd->type) {
|
|
|
|
case PROTOBUF_C_TYPE_INT32:
|
|
|
|
case PROTOBUF_C_TYPE_SINT32:
|
|
|
|
case PROTOBUF_C_TYPE_UINT32:
|
|
|
|
case PROTOBUF_C_TYPE_SFIXED32:
|
|
|
|
show = pb_msg_int32x;
|
|
|
|
fsize = 4;
|
|
|
|
break;
|
|
|
|
case PROTOBUF_C_TYPE_INT64:
|
|
|
|
case PROTOBUF_C_TYPE_SINT64:
|
|
|
|
case PROTOBUF_C_TYPE_SFIXED64:
|
|
|
|
case PROTOBUF_C_TYPE_FIXED32:
|
|
|
|
case PROTOBUF_C_TYPE_UINT64:
|
|
|
|
case PROTOBUF_C_TYPE_FIXED64:
|
|
|
|
show = pb_msg_int64x;
|
|
|
|
fsize = 8;
|
|
|
|
break;
|
|
|
|
case PROTOBUF_C_TYPE_STRING:
|
|
|
|
show = pb_msg_string;
|
|
|
|
fsize = sizeof (void *);
|
|
|
|
break;
|
|
|
|
case PROTOBUF_C_TYPE_MESSAGE:
|
|
|
|
where = (void *)(*(long *)where);
|
2012-07-25 18:28:12 +04:00
|
|
|
ctl->arg = (void *)fd->descriptor;
|
2012-07-18 13:04:00 +04:00
|
|
|
show = show_nested_message;
|
|
|
|
fsize = sizeof (void *);
|
|
|
|
break;
|
2012-07-19 10:17:40 +04:00
|
|
|
case PROTOBUF_C_TYPE_ENUM:
|
|
|
|
show = show_enum;
|
2012-07-25 18:28:12 +04:00
|
|
|
ctl->arg = (void *)fd->descriptor;
|
2012-07-19 10:17:40 +04:00
|
|
|
break;
|
2012-07-18 13:04:00 +04:00
|
|
|
case PROTOBUF_C_TYPE_FLOAT:
|
|
|
|
case PROTOBUF_C_TYPE_DOUBLE:
|
|
|
|
case PROTOBUF_C_TYPE_BOOL:
|
|
|
|
case PROTOBUF_C_TYPE_BYTES:
|
|
|
|
default:
|
|
|
|
show = pb_msg_unk;
|
|
|
|
nr_fields = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2012-07-25 18:28:12 +04:00
|
|
|
show(where, ctl);
|
2012-07-18 13:04:00 +04:00
|
|
|
where += fsize;
|
|
|
|
|
|
|
|
for (counter = 0; counter < nr_fields - 1; counter++, where += fsize) {
|
|
|
|
pr_msg(":");
|
2012-07-25 18:28:12 +04:00
|
|
|
show(where, ctl);
|
2012-07-18 13:04:00 +04:00
|
|
|
}
|
|
|
|
|
2012-07-25 18:28:14 +04:00
|
|
|
if (ctl->single_entry)
|
|
|
|
pr_msg("\n");
|
|
|
|
else
|
|
|
|
pr_msg(" ");
|
2012-07-18 13:04:00 +04:00
|
|
|
}
|
|
|
|
|
2012-07-25 18:28:15 +04:00
|
|
|
static int pb_optional_field_present(const ProtobufCFieldDescriptor *field,
|
|
|
|
void *msg)
|
|
|
|
{
|
|
|
|
if ((field->type == PROTOBUF_C_TYPE_MESSAGE) ||
|
|
|
|
(field->type == PROTOBUF_C_TYPE_STRING)) {
|
|
|
|
const void *opt_flag = * (const void * const *)(msg + field->offset);
|
|
|
|
|
|
|
|
if ((opt_flag == NULL) || (opt_flag == field->default_value))
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
const protobuf_c_boolean *has = msg + field->quantifier_offset;
|
|
|
|
|
|
|
|
if (!*has)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2012-07-25 18:28:12 +04:00
|
|
|
static void pb_show_msg(const void *msg, pb_pr_ctl_t *ctl)
|
2012-07-18 13:04:00 +04:00
|
|
|
{
|
|
|
|
int i;
|
2012-07-25 18:28:12 +04:00
|
|
|
const ProtobufCMessageDescriptor *md = ctl->arg;
|
2012-07-18 13:04:00 +04:00
|
|
|
|
|
|
|
BUG_ON(md == NULL);
|
|
|
|
|
|
|
|
for (i = 0; i < md->n_fields; i++) {
|
|
|
|
const ProtobufCFieldDescriptor fd = md->fields[i];
|
|
|
|
unsigned long *data;
|
|
|
|
size_t nr_fields;
|
|
|
|
|
|
|
|
nr_fields = 1;
|
|
|
|
data = (unsigned long *)(msg + fd.offset);
|
|
|
|
|
2012-07-25 18:28:15 +04:00
|
|
|
if (fd.label == PROTOBUF_C_LABEL_OPTIONAL) {
|
|
|
|
if (!pb_optional_field_present(&fd, data))
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2012-07-18 13:04:00 +04:00
|
|
|
if (fd.label == PROTOBUF_C_LABEL_REPEATED) {
|
|
|
|
nr_fields = *(size_t *)(msg + fd.quantifier_offset);
|
|
|
|
data = (unsigned long *)*data;
|
|
|
|
}
|
|
|
|
|
2012-07-25 18:28:12 +04:00
|
|
|
pb_show_field(&fd, data, nr_fields, ctl);
|
2012-07-18 13:04:00 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-18 19:09:51 +04:00
|
|
|
void do_pb_show_plain(int fd, const ProtobufCMessageDescriptor *md,
|
2012-07-25 18:28:14 +04:00
|
|
|
pb_unpack_t unpack, pb_free_t free, int single_entry)
|
2012-07-18 19:09:51 +04:00
|
|
|
{
|
2012-07-25 18:28:15 +04:00
|
|
|
pb_pr_ctl_t ctl = {NULL, single_entry, 0};
|
2012-07-25 18:28:12 +04:00
|
|
|
|
2012-07-18 19:09:51 +04:00
|
|
|
while (1) {
|
|
|
|
void *obj;
|
|
|
|
|
|
|
|
if (pb_read_object_with_header(fd, &obj, unpack, true) <= 0)
|
|
|
|
break;
|
|
|
|
|
2012-07-25 18:28:12 +04:00
|
|
|
ctl.arg = (void *)md;
|
|
|
|
pb_show_msg(obj, &ctl);
|
2012-07-18 19:09:51 +04:00
|
|
|
free(obj, NULL);
|
2012-07-25 18:28:14 +04:00
|
|
|
if (single_entry)
|
|
|
|
break;
|
|
|
|
pr_msg("\n");
|
2012-07-18 19:09:51 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-17 07:00:18 +04:00
|
|
|
/*
|
|
|
|
* Reads PB record (header + packed object) from file @fd and unpack
|
|
|
|
* it with @unpack procedure to the pointer @pobj
|
|
|
|
*
|
|
|
|
* 1 on success
|
|
|
|
* -1 on error (or EOF met and @eof set to false)
|
|
|
|
* 0 on EOF and @eof set to true
|
|
|
|
*
|
|
|
|
* Don't forget to free memory granted to unpacked object in calling code if needed
|
|
|
|
*/
|
2012-07-18 18:42:35 +04:00
|
|
|
int pb_read_object_with_header(int fd, void **pobj, pb_unpack_t unpack, bool eof)
|
2012-07-17 07:00:18 +04:00
|
|
|
{
|
|
|
|
u8 local[PB_PKOBJ_LOCAL_SIZE];
|
|
|
|
void *buf = (void *)&local;
|
|
|
|
u32 size;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
*pobj = NULL;
|
|
|
|
|
|
|
|
ret = read(fd, &size, sizeof(size));
|
|
|
|
if (ret == 0) {
|
|
|
|
if (eof) {
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
pr_err("Unexpected EOF\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else if (ret < sizeof(size)) {
|
|
|
|
pr_perror("Read %d bytes while %d expected",
|
|
|
|
ret, (int)sizeof(size));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (size > sizeof(local)) {
|
|
|
|
ret = -1;
|
|
|
|
buf = xmalloc(size);
|
|
|
|
if (!buf)
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = read(fd, buf, size);
|
|
|
|
if (ret < 0) {
|
|
|
|
pr_perror("Can't read %d bytes from file", size);
|
|
|
|
goto err;
|
|
|
|
} else if (ret != size) {
|
|
|
|
pr_perror("Read %d bytes while %d expected", ret, size);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
*pobj = unpack(NULL, size, buf);
|
|
|
|
if (!*pobj) {
|
|
|
|
ret = -1;
|
|
|
|
pr_err("Failed unpacking object %p\n", pobj);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 1;
|
|
|
|
err:
|
|
|
|
if (buf != (void *)&local)
|
|
|
|
xfree(buf);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Writes PB record (header + packed object pointed by @obj)
|
|
|
|
* to file @fd, using @getpksize to get packed size and @pack
|
|
|
|
* to implement packing
|
|
|
|
*
|
|
|
|
* 0 on success
|
|
|
|
* -1 on error
|
|
|
|
*/
|
2012-07-18 18:42:35 +04:00
|
|
|
int pb_write_object_with_header(int fd, void *obj, pb_getpksize_t getpksize, pb_pack_t pack)
|
2012-07-17 07:00:18 +04:00
|
|
|
{
|
|
|
|
u8 local[PB_PKOBJ_LOCAL_SIZE];
|
|
|
|
void *buf = (void *)&local;
|
|
|
|
u32 size, packed;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
size = getpksize(obj);
|
|
|
|
if (size > (u32)sizeof(local)) {
|
|
|
|
buf = xmalloc(size);
|
|
|
|
if (!buf)
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
packed = pack(obj, buf);
|
|
|
|
if (packed != size) {
|
|
|
|
pr_err("Failed packing PB object %p\n", obj);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = write(fd, &size, sizeof(size));
|
|
|
|
if (ret != sizeof(size)) {
|
|
|
|
ret = -1;
|
|
|
|
pr_perror("Can't write %d bytes", (int)sizeof(size));
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = write(fd, buf, size);
|
|
|
|
if (ret != size) {
|
|
|
|
ret = -1;
|
|
|
|
pr_perror("Can't write %d bytes", size);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
err:
|
|
|
|
if (buf != (void *)&local)
|
|
|
|
xfree(buf);
|
|
|
|
return ret;
|
|
|
|
}
|