blob: a5455056d10792703934c5393ed4649b5a2f886c [file] [log] [blame]
/**
* Copyright © 2016 IBM Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <endian.h>
#include <err.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <linux/types.h>
#include "console-server.h"
struct log_handler {
struct handler handler;
struct console *console;
struct ringbuffer_consumer *rbc;
int fd;
size_t size;
size_t maxsize;
size_t pagesize;
char *log_filename;
char *rotate_filename;
};
static const char *default_filename = LOCALSTATEDIR "/log/obmc-console.log";
static const size_t default_logsize = 16ul * 1024ul;
static struct log_handler *to_log_handler(struct handler *handler)
{
return container_of(handler, struct log_handler, handler);
}
static int log_trim(struct log_handler *lh)
{
int rc;
/* Move the log buffer file to the rotate file */
close(lh->fd);
rc = rename(lh->log_filename, lh->rotate_filename);
if (rc) {
warn("Failed to rename %s to %s", lh->log_filename,
lh->rotate_filename);
/* don't return, as we need to re-open the logfile */
}
lh->fd = open(lh->log_filename, O_RDWR | O_CREAT | O_TRUNC, 0644);
if (lh->fd < 0) {
warn("Can't open log buffer file %s", lh->log_filename);
return -1;
}
lh->size = 0;
return 0;
}
static int log_data(struct log_handler *lh, uint8_t *buf, size_t len)
{
int rc;
if (len > lh->maxsize) {
buf += len - lh->maxsize;
len = lh->maxsize;
}
if (lh->size + len > lh->maxsize) {
rc = log_trim(lh);
if (rc) {
return rc;
}
}
rc = write_buf_to_fd(lh->fd, buf, len);
if (rc) {
return rc;
}
lh->size += len;
return 0;
}
static enum ringbuffer_poll_ret log_ringbuffer_poll(void *arg, size_t force_len
__attribute__((unused)))
{
struct log_handler *lh = arg;
uint8_t *buf;
size_t len;
int rc;
/* we log synchronously, so just dequeue everything we can, and
* commit straight away. */
for (;;) {
len = ringbuffer_dequeue_peek(lh->rbc, 0, &buf);
if (!len) {
break;
}
rc = log_data(lh, buf, len);
if (rc) {
return RINGBUFFER_POLL_REMOVE;
}
ringbuffer_dequeue_commit(lh->rbc, len);
}
return RINGBUFFER_POLL_OK;
}
static int log_init(struct handler *handler, struct console *console,
struct config *config)
{
struct log_handler *lh = to_log_handler(handler);
const char *filename;
const char *logsize_str;
size_t logsize = default_logsize;
int rc;
lh->console = console;
lh->pagesize = 4096;
lh->size = 0;
lh->log_filename = NULL;
lh->rotate_filename = NULL;
logsize_str = config_get_value(config, "logsize");
rc = config_parse_bytesize(logsize_str, &logsize);
if (logsize_str != NULL && rc) {
logsize = default_logsize;
warn("Invalid logsize. Default to %ukB",
(unsigned int)(logsize >> 10));
}
lh->maxsize = logsize <= lh->pagesize ? lh->pagesize + 1 : logsize;
filename = config_get_value(config, "logfile");
if (!filename) {
filename = default_filename;
}
lh->fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, 0644);
if (lh->fd < 0) {
warn("Can't open log buffer file %s", filename);
return -1;
}
lh->log_filename = strdup(filename);
rc = asprintf(&lh->rotate_filename, "%s.1", filename);
if (rc < 0) {
warn("Failed to construct rotate filename");
return -1;
}
lh->rbc = console_ringbuffer_consumer_register(console,
log_ringbuffer_poll, lh);
return 0;
}
static void log_fini(struct handler *handler)
{
struct log_handler *lh = to_log_handler(handler);
ringbuffer_consumer_unregister(lh->rbc);
close(lh->fd);
free(lh->log_filename);
free(lh->rotate_filename);
}
static struct log_handler log_handler = {
.handler = {
.name = "log",
.init = log_init,
.fini = log_fini,
},
};
console_handler_register(&log_handler.handler);