xref: /openbmc/obmc-console/log-handler.c (revision 9836e2b3)
1 /**
2  * Copyright © 2016 IBM Corporation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <endian.h>
18 #include <err.h>
19 #include <fcntl.h>
20 #include <stdbool.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 
26 #include <sys/mman.h>
27 
28 #include <linux/types.h>
29 
30 #include "console-server.h"
31 
32 struct log_handler {
33 	struct handler			handler;
34 	struct console			*console;
35 	struct ringbuffer_consumer	*rbc;
36 	int				fd;
37 	size_t				size;
38 	size_t				maxsize;
39 	int				pagesize;
40 };
41 
42 
43 static const char *default_filename = LOCALSTATEDIR "/log/obmc-console.log";
44 static const size_t default_logsize = 16 * 1024;
45 
46 static struct log_handler *to_log_handler(struct handler *handler)
47 {
48 	return container_of(handler, struct log_handler, handler);
49 }
50 
51 static int log_trim(struct log_handler *lh, size_t space)
52 {
53 	int rc, n_shift_pages, shift_len, shift_start;
54 	off_t pos;
55 	void *buf;
56 
57 	pos = lseek(lh->fd, 0, SEEK_CUR);
58 
59 	n_shift_pages = (space + lh->pagesize - 1) / lh->pagesize;
60 	shift_start = n_shift_pages * lh->pagesize;
61 	shift_len = pos - (n_shift_pages * lh->pagesize);
62 
63 	buf = mmap(NULL, pos, PROT_READ | PROT_WRITE, MAP_SHARED, lh->fd, 0);
64 	if (buf == MAP_FAILED)
65 		return -1;
66 
67 	memmove(buf, buf + shift_start, shift_len);
68 
69 	munmap(buf, pos);
70 
71 	lh->size = shift_len;
72 	rc = ftruncate(lh->fd, lh->size);
73 	if (rc)
74 		warn("failed to truncate file");
75 	lseek(lh->fd, 0, SEEK_END);
76 
77 	return 0;
78 
79 }
80 
81 static int log_data(struct log_handler *lh, uint8_t *buf, size_t len)
82 {
83 	int rc;
84 
85 	if (len > lh->maxsize) {
86 		buf += len - lh->maxsize;
87 		len = lh->maxsize;
88 	}
89 
90 	if (lh->size + len > lh->maxsize) {
91 		rc = log_trim(lh, len);
92 		if (rc)
93 			return rc;
94 	}
95 
96 	rc = write_buf_to_fd(lh->fd, buf, len);
97 	if (rc)
98 		return rc;
99 
100 	lh->size += len;
101 
102 	return 0;
103 }
104 
105 static enum ringbuffer_poll_ret log_ringbuffer_poll(void *arg,
106 		size_t force_len __attribute__((unused)))
107 {
108 	struct log_handler *lh = arg;
109 	uint8_t *buf;
110 	size_t len;
111 	int rc;
112 
113 	/* we log synchronously, so just dequeue everything we can, and
114 	 * commit straight away. */
115 	for (;;) {
116 		len = ringbuffer_dequeue_peek(lh->rbc, 0, &buf);
117 		if (!len)
118 			break;
119 
120 		rc = log_data(lh, buf, len);
121 		if (rc)
122 			return RINGBUFFER_POLL_REMOVE;
123 
124 		ringbuffer_dequeue_commit(lh->rbc, len);
125 	}
126 
127 	return RINGBUFFER_POLL_OK;
128 }
129 
130 static int log_init(struct handler *handler, struct console *console,
131 		struct config *config)
132 {
133 	struct log_handler *lh = to_log_handler(handler);
134 	const char *filename, *logsize_str;
135 	size_t logsize = default_logsize;
136 	int rc;
137 
138 	lh->console = console;
139 	lh->pagesize = 4096;
140 	lh->size = 0;
141 
142 	logsize_str = config_get_value(config, "logsize");
143 	rc = config_parse_logsize(logsize_str, &logsize);
144 	if (logsize_str != NULL && rc) {
145 		logsize = default_logsize;
146 		warn("Invalid logsize. Default to %ukB",
147                      (unsigned int)(logsize >> 10));
148 	}
149 	lh->maxsize = logsize <= lh->pagesize ? lh->pagesize + 1 : logsize;
150 
151 	filename = config_get_value(config, "logfile");
152 	if (!filename)
153 		filename = default_filename;
154 
155 	lh->fd = open(filename, O_RDWR | O_CREAT, 0644);
156 	if (lh->fd < 0) {
157 		warn("Can't open log buffer file %s", filename);
158 		return -1;
159 	}
160 	rc = ftruncate(lh->fd, 0);
161 	if (rc) {
162 		warn("Can't truncate file %s", filename);
163 		close(lh->fd);
164 		return -1;
165 	}
166 
167 	lh->rbc = console_ringbuffer_consumer_register(console,
168 			log_ringbuffer_poll, lh);
169 
170 	return 0;
171 }
172 
173 static void log_fini(struct handler *handler)
174 {
175 	struct log_handler *lh = to_log_handler(handler);
176 	ringbuffer_consumer_unregister(lh->rbc);
177 	close(lh->fd);
178 }
179 
180 static struct log_handler log_handler = {
181 	.handler = {
182 		.name		= "log",
183 		.init		= log_init,
184 		.fini		= log_fini,
185 	},
186 };
187 
188 console_handler_register(&log_handler.handler);
189 
190