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 <ctype.h> 18 #include <err.h> 19 #include <errno.h> 20 #include <fcntl.h> 21 #include <limits.h> 22 #include <stdint.h> 23 #include <stdio.h> 24 #include <stdlib.h> 25 #include <string.h> 26 #include <strings.h> 27 #include <termios.h> /* for speed_t */ 28 #include <unistd.h> 29 30 #include <sys/mman.h> 31 #include <sys/stat.h> 32 33 #include <iniparser/iniparser.h> 34 35 #include "config-internal.h" 36 #include "config.h" 37 #include "util.h" 38 39 static const char *config_default_filename = SYSCONFDIR "/obmc-console.conf"; 40 41 const char *config_get_value(struct config *config, const char *name) 42 { 43 char buf[CONFIG_MAX_KEY_LENGTH]; 44 int rc; 45 46 if (!config->dict) { 47 return NULL; 48 } 49 50 rc = snprintf(buf, CONFIG_MAX_KEY_LENGTH, ":%s", name); 51 if (rc < 0) { 52 return NULL; 53 } 54 55 if ((size_t)rc >= sizeof(buf)) { 56 return NULL; 57 } 58 59 const char *value = iniparser_getstring(config->dict, buf, NULL); 60 if (value && strlen(value) == 0) { 61 return NULL; 62 } 63 64 return value; 65 } 66 67 struct config *config_init(const char *filename) 68 { 69 struct config *config; 70 dictionary *dict; 71 72 if (!filename) { 73 filename = config_default_filename; 74 } 75 76 if (access(filename, R_OK) == 0) { 77 dict = iniparser_load(filename); 78 if (!dict) { 79 /* Assume this is a parse failure */ 80 return NULL; 81 } 82 } else { 83 /* If a config file was explicitly specified, then lack of access is always an error */ 84 if (filename != config_default_filename) { 85 warn("Failed to open configuration file at '%s'", 86 filename); 87 return NULL; 88 } 89 90 /* For the default config path, any result other than not-present is an error */ 91 if (errno != ENOENT && errno != ENOTDIR) { 92 warn("Failed to open configuration file at '%s'", 93 filename); 94 return NULL; 95 } 96 97 /* Config not present at default path, pretend its empty */ 98 dict = NULL; 99 } 100 101 config = malloc(sizeof(*config)); 102 if (!config) { 103 iniparser_freedict(dict); 104 return NULL; 105 } 106 107 config->dict = dict; 108 109 return config; 110 } 111 112 const char *config_get_section_value(struct config *config, const char *secname, 113 const char *name) 114 { 115 char buf[CONFIG_MAX_KEY_LENGTH]; 116 int rc; 117 118 rc = snprintf(buf, sizeof(buf), "%s:%s", secname, name); 119 if (rc < 0) { 120 return NULL; 121 } 122 123 if ((size_t)rc >= sizeof(buf)) { 124 // error / key too long for the buffer 125 warnx("config: section:key too long for buffer: '%s':'%s'", 126 secname, name); 127 return NULL; 128 } 129 130 return iniparser_getstring(config->dict, buf, NULL); 131 } 132 133 void config_fini(struct config *config) 134 { 135 if (!config) { 136 return; 137 } 138 139 if (config->dict) { 140 iniparser_freedict(config->dict); 141 } 142 143 free(config); 144 } 145 146 struct terminal_speed_name { 147 speed_t speed; 148 uint32_t baud; 149 const char *name; 150 }; 151 152 #define TERM_SPEED(x) \ 153 { \ 154 B##x, x, #x \ 155 } 156 157 // clang-format off 158 static const struct terminal_speed_name terminal_speeds[] = { 159 TERM_SPEED(50), 160 TERM_SPEED(75), 161 TERM_SPEED(110), 162 TERM_SPEED(134), 163 TERM_SPEED(150), 164 TERM_SPEED(200), 165 TERM_SPEED(300), 166 TERM_SPEED(600), 167 TERM_SPEED(1200), 168 TERM_SPEED(1800), 169 TERM_SPEED(2400), 170 TERM_SPEED(4800), 171 TERM_SPEED(9600), 172 TERM_SPEED(19200), 173 TERM_SPEED(38400), 174 TERM_SPEED(57600), 175 TERM_SPEED(115200), 176 TERM_SPEED(230400), 177 TERM_SPEED(460800), 178 TERM_SPEED(500000), 179 TERM_SPEED(576000), 180 TERM_SPEED(921600), 181 TERM_SPEED(1000000), 182 TERM_SPEED(1152000), 183 TERM_SPEED(1500000), 184 TERM_SPEED(2000000), 185 TERM_SPEED(2500000), 186 TERM_SPEED(3000000), 187 TERM_SPEED(3500000), 188 TERM_SPEED(4000000), 189 }; 190 // clang-format on 191 192 int config_parse_baud(speed_t *speed, const char *baud_string) 193 { 194 size_t i; 195 196 for (i = 0; i < ARRAY_SIZE(terminal_speeds); i++) { 197 if (strcmp(baud_string, terminal_speeds[i].name) == 0) { 198 *speed = terminal_speeds[i].speed; 199 return 0; 200 } 201 } 202 return -1; 203 } 204 205 uint32_t parse_baud_to_int(speed_t speed) 206 { 207 size_t i; 208 209 for (i = 0; i < ARRAY_SIZE(terminal_speeds); i++) { 210 if (terminal_speeds[i].speed == speed) { 211 return terminal_speeds[i].baud; 212 } 213 } 214 return 0; 215 } 216 217 speed_t parse_int_to_baud(uint32_t baud) 218 { 219 size_t i; 220 221 for (i = 0; i < ARRAY_SIZE(terminal_speeds); i++) { 222 if (terminal_speeds[i].baud == baud) { 223 return terminal_speeds[i].speed; 224 } 225 } 226 return 0; 227 } 228 229 int config_parse_bytesize(const char *size_str, size_t *size) 230 { 231 struct size_suffix_shift { 232 /* Left shiftwidth corresponding to the suffix. */ 233 size_t shiftwidth; 234 int unit; 235 }; 236 237 const struct size_suffix_shift suffixes[] = { 238 { 10, 'k' }, 239 { 20, 'M' }, 240 { 30, 'G' }, 241 }; 242 const size_t num_suffixes = 243 sizeof(suffixes) / sizeof(struct size_suffix_shift); 244 size_t logsize; 245 char *suffix; 246 size_t i; 247 248 if (!size_str) { 249 return -1; 250 } 251 252 logsize = strtoul(size_str, &suffix, 0); 253 if (logsize == 0 || logsize >= UINT32_MAX || suffix == size_str) { 254 return -1; 255 } 256 257 /* Ignore spaces between number and suffix */ 258 while (*suffix && isspace(*suffix)) { 259 suffix++; 260 } 261 262 for (i = 0; i < num_suffixes; i++) { 263 if (*suffix == suffixes[i].unit) { 264 /* 265 * If logsize overflows, probably something was wrong. 266 * Return instead of clamping to an arbitrary value. 267 */ 268 if (logsize > (UINT32_MAX >> suffixes[i].shiftwidth)) { 269 return -1; 270 } 271 272 logsize <<= suffixes[i].shiftwidth; 273 suffix++; 274 break; 275 } 276 } 277 278 /* Allow suffix like 'kB' */ 279 while (*suffix && (tolower(*suffix) == 'b' || isspace(*suffix))) { 280 suffix++; 281 } 282 283 if (*suffix) { 284 warn("Invalid suffix!"); 285 return -1; 286 } 287 288 *size = logsize; 289 return 0; 290 } 291 292 /* Default console id if not specified on command line or in config */ 293 #define DEFAULT_CONSOLE_ID "default" 294 295 /* Get the console id */ 296 const char *config_resolve_console_id(struct config *config, const char *id_arg) 297 { 298 const char *configured; 299 300 if (id_arg) { 301 return id_arg; 302 } 303 304 if ((configured = config_get_value(config, "console-id"))) { 305 return configured; 306 } 307 308 return DEFAULT_CONSOLE_ID; 309 } 310 311 int config_count_sections(struct config *config) 312 { 313 return iniparser_getnsec(config->dict); 314 } 315 316 const char *config_get_section_name(struct config *config, int i) 317 { 318 return iniparser_getsecname(config->dict, i); 319 } 320