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"
311e04f449SAlexander Hansen #include "config.h"
323f54de4cSJeremy Kerr
333f54de4cSJeremy Kerr struct log_handler {
343f54de4cSJeremy Kerr struct handler handler;
353f54de4cSJeremy Kerr struct console *console;
36f733c85aSJeremy Kerr struct ringbuffer_consumer *rbc;
373f54de4cSJeremy Kerr int fd;
383f54de4cSJeremy Kerr size_t size;
393f54de4cSJeremy Kerr size_t maxsize;
408f548f6cSAndrew Jeffery size_t pagesize;
4193fd8a39SLei YU char *log_filename;
4293fd8a39SLei YU char *rotate_filename;
433f54de4cSJeremy Kerr };
443f54de4cSJeremy Kerr
45e440a407SJeremy Kerr static const char *default_filename = LOCALSTATEDIR "/log/obmc-console.log";
465db8c792SAndrew Jeffery static const size_t default_logsize = 16ul * 1024ul;
473f54de4cSJeremy Kerr
to_log_handler(struct handler * handler)483f54de4cSJeremy Kerr static struct log_handler *to_log_handler(struct handler *handler)
493f54de4cSJeremy Kerr {
503f54de4cSJeremy Kerr return container_of(handler, struct log_handler, handler);
513f54de4cSJeremy Kerr }
523f54de4cSJeremy Kerr
log_trim(struct log_handler * lh)53fd048328SAndrew Jeffery static int log_trim(struct log_handler *lh)
543f54de4cSJeremy Kerr {
5593fd8a39SLei YU int rc;
563f54de4cSJeremy Kerr
5793fd8a39SLei YU /* Move the log buffer file to the rotate file */
5893fd8a39SLei YU close(lh->fd);
5993fd8a39SLei YU rc = rename(lh->log_filename, lh->rotate_filename);
6093fd8a39SLei YU if (rc) {
61a72711afSAndrew Jeffery warn("Failed to rename %s to %s", lh->log_filename,
62a72711afSAndrew Jeffery lh->rotate_filename);
6393fd8a39SLei YU /* don't return, as we need to re-open the logfile */
6493fd8a39SLei YU }
653f54de4cSJeremy Kerr
6646d9ef29SJohn Wang lh->fd = open(lh->log_filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
6793fd8a39SLei YU if (lh->fd < 0) {
6893fd8a39SLei YU warn("Can't open log buffer file %s", lh->log_filename);
693f54de4cSJeremy Kerr return -1;
7093fd8a39SLei YU }
713f54de4cSJeremy Kerr
7293fd8a39SLei YU lh->size = 0;
733f54de4cSJeremy Kerr
743f54de4cSJeremy Kerr return 0;
753f54de4cSJeremy Kerr }
763f54de4cSJeremy Kerr
log_data(struct log_handler * lh,uint8_t * buf,size_t len)77f733c85aSJeremy Kerr static int log_data(struct log_handler *lh, uint8_t *buf, size_t len)
783f54de4cSJeremy Kerr {
793f54de4cSJeremy Kerr int rc;
803f54de4cSJeremy Kerr
813f54de4cSJeremy Kerr if (len > lh->maxsize) {
823f54de4cSJeremy Kerr buf += len - lh->maxsize;
833f54de4cSJeremy Kerr len = lh->maxsize;
843f54de4cSJeremy Kerr }
853f54de4cSJeremy Kerr
863f54de4cSJeremy Kerr if (lh->size + len > lh->maxsize) {
87fd048328SAndrew Jeffery rc = log_trim(lh);
882834c5b1SAndrew Jeffery if (rc) {
893f54de4cSJeremy Kerr return rc;
903f54de4cSJeremy Kerr }
912834c5b1SAndrew Jeffery }
923f54de4cSJeremy Kerr
933f54de4cSJeremy Kerr rc = write_buf_to_fd(lh->fd, buf, len);
942834c5b1SAndrew Jeffery if (rc) {
953f54de4cSJeremy Kerr return rc;
962834c5b1SAndrew Jeffery }
973f54de4cSJeremy Kerr
983f54de4cSJeremy Kerr lh->size += len;
993f54de4cSJeremy Kerr
1003f54de4cSJeremy Kerr return 0;
1013f54de4cSJeremy Kerr }
1023f54de4cSJeremy Kerr
log_ringbuffer_poll(void * arg,size_t force_len)103a72711afSAndrew Jeffery static enum ringbuffer_poll_ret log_ringbuffer_poll(void *arg, size_t force_len
104a72711afSAndrew Jeffery __attribute__((unused)))
105f733c85aSJeremy Kerr {
106f733c85aSJeremy Kerr struct log_handler *lh = arg;
107f733c85aSJeremy Kerr uint8_t *buf;
108f733c85aSJeremy Kerr size_t len;
109f733c85aSJeremy Kerr int rc;
110f733c85aSJeremy Kerr
111f733c85aSJeremy Kerr /* we log synchronously, so just dequeue everything we can, and
112f733c85aSJeremy Kerr * commit straight away. */
113f733c85aSJeremy Kerr for (;;) {
114f733c85aSJeremy Kerr len = ringbuffer_dequeue_peek(lh->rbc, 0, &buf);
1152834c5b1SAndrew Jeffery if (!len) {
116f733c85aSJeremy Kerr break;
1172834c5b1SAndrew Jeffery }
118f733c85aSJeremy Kerr
119f733c85aSJeremy Kerr rc = log_data(lh, buf, len);
1202834c5b1SAndrew Jeffery if (rc) {
121f733c85aSJeremy Kerr return RINGBUFFER_POLL_REMOVE;
1222834c5b1SAndrew Jeffery }
123f733c85aSJeremy Kerr
124f733c85aSJeremy Kerr ringbuffer_dequeue_commit(lh->rbc, len);
125f733c85aSJeremy Kerr }
126f733c85aSJeremy Kerr
127f733c85aSJeremy Kerr return RINGBUFFER_POLL_OK;
128f733c85aSJeremy Kerr }
129f733c85aSJeremy Kerr
log_create(struct log_handler * lh)13046d9ef29SJohn Wang static int log_create(struct log_handler *lh)
13146d9ef29SJohn Wang {
13246d9ef29SJohn Wang off_t pos;
13346d9ef29SJohn Wang
13446d9ef29SJohn Wang lh->fd = open(lh->log_filename, O_WRONLY | O_CREAT | O_APPEND, 0644);
13546d9ef29SJohn Wang if (lh->fd < 0) {
13646d9ef29SJohn Wang warn("Can't open log buffer file %s", lh->log_filename);
13746d9ef29SJohn Wang return -1;
13846d9ef29SJohn Wang }
139*366651d9SMarshall Zhan pos = lseek(lh->fd, 0, SEEK_END);
14046d9ef29SJohn Wang if (pos < 0) {
14146d9ef29SJohn Wang warn("Can't query log position for file %s", lh->log_filename);
14246d9ef29SJohn Wang close(lh->fd);
14346d9ef29SJohn Wang return -1;
14446d9ef29SJohn Wang }
14546d9ef29SJohn Wang lh->size = pos;
14646d9ef29SJohn Wang if ((size_t)pos >= lh->maxsize) {
14746d9ef29SJohn Wang return log_trim(lh);
14846d9ef29SJohn Wang }
14946d9ef29SJohn Wang
15046d9ef29SJohn Wang return 0;
15146d9ef29SJohn Wang }
15246d9ef29SJohn Wang
log_init(const struct handler_type * type,struct console * console,struct config * config)153e2826c7dSJeremy Kerr static struct handler *log_init(const struct handler_type *type
154e2826c7dSJeremy Kerr __attribute__((unused)),
155e2826c7dSJeremy Kerr struct console *console, struct config *config)
156f733c85aSJeremy Kerr {
157e2826c7dSJeremy Kerr struct log_handler *lh;
158b70f8713SAndrew Jeffery const char *filename;
159b70f8713SAndrew Jeffery const char *logsize_str;
16018644357SKun Yi size_t logsize = default_logsize;
161f733c85aSJeremy Kerr int rc;
162f733c85aSJeremy Kerr
163e2826c7dSJeremy Kerr lh = malloc(sizeof(*lh));
164e2826c7dSJeremy Kerr if (!lh) {
165e2826c7dSJeremy Kerr return NULL;
166e2826c7dSJeremy Kerr }
167e2826c7dSJeremy Kerr
168f733c85aSJeremy Kerr lh->console = console;
169f733c85aSJeremy Kerr lh->pagesize = 4096;
170f733c85aSJeremy Kerr lh->size = 0;
17193fd8a39SLei YU lh->log_filename = NULL;
17293fd8a39SLei YU lh->rotate_filename = NULL;
173f733c85aSJeremy Kerr
1746424cc3bSKun Yi logsize_str = config_get_value(config, "logsize");
175d6e8b64aSMedicine Yeh rc = config_parse_bytesize(logsize_str, &logsize);
1766424cc3bSKun Yi if (logsize_str != NULL && rc) {
1776424cc3bSKun Yi logsize = default_logsize;
1786424cc3bSKun Yi warn("Invalid logsize. Default to %ukB",
1796424cc3bSKun Yi (unsigned int)(logsize >> 10));
1806424cc3bSKun Yi }
1816424cc3bSKun Yi lh->maxsize = logsize <= lh->pagesize ? lh->pagesize + 1 : logsize;
1826424cc3bSKun Yi
183a6b29104SAlexander Hansen filename = config_get_section_value(config, console->console_id,
184a6b29104SAlexander Hansen "logfile");
185a6b29104SAlexander Hansen
1866498f9faSAlexander Hansen if (!filename && config_count_sections(config) == 0) {
187f733c85aSJeremy Kerr filename = config_get_value(config, "logfile");
188a6b29104SAlexander Hansen }
189a6b29104SAlexander Hansen
1902834c5b1SAndrew Jeffery if (!filename) {
191f733c85aSJeremy Kerr filename = default_filename;
1922834c5b1SAndrew Jeffery }
193f733c85aSJeremy Kerr
19493fd8a39SLei YU lh->log_filename = strdup(filename);
19593fd8a39SLei YU
19693fd8a39SLei YU rc = asprintf(&lh->rotate_filename, "%s.1", filename);
19793fd8a39SLei YU if (rc < 0) {
19893fd8a39SLei YU warn("Failed to construct rotate filename");
199e2826c7dSJeremy Kerr goto err_free;
200f733c85aSJeremy Kerr }
201f733c85aSJeremy Kerr
20246d9ef29SJohn Wang rc = log_create(lh);
20346d9ef29SJohn Wang if (rc < 0) {
204e2826c7dSJeremy Kerr goto err_free;
20546d9ef29SJohn Wang }
206f733c85aSJeremy Kerr lh->rbc = console_ringbuffer_consumer_register(console,
207f733c85aSJeremy Kerr log_ringbuffer_poll, lh);
208f733c85aSJeremy Kerr
209e2826c7dSJeremy Kerr return &lh->handler;
210e2826c7dSJeremy Kerr
211e2826c7dSJeremy Kerr err_free:
21248d1f533SAndrew Jeffery free(lh->rotate_filename);
213e2826c7dSJeremy Kerr free(lh->log_filename);
214e2826c7dSJeremy Kerr free(lh);
215e2826c7dSJeremy Kerr return NULL;
216f733c85aSJeremy Kerr }
217f733c85aSJeremy Kerr
log_fini(struct handler * handler)2183f54de4cSJeremy Kerr static void log_fini(struct handler *handler)
2193f54de4cSJeremy Kerr {
2203f54de4cSJeremy Kerr struct log_handler *lh = to_log_handler(handler);
221f733c85aSJeremy Kerr ringbuffer_consumer_unregister(lh->rbc);
2223f54de4cSJeremy Kerr close(lh->fd);
22393fd8a39SLei YU free(lh->log_filename);
22493fd8a39SLei YU free(lh->rotate_filename);
225e2826c7dSJeremy Kerr free(lh);
2263f54de4cSJeremy Kerr }
2273f54de4cSJeremy Kerr
228e2826c7dSJeremy Kerr static const struct handler_type log_handler = {
2293f54de4cSJeremy Kerr .name = "log",
2303f54de4cSJeremy Kerr .init = log_init,
2313f54de4cSJeremy Kerr .fini = log_fini,
2323f54de4cSJeremy Kerr };
2333f54de4cSJeremy Kerr
234e2826c7dSJeremy Kerr console_handler_register(&log_handler);
235