xref: /openbmc/obmc-console/log-handler.c (revision 48d1f533efab4d48d45de5a79ed86f9b6814d068)
19326d779SJeremy Kerr /**
29326d779SJeremy Kerr  * Copyright © 2016 IBM Corporation
39326d779SJeremy Kerr  *
49326d779SJeremy Kerr  * Licensed under the Apache License, Version 2.0 (the "License");
59326d779SJeremy Kerr  * you may not use this file except in compliance with the License.
69326d779SJeremy Kerr  * You may obtain a copy of the License at
79326d779SJeremy Kerr  *
89326d779SJeremy Kerr  *     http://www.apache.org/licenses/LICENSE-2.0
99326d779SJeremy Kerr  *
109326d779SJeremy Kerr  * Unless required by applicable law or agreed to in writing, software
119326d779SJeremy Kerr  * distributed under the License is distributed on an "AS IS" BASIS,
129326d779SJeremy Kerr  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139326d779SJeremy Kerr  * See the License for the specific language governing permissions and
149326d779SJeremy Kerr  * limitations under the License.
159326d779SJeremy Kerr  */
163f54de4cSJeremy Kerr 
173f54de4cSJeremy Kerr #include <endian.h>
183f54de4cSJeremy Kerr #include <err.h>
193f54de4cSJeremy Kerr #include <fcntl.h>
203f54de4cSJeremy Kerr #include <stdbool.h>
213f54de4cSJeremy Kerr #include <stdio.h>
223f54de4cSJeremy Kerr #include <stdlib.h>
233f54de4cSJeremy Kerr #include <string.h>
243f54de4cSJeremy Kerr #include <unistd.h>
253f54de4cSJeremy Kerr 
263f54de4cSJeremy Kerr #include <sys/mman.h>
273f54de4cSJeremy Kerr 
283f54de4cSJeremy Kerr #include <linux/types.h>
293f54de4cSJeremy Kerr 
303f54de4cSJeremy Kerr #include "console-server.h"
313f54de4cSJeremy Kerr 
323f54de4cSJeremy Kerr struct log_handler {
333f54de4cSJeremy Kerr 	struct handler handler;
343f54de4cSJeremy Kerr 	struct console *console;
35f733c85aSJeremy Kerr 	struct ringbuffer_consumer *rbc;
363f54de4cSJeremy Kerr 	int fd;
373f54de4cSJeremy Kerr 	size_t size;
383f54de4cSJeremy Kerr 	size_t maxsize;
398f548f6cSAndrew Jeffery 	size_t pagesize;
4093fd8a39SLei YU 	char *log_filename;
4193fd8a39SLei YU 	char *rotate_filename;
423f54de4cSJeremy Kerr };
433f54de4cSJeremy Kerr 
44e440a407SJeremy Kerr static const char *default_filename = LOCALSTATEDIR "/log/obmc-console.log";
455db8c792SAndrew Jeffery static const size_t default_logsize = 16ul * 1024ul;
463f54de4cSJeremy Kerr 
473f54de4cSJeremy Kerr static struct log_handler *to_log_handler(struct handler *handler)
483f54de4cSJeremy Kerr {
493f54de4cSJeremy Kerr 	return container_of(handler, struct log_handler, handler);
503f54de4cSJeremy Kerr }
513f54de4cSJeremy Kerr 
52fd048328SAndrew Jeffery static int log_trim(struct log_handler *lh)
533f54de4cSJeremy Kerr {
5493fd8a39SLei YU 	int rc;
553f54de4cSJeremy Kerr 
5693fd8a39SLei YU 	/* Move the log buffer file to the rotate file */
5793fd8a39SLei YU 	close(lh->fd);
5893fd8a39SLei YU 	rc = rename(lh->log_filename, lh->rotate_filename);
5993fd8a39SLei YU 	if (rc) {
60a72711afSAndrew Jeffery 		warn("Failed to rename %s to %s", lh->log_filename,
61a72711afSAndrew Jeffery 		     lh->rotate_filename);
6293fd8a39SLei YU 		/* don't return, as we need to re-open the logfile */
6393fd8a39SLei YU 	}
643f54de4cSJeremy Kerr 
6546d9ef29SJohn Wang 	lh->fd = open(lh->log_filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
6693fd8a39SLei YU 	if (lh->fd < 0) {
6793fd8a39SLei YU 		warn("Can't open log buffer file %s", lh->log_filename);
683f54de4cSJeremy Kerr 		return -1;
6993fd8a39SLei YU 	}
703f54de4cSJeremy Kerr 
7193fd8a39SLei YU 	lh->size = 0;
723f54de4cSJeremy Kerr 
733f54de4cSJeremy Kerr 	return 0;
743f54de4cSJeremy Kerr }
753f54de4cSJeremy Kerr 
76f733c85aSJeremy Kerr static int log_data(struct log_handler *lh, uint8_t *buf, size_t len)
773f54de4cSJeremy Kerr {
783f54de4cSJeremy Kerr 	int rc;
793f54de4cSJeremy Kerr 
803f54de4cSJeremy Kerr 	if (len > lh->maxsize) {
813f54de4cSJeremy Kerr 		buf += len - lh->maxsize;
823f54de4cSJeremy Kerr 		len = lh->maxsize;
833f54de4cSJeremy Kerr 	}
843f54de4cSJeremy Kerr 
853f54de4cSJeremy Kerr 	if (lh->size + len > lh->maxsize) {
86fd048328SAndrew Jeffery 		rc = log_trim(lh);
872834c5b1SAndrew Jeffery 		if (rc) {
883f54de4cSJeremy Kerr 			return rc;
893f54de4cSJeremy Kerr 		}
902834c5b1SAndrew Jeffery 	}
913f54de4cSJeremy Kerr 
923f54de4cSJeremy Kerr 	rc = write_buf_to_fd(lh->fd, buf, len);
932834c5b1SAndrew Jeffery 	if (rc) {
943f54de4cSJeremy Kerr 		return rc;
952834c5b1SAndrew Jeffery 	}
963f54de4cSJeremy Kerr 
973f54de4cSJeremy Kerr 	lh->size += len;
983f54de4cSJeremy Kerr 
993f54de4cSJeremy Kerr 	return 0;
1003f54de4cSJeremy Kerr }
1013f54de4cSJeremy Kerr 
102a72711afSAndrew Jeffery static enum ringbuffer_poll_ret log_ringbuffer_poll(void *arg, size_t force_len
103a72711afSAndrew Jeffery 						    __attribute__((unused)))
104f733c85aSJeremy Kerr {
105f733c85aSJeremy Kerr 	struct log_handler *lh = arg;
106f733c85aSJeremy Kerr 	uint8_t *buf;
107f733c85aSJeremy Kerr 	size_t len;
108f733c85aSJeremy Kerr 	int rc;
109f733c85aSJeremy Kerr 
110f733c85aSJeremy Kerr 	/* we log synchronously, so just dequeue everything we can, and
111f733c85aSJeremy Kerr 	 * commit straight away. */
112f733c85aSJeremy Kerr 	for (;;) {
113f733c85aSJeremy Kerr 		len = ringbuffer_dequeue_peek(lh->rbc, 0, &buf);
1142834c5b1SAndrew Jeffery 		if (!len) {
115f733c85aSJeremy Kerr 			break;
1162834c5b1SAndrew Jeffery 		}
117f733c85aSJeremy Kerr 
118f733c85aSJeremy Kerr 		rc = log_data(lh, buf, len);
1192834c5b1SAndrew Jeffery 		if (rc) {
120f733c85aSJeremy Kerr 			return RINGBUFFER_POLL_REMOVE;
1212834c5b1SAndrew Jeffery 		}
122f733c85aSJeremy Kerr 
123f733c85aSJeremy Kerr 		ringbuffer_dequeue_commit(lh->rbc, len);
124f733c85aSJeremy Kerr 	}
125f733c85aSJeremy Kerr 
126f733c85aSJeremy Kerr 	return RINGBUFFER_POLL_OK;
127f733c85aSJeremy Kerr }
128f733c85aSJeremy Kerr 
12946d9ef29SJohn Wang static int log_create(struct log_handler *lh)
13046d9ef29SJohn Wang {
13146d9ef29SJohn Wang 	off_t pos;
13246d9ef29SJohn Wang 
13346d9ef29SJohn Wang 	lh->fd = open(lh->log_filename, O_WRONLY | O_CREAT | O_APPEND, 0644);
13446d9ef29SJohn Wang 	if (lh->fd < 0) {
13546d9ef29SJohn Wang 		warn("Can't open log buffer file %s", lh->log_filename);
13646d9ef29SJohn Wang 		return -1;
13746d9ef29SJohn Wang 	}
13846d9ef29SJohn Wang 	pos = lseek(lh->fd, 0, SEEK_CUR);
13946d9ef29SJohn Wang 	if (pos < 0) {
14046d9ef29SJohn Wang 		warn("Can't query log position for file %s", lh->log_filename);
14146d9ef29SJohn Wang 		close(lh->fd);
14246d9ef29SJohn Wang 		return -1;
14346d9ef29SJohn Wang 	}
14446d9ef29SJohn Wang 	lh->size = pos;
14546d9ef29SJohn Wang 	if ((size_t)pos >= lh->maxsize) {
14646d9ef29SJohn Wang 		return log_trim(lh);
14746d9ef29SJohn Wang 	}
14846d9ef29SJohn Wang 
14946d9ef29SJohn Wang 	return 0;
15046d9ef29SJohn Wang }
15146d9ef29SJohn Wang 
152e2826c7dSJeremy Kerr static struct handler *log_init(const struct handler_type *type
153e2826c7dSJeremy Kerr 				__attribute__((unused)),
154e2826c7dSJeremy Kerr 				struct console *console, struct config *config)
155f733c85aSJeremy Kerr {
156e2826c7dSJeremy Kerr 	struct log_handler *lh;
157b70f8713SAndrew Jeffery 	const char *filename;
158b70f8713SAndrew Jeffery 	const char *logsize_str;
15918644357SKun Yi 	size_t logsize = default_logsize;
160f733c85aSJeremy Kerr 	int rc;
161f733c85aSJeremy Kerr 
162e2826c7dSJeremy Kerr 	lh = malloc(sizeof(*lh));
163e2826c7dSJeremy Kerr 	if (!lh) {
164e2826c7dSJeremy Kerr 		return NULL;
165e2826c7dSJeremy Kerr 	}
166e2826c7dSJeremy Kerr 
167f733c85aSJeremy Kerr 	lh->console = console;
168f733c85aSJeremy Kerr 	lh->pagesize = 4096;
169f733c85aSJeremy Kerr 	lh->size = 0;
17093fd8a39SLei YU 	lh->log_filename = NULL;
17193fd8a39SLei YU 	lh->rotate_filename = NULL;
172f733c85aSJeremy Kerr 
1736424cc3bSKun Yi 	logsize_str = config_get_value(config, "logsize");
174d6e8b64aSMedicine Yeh 	rc = config_parse_bytesize(logsize_str, &logsize);
1756424cc3bSKun Yi 	if (logsize_str != NULL && rc) {
1766424cc3bSKun Yi 		logsize = default_logsize;
1776424cc3bSKun Yi 		warn("Invalid logsize. Default to %ukB",
1786424cc3bSKun Yi 		     (unsigned int)(logsize >> 10));
1796424cc3bSKun Yi 	}
1806424cc3bSKun Yi 	lh->maxsize = logsize <= lh->pagesize ? lh->pagesize + 1 : logsize;
1816424cc3bSKun Yi 
182f733c85aSJeremy Kerr 	filename = config_get_value(config, "logfile");
1832834c5b1SAndrew Jeffery 	if (!filename) {
184f733c85aSJeremy Kerr 		filename = default_filename;
1852834c5b1SAndrew Jeffery 	}
186f733c85aSJeremy Kerr 
18793fd8a39SLei YU 	lh->log_filename = strdup(filename);
18893fd8a39SLei YU 
18993fd8a39SLei YU 	rc = asprintf(&lh->rotate_filename, "%s.1", filename);
19093fd8a39SLei YU 	if (rc < 0) {
19193fd8a39SLei YU 		warn("Failed to construct rotate filename");
192e2826c7dSJeremy Kerr 		goto err_free;
193f733c85aSJeremy Kerr 	}
194f733c85aSJeremy Kerr 
19546d9ef29SJohn Wang 	rc = log_create(lh);
19646d9ef29SJohn Wang 	if (rc < 0) {
197e2826c7dSJeremy Kerr 		goto err_free;
19846d9ef29SJohn Wang 	}
199f733c85aSJeremy Kerr 	lh->rbc = console_ringbuffer_consumer_register(console,
200f733c85aSJeremy Kerr 						       log_ringbuffer_poll, lh);
201f733c85aSJeremy Kerr 
202e2826c7dSJeremy Kerr 	return &lh->handler;
203e2826c7dSJeremy Kerr 
204e2826c7dSJeremy Kerr err_free:
205*48d1f533SAndrew Jeffery 	free(lh->rotate_filename);
206e2826c7dSJeremy Kerr 	free(lh->log_filename);
207e2826c7dSJeremy Kerr 	free(lh);
208e2826c7dSJeremy Kerr 	return NULL;
209f733c85aSJeremy Kerr }
210f733c85aSJeremy Kerr 
2113f54de4cSJeremy Kerr static void log_fini(struct handler *handler)
2123f54de4cSJeremy Kerr {
2133f54de4cSJeremy Kerr 	struct log_handler *lh = to_log_handler(handler);
214f733c85aSJeremy Kerr 	ringbuffer_consumer_unregister(lh->rbc);
2153f54de4cSJeremy Kerr 	close(lh->fd);
21693fd8a39SLei YU 	free(lh->log_filename);
21793fd8a39SLei YU 	free(lh->rotate_filename);
218e2826c7dSJeremy Kerr 	free(lh);
2193f54de4cSJeremy Kerr }
2203f54de4cSJeremy Kerr 
221e2826c7dSJeremy Kerr static const struct handler_type log_handler = {
2223f54de4cSJeremy Kerr 	.name = "log",
2233f54de4cSJeremy Kerr 	.init = log_init,
2243f54de4cSJeremy Kerr 	.fini = log_fini,
2253f54de4cSJeremy Kerr };
2263f54de4cSJeremy Kerr 
227e2826c7dSJeremy Kerr console_handler_register(&log_handler);
228