xref: /openbmc/obmc-console/log-handler.c (revision cc07530f)
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 #define _GNU_SOURCE
18 #include <endian.h>
19 #include <err.h>
20 #include <fcntl.h>
21 #include <stdbool.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 
27 #include <sys/mman.h>
28 
29 #include <linux/types.h>
30 
31 #include "console-server.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 	int				pagesize;
41 	char				*log_filename;
42 	char				*rotate_filename;
43 };
44 
45 
46 static const char *default_filename = LOCALSTATEDIR "/log/obmc-console.log";
47 static const size_t default_logsize = 16 * 1024;
48 
49 static struct log_handler *to_log_handler(struct handler *handler)
50 {
51 	return container_of(handler, struct log_handler, handler);
52 }
53 
54 static int log_trim(struct log_handler *lh, size_t space)
55 {
56 	int rc;
57 
58 	/* Move the log buffer file to the rotate file */
59 	close(lh->fd);
60 	rc = rename(lh->log_filename, lh->rotate_filename);
61 	if (rc) {
62 		warn("Failed to rename %s to %s", lh->log_filename, 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_RDWR | 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 
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, len);
88 		if (rc)
89 			return rc;
90 	}
91 
92 	rc = write_buf_to_fd(lh->fd, buf, len);
93 	if (rc)
94 		return rc;
95 
96 	lh->size += len;
97 
98 	return 0;
99 }
100 
101 static enum ringbuffer_poll_ret log_ringbuffer_poll(void *arg,
102 		size_t force_len __attribute__((unused)))
103 {
104 	struct log_handler *lh = arg;
105 	uint8_t *buf;
106 	size_t len;
107 	int rc;
108 
109 	/* we log synchronously, so just dequeue everything we can, and
110 	 * commit straight away. */
111 	for (;;) {
112 		len = ringbuffer_dequeue_peek(lh->rbc, 0, &buf);
113 		if (!len)
114 			break;
115 
116 		rc = log_data(lh, buf, len);
117 		if (rc)
118 			return RINGBUFFER_POLL_REMOVE;
119 
120 		ringbuffer_dequeue_commit(lh->rbc, len);
121 	}
122 
123 	return RINGBUFFER_POLL_OK;
124 }
125 
126 static int log_init(struct handler *handler, struct console *console,
127 		struct config *config)
128 {
129 	struct log_handler *lh = to_log_handler(handler);
130 	const char *filename, *logsize_str;
131 	size_t logsize = default_logsize;
132 	int rc;
133 
134 	lh->console = console;
135 	lh->pagesize = 4096;
136 	lh->size = 0;
137 	lh->log_filename = NULL;
138 	lh->rotate_filename = NULL;
139 
140 	logsize_str = config_get_value(config, "logsize");
141 	rc = config_parse_logsize(logsize_str, &logsize);
142 	if (logsize_str != NULL && rc) {
143 		logsize = default_logsize;
144 		warn("Invalid logsize. Default to %ukB",
145                      (unsigned int)(logsize >> 10));
146 	}
147 	lh->maxsize = logsize <= lh->pagesize ? lh->pagesize + 1 : logsize;
148 
149 	filename = config_get_value(config, "logfile");
150 	if (!filename)
151 		filename = default_filename;
152 
153 	lh->fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, 0644);
154 	if (lh->fd < 0) {
155 		warn("Can't open log buffer file %s", filename);
156 		return -1;
157 	}
158 
159 	lh->log_filename = strdup(filename);
160 
161 	rc = asprintf(&lh->rotate_filename, "%s.1", filename);
162 	if (rc < 0) {
163 		warn("Failed to construct rotate filename");
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 	free(lh->log_filename);
179 	free(lh->rotate_filename);
180 }
181 
182 static struct log_handler log_handler = {
183 	.handler = {
184 		.name		= "log",
185 		.init		= log_init,
186 		.fini		= log_fini,
187 	},
188 };
189 
190 console_handler_register(&log_handler.handler);
191 
192