1 /* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. 2 * Use of this source code is governed by a BSD-style license that can be 3 * found in the LICENSE file. 4 */ 5 6 #include "util.h" 7 8 #include <ctype.h> 9 #include <errno.h> 10 #include <limits.h> 11 #include <stdarg.h> 12 #include <stdbool.h> 13 #include <stdint.h> 14 #include <stdio.h> 15 #include <string.h> 16 17 #include "libconstants.h" 18 #include "libsyscalls.h" 19 20 /* 21 * These are syscalls used by the syslog() C library call. You can find them 22 * by running a simple test program. See below for x86_64 behavior: 23 * $ cat test.c 24 * #include <syslog.h> 25 * main() { syslog(0, "foo"); } 26 * $ gcc test.c -static 27 * $ strace ./a.out 28 * ... 29 * socket(PF_FILE, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 3 <- look for socket connection 30 * connect(...) <- important 31 * sendto(...) <- important 32 * exit_group(0) <- finish! 33 */ 34 #if defined(__x86_64__) 35 #if defined(__ANDROID__) 36 const char *log_syscalls[] = {"socket", "connect", "fcntl", "writev"}; 37 #else 38 const char *log_syscalls[] = {"socket", "connect", "sendto", "writev"}; 39 #endif 40 #elif defined(__i386__) 41 #if defined(__ANDROID__) 42 const char *log_syscalls[] = {"socketcall", "writev", "fcntl64", 43 "clock_gettime"}; 44 #else 45 const char *log_syscalls[] = {"socketcall", "time", "writev"}; 46 #endif 47 #elif defined(__arm__) 48 #if defined(__ANDROID__) 49 const char *log_syscalls[] = {"clock_gettime", "connect", "fcntl64", "socket", 50 "writev"}; 51 #else 52 const char *log_syscalls[] = {"socket", "connect", "gettimeofday", "send", 53 "writev"}; 54 #endif 55 #elif defined(__aarch64__) 56 #if defined(__ANDROID__) 57 const char *log_syscalls[] = {"connect", "fcntl", "sendto", "socket", "writev"}; 58 #else 59 const char *log_syscalls[] = {"socket", "connect", "send", "writev"}; 60 #endif 61 #elif defined(__powerpc__) || defined(__ia64__) || defined(__hppa__) || \ 62 defined(__sparc__) || defined(__mips__) 63 const char *log_syscalls[] = {"socket", "connect", "send"}; 64 #else 65 #error "Unsupported platform" 66 #endif 67 68 const size_t log_syscalls_len = ARRAY_SIZE(log_syscalls); 69 70 /* clang-format off */ 71 static struct logging_config_t { 72 /* The logging system to use. The default is syslog. */ 73 enum logging_system_t logger; 74 75 /* File descriptor to log to. Only used when logger is LOG_TO_FD. */ 76 int fd; 77 78 /* Minimum priority to log. Only used when logger is LOG_TO_FD. */ 79 int min_priority; 80 } logging_config = { 81 .logger = LOG_TO_SYSLOG, 82 }; 83 /* clang-format on */ 84 85 #if defined(USE_EXIT_ON_DIE) 86 #define do_abort() exit(1) 87 #else 88 #define do_abort() abort() 89 #endif 90 91 #if defined(__clang__) 92 #define attribute_no_optimize __attribute__((optnone)) 93 #else 94 #define attribute_no_optimize __attribute__((__optimize__(0))) 95 #endif 96 97 /* Forces the compiler to perform no optimizations on |var|. */ 98 static void attribute_no_optimize alias(const void *var) 99 { 100 (void)var; 101 } 102 103 void do_fatal_log(int priority, const char *format, ...) 104 { 105 va_list args, stack_args; 106 va_start(args, format); 107 va_copy(stack_args, args); 108 if (logging_config.logger == LOG_TO_SYSLOG) { 109 vsyslog(priority, format, args); 110 } else { 111 vdprintf(logging_config.fd, format, args); 112 dprintf(logging_config.fd, "\n"); 113 } 114 va_end(args); 115 116 /* 117 * Write another copy of the first few characters of the message into a 118 * stack-based buffer so that it can appear in minidumps. Choosing a 119 * small-ish buffer size since breakpad will only pick up the first few 120 * kilobytes of each stack, so that will prevent this buffer from 121 * kicking out other stack frames. 122 */ 123 char log_line[512]; 124 vsnprintf(log_line, sizeof(log_line), format, stack_args); 125 va_end(stack_args); 126 alias(log_line); 127 do_abort(); 128 } 129 130 void do_log(int priority, const char *format, ...) 131 { 132 if (logging_config.logger == LOG_TO_SYSLOG) { 133 va_list args; 134 va_start(args, format); 135 vsyslog(priority, format, args); 136 va_end(args); 137 return; 138 } 139 140 if (logging_config.min_priority < priority) 141 return; 142 143 va_list args; 144 va_start(args, format); 145 vdprintf(logging_config.fd, format, args); 146 va_end(args); 147 dprintf(logging_config.fd, "\n"); 148 } 149 150 int lookup_syscall(const char *name) 151 { 152 const struct syscall_entry *entry = syscall_table; 153 for (; entry->name && entry->nr >= 0; ++entry) 154 if (!strcmp(entry->name, name)) 155 return entry->nr; 156 return -1; 157 } 158 159 const char *lookup_syscall_name(int nr) 160 { 161 const struct syscall_entry *entry = syscall_table; 162 for (; entry->name && entry->nr >= 0; ++entry) 163 if (entry->nr == nr) 164 return entry->name; 165 return NULL; 166 } 167 168 long int parse_single_constant(char *constant_str, char **endptr) 169 { 170 const struct constant_entry *entry = constant_table; 171 long int res = 0; 172 for (; entry->name; ++entry) { 173 if (!strcmp(entry->name, constant_str)) { 174 *endptr = constant_str + strlen(constant_str); 175 return entry->value; 176 } 177 } 178 179 errno = 0; 180 res = strtol(constant_str, endptr, 0); 181 if (errno == ERANGE) { 182 if (res == LONG_MAX) { 183 /* See if the constant fits in an unsigned long int. */ 184 errno = 0; 185 res = strtoul(constant_str, endptr, 0); 186 if (errno == ERANGE) { 187 /* 188 * On unsigned overflow, use the same convention 189 * as when strtol(3) finds no digits: set 190 * |*endptr| to |constant_str| and return 0. 191 */ 192 warn("unsigned overflow: '%s'", constant_str); 193 *endptr = constant_str; 194 return 0; 195 } 196 } else if (res == LONG_MIN) { 197 /* 198 * Same for signed underflow: set |*endptr| to 199 * |constant_str| and return 0. 200 */ 201 warn("signed underflow: '%s'", constant_str); 202 *endptr = constant_str; 203 return 0; 204 } 205 } 206 if (**endptr != '\0') { 207 warn("trailing garbage after constant: '%s'", constant_str); 208 *endptr = constant_str; 209 return 0; 210 } 211 return res; 212 } 213 214 static char *tokenize_parenthesized_expression(char **stringp) 215 { 216 char *ret = NULL, *found = NULL; 217 size_t paren_count = 1; 218 219 /* If the string is NULL, there are no parens to be found. */ 220 if (stringp == NULL || *stringp == NULL) 221 return NULL; 222 223 /* If the string is not on an open paren, the results are undefined. */ 224 if (**stringp != '(') 225 return NULL; 226 227 for (found = *stringp + 1; *found; ++found) { 228 switch (*found) { 229 case '(': 230 ++paren_count; 231 break; 232 case ')': 233 --paren_count; 234 if (!paren_count) { 235 *found = '\0'; 236 ret = *stringp + 1; 237 *stringp = found + 1; 238 return ret; 239 } 240 break; 241 } 242 } 243 244 /* We got to the end without finding the closing paren. */ 245 warn("unclosed parenthesis: '%s'", *stringp); 246 return NULL; 247 } 248 249 long int parse_constant(char *constant_str, char **endptr) 250 { 251 long int value = 0, current_value; 252 char *group, *lastpos = constant_str; 253 254 /* 255 * If |endptr| is provided, parsing errors are signaled as |endptr| 256 * pointing to |constant_str|. 257 */ 258 if (endptr) 259 *endptr = constant_str; 260 261 /* 262 * Try to parse constant expressions. Valid constant expressions are: 263 * 264 * - A number that can be parsed with strtol(3). 265 * - A named constant expression. 266 * - A parenthesized, valid constant expression. 267 * - A valid constant expression prefixed with the unary bitwise 268 * complement operator ~. 269 * - A series of valid constant expressions separated by pipes. Note 270 * that since |constant_str| is an atom, there can be no spaces 271 * between the constant and the pipe. 272 * 273 * If there is an error parsing any of the constants, the whole process 274 * fails. 275 */ 276 while (constant_str && *constant_str) { 277 bool negate = false; 278 if (*constant_str == '~') { 279 negate = true; 280 ++constant_str; 281 } 282 if (*constant_str == '(') { 283 group = 284 tokenize_parenthesized_expression(&constant_str); 285 if (group == NULL) 286 return 0; 287 char *end = group; 288 /* Recursively parse the parenthesized subexpression. */ 289 current_value = parse_constant(group, &end); 290 if (end == group) 291 return 0; 292 if (constant_str && *constant_str) { 293 /* 294 * If this is not the end of the atom, there 295 * should be another | followed by more stuff. 296 */ 297 if (*constant_str != '|') { 298 warn("unterminated constant " 299 "expression: '%s'", 300 constant_str); 301 return 0; 302 } 303 ++constant_str; 304 if (*constant_str == '\0') { 305 warn("unterminated constant " 306 "expression: '%s'", 307 constant_str); 308 return 0; 309 } 310 } 311 lastpos = end; 312 } else { 313 group = tokenize(&constant_str, "|"); 314 char *end = group; 315 current_value = parse_single_constant(group, &end); 316 if (end == group) 317 return 0; 318 lastpos = end; 319 } 320 if (negate) 321 current_value = ~current_value; 322 value |= current_value; 323 } 324 if (endptr) 325 *endptr = lastpos; 326 return value; 327 } 328 329 /* 330 * parse_size, specified as a string with a decimal number in bytes, 331 * possibly with one 1-character suffix like "10K" or "6G". 332 * Assumes both pointers are non-NULL. 333 * 334 * Returns 0 on success, negative errno on failure. 335 * Only writes to result on success. 336 */ 337 int parse_size(size_t *result, const char *sizespec) 338 { 339 const char prefixes[] = "KMGTPE"; 340 size_t i, multiplier = 1, nsize, size = 0; 341 unsigned long long parsed; 342 const size_t len = strlen(sizespec); 343 char *end; 344 345 if (len == 0 || sizespec[0] == '-') 346 return -EINVAL; 347 348 for (i = 0; i < sizeof(prefixes); ++i) { 349 if (sizespec[len - 1] == prefixes[i]) { 350 #if __WORDSIZE == 32 351 if (i >= 3) 352 return -ERANGE; 353 #endif 354 multiplier = 1024; 355 while (i-- > 0) 356 multiplier *= 1024; 357 break; 358 } 359 } 360 361 /* We only need size_t but strtoul(3) is too small on IL32P64. */ 362 parsed = strtoull(sizespec, &end, 10); 363 if (parsed == ULLONG_MAX) 364 return -errno; 365 if (parsed >= SIZE_MAX) 366 return -ERANGE; 367 if ((multiplier != 1 && end != sizespec + len - 1) || 368 (multiplier == 1 && end != sizespec + len)) 369 return -EINVAL; 370 size = (size_t)parsed; 371 372 nsize = size * multiplier; 373 if (nsize / multiplier != size) 374 return -ERANGE; 375 *result = nsize; 376 return 0; 377 } 378 379 char *strip(char *s) 380 { 381 char *end; 382 while (*s && isblank(*s)) 383 s++; 384 end = s + strlen(s) - 1; 385 while (end >= s && *end && (isblank(*end) || *end == '\n')) 386 end--; 387 *(end + 1) = '\0'; 388 return s; 389 } 390 391 char *tokenize(char **stringp, const char *delim) 392 { 393 char *ret = NULL; 394 395 /* If the string is NULL, there are no tokens to be found. */ 396 if (stringp == NULL || *stringp == NULL) 397 return NULL; 398 399 /* 400 * If the delimiter is NULL or empty, 401 * the full string makes up the only token. 402 */ 403 if (delim == NULL || *delim == '\0') { 404 ret = *stringp; 405 *stringp = NULL; 406 return ret; 407 } 408 409 char *found = strstr(*stringp, delim); 410 if (!found) { 411 /* 412 * The delimiter was not found, so the full string 413 * makes up the only token, and we're done. 414 */ 415 ret = *stringp; 416 *stringp = NULL; 417 } else { 418 /* There's a token here, possibly empty. That's OK. */ 419 *found = '\0'; 420 ret = *stringp; 421 *stringp = found + strlen(delim); 422 } 423 424 return ret; 425 } 426 427 char *path_join(const char *external_path, const char *internal_path) 428 { 429 char *path; 430 size_t pathlen; 431 432 /* One extra char for '/' and one for '\0', hence + 2. */ 433 pathlen = strlen(external_path) + strlen(internal_path) + 2; 434 path = malloc(pathlen); 435 snprintf(path, pathlen, "%s/%s", external_path, internal_path); 436 437 return path; 438 } 439 440 void *consumebytes(size_t length, char **buf, size_t *buflength) 441 { 442 char *p = *buf; 443 if (length > *buflength) 444 return NULL; 445 *buf += length; 446 *buflength -= length; 447 return p; 448 } 449 450 char *consumestr(char **buf, size_t *buflength) 451 { 452 size_t len = strnlen(*buf, *buflength); 453 if (len == *buflength) 454 /* There's no null-terminator. */ 455 return NULL; 456 return consumebytes(len + 1, buf, buflength); 457 } 458 459 void init_logging(enum logging_system_t logger, int fd, int min_priority) 460 { 461 logging_config.logger = logger; 462 logging_config.fd = fd; 463 logging_config.min_priority = min_priority; 464 } 465