blob: 275cc8112aea3b0e712cfbff29e23aae987cf655 [file] [log] [blame]
Jeremy Kerr9326d772016-03-17 17:15:02 +08001/**
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 */
Jeremy Kerr3f54de42016-03-09 18:10:15 +080016
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
32struct log_handler {
Jeremy Kerrf733c852017-02-07 18:40:10 +080033 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;
Jeremy Kerr3f54de42016-03-09 18:10:15 +080040};
41
42
Jeremy Kerre440a402016-03-17 16:34:14 +080043static const char *default_filename = LOCALSTATEDIR "/log/obmc-console.log";
Kun Yi6424cc32018-06-14 14:09:28 -070044static const size_t default_logsize = 16 * 1024;
Jeremy Kerr3f54de42016-03-09 18:10:15 +080045
46static struct log_handler *to_log_handler(struct handler *handler)
47{
48 return container_of(handler, struct log_handler, handler);
49}
50
Jeremy Kerr3f54de42016-03-09 18:10:15 +080051static 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
Jeremy Kerrf733c852017-02-07 18:40:10 +080081static int log_data(struct log_handler *lh, uint8_t *buf, size_t len)
Jeremy Kerr3f54de42016-03-09 18:10:15 +080082{
Jeremy Kerr3f54de42016-03-09 18:10:15 +080083 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
Jeremy Kerrf733c852017-02-07 18:40:10 +0800105static 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
130static int log_init(struct handler *handler, struct console *console,
Kun Yi6424cc32018-06-14 14:09:28 -0700131 struct config *config)
Jeremy Kerrf733c852017-02-07 18:40:10 +0800132{
133 struct log_handler *lh = to_log_handler(handler);
Kun Yi6424cc32018-06-14 14:09:28 -0700134 const char *filename, *logsize_str;
Kun Yi18644352018-08-27 11:37:42 -0700135 size_t logsize = default_logsize;
Jeremy Kerrf733c852017-02-07 18:40:10 +0800136 int rc;
137
138 lh->console = console;
Jeremy Kerrf733c852017-02-07 18:40:10 +0800139 lh->pagesize = 4096;
140 lh->size = 0;
141
Kun Yi6424cc32018-06-14 14:09:28 -0700142 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
Jeremy Kerrf733c852017-02-07 18:40:10 +0800151 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
Jeremy Kerr3f54de42016-03-09 18:10:15 +0800173static void log_fini(struct handler *handler)
174{
175 struct log_handler *lh = to_log_handler(handler);
Jeremy Kerrf733c852017-02-07 18:40:10 +0800176 ringbuffer_consumer_unregister(lh->rbc);
Jeremy Kerr3f54de42016-03-09 18:10:15 +0800177 close(lh->fd);
178}
179
180static struct log_handler log_handler = {
181 .handler = {
182 .name = "log",
183 .init = log_init,
Jeremy Kerr3f54de42016-03-09 18:10:15 +0800184 .fini = log_fini,
185 },
186};
187
Jeremy Kerr55c97122017-02-07 17:06:46 +0800188console_handler_register(&log_handler.handler);
Jeremy Kerr3f54de42016-03-09 18:10:15 +0800189