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 int pagesize; 40 }; 41 42 43 static const char *default_filename = LOCALSTATEDIR "/log/obmc-console.log"; 44 static const size_t default_logsize = 16 * 1024; 45 46 static struct log_handler *to_log_handler(struct handler *handler) 47 { 48 return container_of(handler, struct log_handler, handler); 49 } 50 51 static 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 81 static int log_data(struct log_handler *lh, uint8_t *buf, size_t len) 82 { 83 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 105 static 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 130 static int log_init(struct handler *handler, struct console *console, 131 struct config *config) 132 { 133 struct log_handler *lh = to_log_handler(handler); 134 const char *filename, *logsize_str; 135 size_t logsize = default_logsize; 136 int rc; 137 138 lh->console = console; 139 lh->pagesize = 4096; 140 lh->size = 0; 141 142 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 151 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 173 static void log_fini(struct handler *handler) 174 { 175 struct log_handler *lh = to_log_handler(handler); 176 ringbuffer_consumer_unregister(lh->rbc); 177 close(lh->fd); 178 } 179 180 static struct log_handler log_handler = { 181 .handler = { 182 .name = "log", 183 .init = log_init, 184 .fini = log_fini, 185 }, 186 }; 187 188 console_handler_register(&log_handler.handler); 189 190