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