| /* 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; |
| } |