xref: /openbmc/obmc-console/log-handler.c (revision 366651d9)
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 #include "config.h"
32 
33 struct log_handler {
34 	struct handler handler;
35 	struct console *console;
36 	struct ringbuffer_consumer *rbc;
37 	int fd;
38 	size_t size;
39 	size_t maxsize;
40 	size_t pagesize;
41 	char *log_filename;
42 	char *rotate_filename;
43 };
44 
45 static const char *default_filename = LOCALSTATEDIR "/log/obmc-console.log";
46 static const size_t default_logsize = 16ul * 1024ul;
47 
to_log_handler(struct handler * handler)48 static struct log_handler *to_log_handler(struct handler *handler)
49 {
50 	return container_of(handler, struct log_handler, handler);
51 }
52 
log_trim(struct log_handler * lh)53 static int log_trim(struct log_handler *lh)
54 {
55 	int rc;
56 
57 	/* Move the log buffer file to the rotate file */
58 	close(lh->fd);
59 	rc = rename(lh->log_filename, lh->rotate_filename);
60 	if (rc) {
61 		warn("Failed to rename %s to %s", lh->log_filename,
62 		     lh->rotate_filename);
63 		/* don't return, as we need to re-open the logfile */
64 	}
65 
66 	lh->fd = open(lh->log_filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
67 	if (lh->fd < 0) {
68 		warn("Can't open log buffer file %s", lh->log_filename);
69 		return -1;
70 	}
71 
72 	lh->size = 0;
73 
74 	return 0;
75 }
76 
log_data(struct log_handler * lh,uint8_t * buf,size_t len)77 static int log_data(struct log_handler *lh, uint8_t *buf, size_t len)
78 {
79 	int rc;
80 
81 	if (len > lh->maxsize) {
82 		buf += len - lh->maxsize;
83 		len = lh->maxsize;
84 	}
85 
86 	if (lh->size + len > lh->maxsize) {
87 		rc = log_trim(lh);
88 		if (rc) {
89 			return rc;
90 		}
91 	}
92 
93 	rc = write_buf_to_fd(lh->fd, buf, len);
94 	if (rc) {
95 		return rc;
96 	}
97 
98 	lh->size += len;
99 
100 	return 0;
101 }
102 
log_ringbuffer_poll(void * arg,size_t force_len)103 static enum ringbuffer_poll_ret log_ringbuffer_poll(void *arg, size_t force_len
104 						    __attribute__((unused)))
105 {
106 	struct log_handler *lh = arg;
107 	uint8_t *buf;
108 	size_t len;
109 	int rc;
110 
111 	/* we log synchronously, so just dequeue everything we can, and
112 	 * commit straight away. */
113 	for (;;) {
114 		len = ringbuffer_dequeue_peek(lh->rbc, 0, &buf);
115 		if (!len) {
116 			break;
117 		}
118 
119 		rc = log_data(lh, buf, len);
120 		if (rc) {
121 			return RINGBUFFER_POLL_REMOVE;
122 		}
123 
124 		ringbuffer_dequeue_commit(lh->rbc, len);
125 	}
126 
127 	return RINGBUFFER_POLL_OK;
128 }
129 
log_create(struct log_handler * lh)130 static int log_create(struct log_handler *lh)
131 {
132 	off_t pos;
133 
134 	lh->fd = open(lh->log_filename, O_WRONLY | O_CREAT | O_APPEND, 0644);
135 	if (lh->fd < 0) {
136 		warn("Can't open log buffer file %s", lh->log_filename);
137 		return -1;
138 	}
139 	pos = lseek(lh->fd, 0, SEEK_END);
140 	if (pos < 0) {
141 		warn("Can't query log position for file %s", lh->log_filename);
142 		close(lh->fd);
143 		return -1;
144 	}
145 	lh->size = pos;
146 	if ((size_t)pos >= lh->maxsize) {
147 		return log_trim(lh);
148 	}
149 
150 	return 0;
151 }
152 
log_init(const struct handler_type * type,struct console * console,struct config * config)153 static struct handler *log_init(const struct handler_type *type
154 				__attribute__((unused)),
155 				struct console *console, struct config *config)
156 {
157 	struct log_handler *lh;
158 	const char *filename;
159 	const char *logsize_str;
160 	size_t logsize = default_logsize;
161 	int rc;
162 
163 	lh = malloc(sizeof(*lh));
164 	if (!lh) {
165 		return NULL;
166 	}
167 
168 	lh->console = console;
169 	lh->pagesize = 4096;
170 	lh->size = 0;
171 	lh->log_filename = NULL;
172 	lh->rotate_filename = NULL;
173 
174 	logsize_str = config_get_value(config, "logsize");
175 	rc = config_parse_bytesize(logsize_str, &logsize);
176 	if (logsize_str != NULL && rc) {
177 		logsize = default_logsize;
178 		warn("Invalid logsize. Default to %ukB",
179 		     (unsigned int)(logsize >> 10));
180 	}
181 	lh->maxsize = logsize <= lh->pagesize ? lh->pagesize + 1 : logsize;
182 
183 	filename = config_get_section_value(config, console->console_id,
184 					    "logfile");
185 
186 	if (!filename && config_count_sections(config) == 0) {
187 		filename = config_get_value(config, "logfile");
188 	}
189 
190 	if (!filename) {
191 		filename = default_filename;
192 	}
193 
194 	lh->log_filename = strdup(filename);
195 
196 	rc = asprintf(&lh->rotate_filename, "%s.1", filename);
197 	if (rc < 0) {
198 		warn("Failed to construct rotate filename");
199 		goto err_free;
200 	}
201 
202 	rc = log_create(lh);
203 	if (rc < 0) {
204 		goto err_free;
205 	}
206 	lh->rbc = console_ringbuffer_consumer_register(console,
207 						       log_ringbuffer_poll, lh);
208 
209 	return &lh->handler;
210 
211 err_free:
212 	free(lh->rotate_filename);
213 	free(lh->log_filename);
214 	free(lh);
215 	return NULL;
216 }
217 
log_fini(struct handler * handler)218 static void log_fini(struct handler *handler)
219 {
220 	struct log_handler *lh = to_log_handler(handler);
221 	ringbuffer_consumer_unregister(lh->rbc);
222 	close(lh->fd);
223 	free(lh->log_filename);
224 	free(lh->rotate_filename);
225 	free(lh);
226 }
227 
228 static const struct handler_type log_handler = {
229 	.name = "log",
230 	.init = log_init,
231 	.fini = log_fini,
232 };
233 
234 console_handler_register(&log_handler);
235