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 48 static struct log_handler *to_log_handler(struct handler *handler) 49 { 50 return container_of(handler, struct log_handler, handler); 51 } 52 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 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 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 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_CUR); 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 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_value(config, "logfile"); 184 if (!filename) { 185 filename = default_filename; 186 } 187 188 lh->log_filename = strdup(filename); 189 190 rc = asprintf(&lh->rotate_filename, "%s.1", filename); 191 if (rc < 0) { 192 warn("Failed to construct rotate filename"); 193 goto err_free; 194 } 195 196 rc = log_create(lh); 197 if (rc < 0) { 198 goto err_free; 199 } 200 lh->rbc = console_ringbuffer_consumer_register(console, 201 log_ringbuffer_poll, lh); 202 203 return &lh->handler; 204 205 err_free: 206 free(lh->rotate_filename); 207 free(lh->log_filename); 208 free(lh); 209 return NULL; 210 } 211 212 static void log_fini(struct handler *handler) 213 { 214 struct log_handler *lh = to_log_handler(handler); 215 ringbuffer_consumer_unregister(lh->rbc); 216 close(lh->fd); 217 free(lh->log_filename); 218 free(lh->rotate_filename); 219 free(lh); 220 } 221 222 static const struct handler_type log_handler = { 223 .name = "log", 224 .init = log_init, 225 .fini = log_fini, 226 }; 227 228 console_handler_register(&log_handler); 229