blob: 149806c91ef6bb63e48d258189bbd6e65d17ce21 [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";
Jeremy Kerrdf94dc12016-03-16 12:19:49 +080044
Jeremy Kerr3f54de42016-03-09 18:10:15 +080045static const size_t logsize = 16 * 1024;
46
47static struct log_handler *to_log_handler(struct handler *handler)
48{
49 return container_of(handler, struct log_handler, handler);
50}
51
Jeremy Kerr3f54de42016-03-09 18:10:15 +080052static int log_trim(struct log_handler *lh, size_t space)
53{
54 int rc, n_shift_pages, shift_len, shift_start;
55 off_t pos;
56 void *buf;
57
58 pos = lseek(lh->fd, 0, SEEK_CUR);
59
60 n_shift_pages = (space + lh->pagesize - 1) / lh->pagesize;
61 shift_start = n_shift_pages * lh->pagesize;
62 shift_len = pos - (n_shift_pages * lh->pagesize);
63
64 buf = mmap(NULL, pos, PROT_READ | PROT_WRITE, MAP_SHARED, lh->fd, 0);
65 if (buf == MAP_FAILED)
66 return -1;
67
68 memmove(buf, buf + shift_start, shift_len);
69
70 munmap(buf, pos);
71
72 lh->size = shift_len;
73 rc = ftruncate(lh->fd, lh->size);
74 if (rc)
75 warn("failed to truncate file");
76 lseek(lh->fd, 0, SEEK_END);
77
78 return 0;
79
80}
81
Jeremy Kerrf733c852017-02-07 18:40:10 +080082static int log_data(struct log_handler *lh, uint8_t *buf, size_t len)
Jeremy Kerr3f54de42016-03-09 18:10:15 +080083{
Jeremy Kerr3f54de42016-03-09 18:10:15 +080084 int rc;
85
86 if (len > lh->maxsize) {
87 buf += len - lh->maxsize;
88 len = lh->maxsize;
89 }
90
91 if (lh->size + len > lh->maxsize) {
92 rc = log_trim(lh, len);
93 if (rc)
94 return rc;
95 }
96
97 rc = write_buf_to_fd(lh->fd, buf, len);
98 if (rc)
99 return rc;
100
101 lh->size += len;
102
103 return 0;
104}
105
Jeremy Kerrf733c852017-02-07 18:40:10 +0800106static enum ringbuffer_poll_ret log_ringbuffer_poll(void *arg,
107 size_t force_len __attribute__((unused)))
108{
109 struct log_handler *lh = arg;
110 uint8_t *buf;
111 size_t len;
112 int rc;
113
114 /* we log synchronously, so just dequeue everything we can, and
115 * commit straight away. */
116 for (;;) {
117 len = ringbuffer_dequeue_peek(lh->rbc, 0, &buf);
118 if (!len)
119 break;
120
121 rc = log_data(lh, buf, len);
122 if (rc)
123 return RINGBUFFER_POLL_REMOVE;
124
125 ringbuffer_dequeue_commit(lh->rbc, len);
126 }
127
128 return RINGBUFFER_POLL_OK;
129}
130
131static int log_init(struct handler *handler, struct console *console,
132 struct config *config __attribute__((unused)))
133{
134 struct log_handler *lh = to_log_handler(handler);
135 const char *filename;
136 int rc;
137
138 lh->console = console;
139 lh->maxsize = logsize;
140 lh->pagesize = 4096;
141 lh->size = 0;
142
143 filename = config_get_value(config, "logfile");
144 if (!filename)
145 filename = default_filename;
146
147 lh->fd = open(filename, O_RDWR | O_CREAT, 0644);
148 if (lh->fd < 0) {
149 warn("Can't open log buffer file %s", filename);
150 return -1;
151 }
152 rc = ftruncate(lh->fd, 0);
153 if (rc) {
154 warn("Can't truncate file %s", filename);
155 close(lh->fd);
156 return -1;
157 }
158
159 lh->rbc = console_ringbuffer_consumer_register(console,
160 log_ringbuffer_poll, lh);
161
162 return 0;
163}
164
Jeremy Kerr3f54de42016-03-09 18:10:15 +0800165static void log_fini(struct handler *handler)
166{
167 struct log_handler *lh = to_log_handler(handler);
Jeremy Kerrf733c852017-02-07 18:40:10 +0800168 ringbuffer_consumer_unregister(lh->rbc);
Jeremy Kerr3f54de42016-03-09 18:10:15 +0800169 close(lh->fd);
170}
171
172static struct log_handler log_handler = {
173 .handler = {
174 .name = "log",
175 .init = log_init,
Jeremy Kerr3f54de42016-03-09 18:10:15 +0800176 .fini = log_fini,
177 },
178};
179
Jeremy Kerr55c97122017-02-07 17:06:46 +0800180console_handler_register(&log_handler.handler);
Jeremy Kerr3f54de42016-03-09 18:10:15 +0800181