blob: 5da16df85a8d0629c9450f57067a6e1ad4d1cbfd [file] [log] [blame]
/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include "dumper.h"
void dumpf(struct dumper *dumper, const char *format, ...)
{
va_list ap;
va_start(ap, format);
dumper->vprintf(dumper, format, ap);
va_end(ap);
}
/* dumper which outputs to syslog */
struct syslog_data {
int priority;
struct dumper *mem_dumper;
};
static void syslog_vprintf(struct dumper *dumper, const char *fmt, va_list ap)
{
char *buf;
int size, i;
struct syslog_data *data = (struct syslog_data *)dumper->data;
struct dumper *mem_dumper = data->mem_dumper;
/* We cannot use syslog() directly each time we are called,
* because syslog() will always append a newline to the
* output, so the user will not be able to construct a line
* incrementally using multiple calls. What we do here is to
* collect the output in a buffer until a newline is given by
* the user. */
mem_dumper->vprintf(mem_dumper, fmt, ap);
again:
mem_dumper_get(mem_dumper, &buf, &size);
for (i = 0; i < size; i++) {
if (buf[i] == '\n') {
syslog(data->priority, "%.*s", i + 1, buf);
mem_dumper_consume(mem_dumper, i + 1);
goto again;
}
}
}
struct dumper *syslog_dumper_create(int priority)
{
struct dumper *dumper = calloc(1, sizeof(struct dumper));
struct syslog_data *data = calloc(1, sizeof(struct syslog_data));
data->priority = priority;
data->mem_dumper = mem_dumper_create();
dumper->data = data;
dumper->vprintf = &syslog_vprintf;
return dumper;
}
void syslog_dumper_free(struct dumper *dumper)
{
mem_dumper_free(((struct syslog_data *)dumper->data)->mem_dumper);
free(dumper->data);
free(dumper);
}
/* dumper which outputs to a memory buffer */
struct mem_data {
char *buf;
int size;
int capacity;
};
static void mem_vprintf(struct dumper *dumper, const char *format, va_list ap)
{
int n;
char *tmp;
struct mem_data *data = (struct mem_data *)dumper->data;
while (1) {
/* try to use the remaining space */
int remaining = data->capacity - data->size;
n = vsnprintf(data->buf + data->size, remaining, format, ap);
/* enough space? */
if (n > -1 && n < remaining) {
data->size += n;
return;
}
/* allocate more space and try again */
tmp = realloc(data->buf, data->capacity * 2);
if (tmp == NULL)
return;
data->buf = tmp;
data->capacity *= 2;
}
}
struct dumper *mem_dumper_create()
{
struct dumper *dumper = calloc(1, sizeof(struct dumper));
struct mem_data *data = calloc(1, sizeof(struct mem_data));
if (!dumper || !data)
goto error;
data->size = 0;
data->capacity = 80;
data->buf = malloc(data->capacity);
if (!data->buf)
goto error;
data->buf[0] = '\0';
dumper->data = data;
dumper->vprintf = &mem_vprintf;
return dumper;
error:
if (dumper)
free(dumper);
if (data)
free(data);
return NULL;
}
void mem_dumper_free(struct dumper *dumper)
{
struct mem_data *data = (struct mem_data *)dumper->data;
free(data->buf);
free(data);
free(dumper);
}
void mem_dumper_clear(struct dumper *dumper)
{
struct mem_data *data = (struct mem_data *)dumper->data;
data->buf[0] = '\0';
data->size = 0;
}
void mem_dumper_consume(struct dumper *dumper, int n)
{
struct mem_data *data = (struct mem_data *)dumper->data;
if (n > data->size)
n = data->size;
memmove(data->buf, data->buf + n, data->size - n + 1);
data->size -= n;
}
void mem_dumper_get(struct dumper *dumper, char **buf, int *size)
{
struct mem_data *data = (struct mem_data *)dumper->data;
*buf = data->buf;
*size = data->size;
}