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
32 struct log_handler {
33 struct handler handler;
34 struct console *console;
35 struct ringbuffer_consumer *rbc;
36 int fd;
37 size_t size;
38 size_t maxsize;
39 size_t pagesize;
40 char *log_filename;
41 char *rotate_filename;
42 };
43
44 static const char *default_filename = LOCALSTATEDIR "/log/obmc-console.log";
45 static const size_t default_logsize = 16ul * 1024ul;
46
to_log_handler(struct handler * handler)47 static struct log_handler *to_log_handler(struct handler *handler)
48 {
49 return container_of(handler, struct log_handler, handler);
50 }
51
log_trim(struct log_handler * lh)52 static int log_trim(struct log_handler *lh)
53 {
54 int rc;
55
56 /* Move the log buffer file to the rotate file */
57 close(lh->fd);
58 rc = rename(lh->log_filename, lh->rotate_filename);
59 if (rc) {
60 warn("Failed to rename %s to %s", lh->log_filename,
61 lh->rotate_filename);
62 /* don't return, as we need to re-open the logfile */
63 }
64
65 lh->fd = open(lh->log_filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
66 if (lh->fd < 0) {
67 warn("Can't open log buffer file %s", lh->log_filename);
68 return -1;
69 }
70
71 lh->size = 0;
72
73 return 0;
74 }
75
log_data(struct log_handler * lh,uint8_t * buf,size_t len)76 static int log_data(struct log_handler *lh, uint8_t *buf, size_t len)
77 {
78 int rc;
79
80 if (len > lh->maxsize) {
81 buf += len - lh->maxsize;
82 len = lh->maxsize;
83 }
84
85 if (lh->size + len > lh->maxsize) {
86 rc = log_trim(lh);
87 if (rc) {
88 return rc;
89 }
90 }
91
92 rc = write_buf_to_fd(lh->fd, buf, len);
93 if (rc) {
94 return rc;
95 }
96
97 lh->size += len;
98
99 return 0;
100 }
101
log_ringbuffer_poll(void * arg,size_t force_len)102 static enum ringbuffer_poll_ret log_ringbuffer_poll(void *arg, size_t force_len
103 __attribute__((unused)))
104 {
105 struct log_handler *lh = arg;
106 uint8_t *buf;
107 size_t len;
108 int rc;
109
110 /* we log synchronously, so just dequeue everything we can, and
111 * commit straight away. */
112 for (;;) {
113 len = ringbuffer_dequeue_peek(lh->rbc, 0, &buf);
114 if (!len) {
115 break;
116 }
117
118 rc = log_data(lh, buf, len);
119 if (rc) {
120 return RINGBUFFER_POLL_REMOVE;
121 }
122
123 ringbuffer_dequeue_commit(lh->rbc, len);
124 }
125
126 return RINGBUFFER_POLL_OK;
127 }
128
log_create(struct log_handler * lh)129 static int log_create(struct log_handler *lh)
130 {
131 off_t pos;
132
133 lh->fd = open(lh->log_filename, O_WRONLY | O_CREAT | O_APPEND, 0644);
134 if (lh->fd < 0) {
135 warn("Can't open log buffer file %s", lh->log_filename);
136 return -1;
137 }
138 pos = lseek(lh->fd, 0, SEEK_CUR);
139 if (pos < 0) {
140 warn("Can't query log position for file %s", lh->log_filename);
141 close(lh->fd);
142 return -1;
143 }
144 lh->size = pos;
145 if ((size_t)pos >= lh->maxsize) {
146 return log_trim(lh);
147 }
148
149 return 0;
150 }
151
log_init(struct handler * handler,struct console * console,struct config * config)152 static int log_init(struct handler *handler, struct console *console,
153 struct config *config)
154 {
155 struct log_handler *lh = to_log_handler(handler);
156 const char *filename;
157 const char *logsize_str;
158 size_t logsize = default_logsize;
159 int rc;
160
161 lh->console = console;
162 lh->pagesize = 4096;
163 lh->size = 0;
164 lh->log_filename = NULL;
165 lh->rotate_filename = NULL;
166
167 logsize_str = config_get_value(config, "logsize");
168 rc = config_parse_bytesize(logsize_str, &logsize);
169 if (logsize_str != NULL && rc) {
170 logsize = default_logsize;
171 warn("Invalid logsize. Default to %ukB",
172 (unsigned int)(logsize >> 10));
173 }
174 lh->maxsize = logsize <= lh->pagesize ? lh->pagesize + 1 : logsize;
175
176 filename = config_get_value(config, "logfile");
177 if (!filename) {
178 filename = default_filename;
179 }
180
181 lh->log_filename = strdup(filename);
182
183 rc = asprintf(&lh->rotate_filename, "%s.1", filename);
184 if (rc < 0) {
185 warn("Failed to construct rotate filename");
186 return -1;
187 }
188
189 rc = log_create(lh);
190 if (rc < 0) {
191 return -1;
192 }
193 lh->rbc = console_ringbuffer_consumer_register(console,
194 log_ringbuffer_poll, lh);
195
196 return 0;
197 }
198
log_fini(struct handler * handler)199 static void log_fini(struct handler *handler)
200 {
201 struct log_handler *lh = to_log_handler(handler);
202 ringbuffer_consumer_unregister(lh->rbc);
203 close(lh->fd);
204 free(lh->log_filename);
205 free(lh->rotate_filename);
206 }
207
208 static struct log_handler log_handler = {
209 .handler = {
210 .name = "log",
211 .init = log_init,
212 .fini = log_fini,
213 },
214 };
215
216 console_handler_register(&log_handler.handler);
217