/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "util.h" #include #include #include #include #include #include #include "libconstants.h" #include "libsyscalls.h" /* * These are syscalls used by the syslog() C library call. You can find them * by running a simple test program. See below for x86_64 behavior: * $ cat test.c * #include * main() { syslog(0, "foo"); } * $ gcc test.c -static * $ strace ./a.out * ... * socket(PF_FILE, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 3 <- look for socket connection * connect(...) <- important * sendto(...) <- important * exit_group(0) <- finish! */ #if defined(__x86_64__) #if defined(__ANDROID__) const char *log_syscalls[] = {"socket", "connect", "fcntl", "writev"}; #else const char *log_syscalls[] = {"connect", "sendto"}; #endif #elif defined(__i386__) #if defined(__ANDROID__) const char *log_syscalls[] = {"socketcall", "writev", "fcntl64", "clock_gettime"}; #else const char *log_syscalls[] = {"socketcall", "time"}; #endif #elif defined(__arm__) #if defined(__ANDROID__) const char *log_syscalls[] = {"clock_gettime", "connect", "fcntl64", "socket", "writev"}; #else const char *log_syscalls[] = {"connect", "gettimeofday", "send"}; #endif #elif defined(__aarch64__) #if defined(__ANDROID__) const char *log_syscalls[] = {"connect", "fcntl", "sendto", "socket", "writev"}; #else const char *log_syscalls[] = {"connect", "send"}; #endif #elif defined(__powerpc__) || defined(__ia64__) || defined(__hppa__) || \ defined(__sparc__) || defined(__mips__) const char *log_syscalls[] = {"connect", "send"}; #else #error "Unsupported platform" #endif const size_t log_syscalls_len = ARRAY_SIZE(log_syscalls); int lookup_syscall(const char *name) { const struct syscall_entry *entry = syscall_table; for (; entry->name && entry->nr >= 0; ++entry) if (!strcmp(entry->name, name)) return entry->nr; return -1; } const char *lookup_syscall_name(int nr) { const struct syscall_entry *entry = syscall_table; for (; entry->name && entry->nr >= 0; ++entry) if (entry->nr == nr) return entry->name; return NULL; } long int parse_single_constant(char *constant_str, char **endptr) { const struct constant_entry *entry = constant_table; long int res = 0; for (; entry->name; ++entry) { if (!strcmp(entry->name, constant_str)) { if (endptr) *endptr = constant_str + strlen(constant_str); return entry->value; } } errno = 0; res = strtol(constant_str, endptr, 0); if (errno == ERANGE) { if (res == LONG_MAX) { /* See if the constant fits in an unsigned long int. */ errno = 0; res = strtoul(constant_str, endptr, 0); if (errno == ERANGE) { /* * On unsigned overflow, use the same convention * as when strtol(3) finds no digits: set * |*endptr| to |constant_str| and return 0. */ warn("unsigned overflow: '%s'", constant_str); *endptr = constant_str; res = 0; } } else if (res == LONG_MIN) { /* * Same for signed underflow: set |*endptr| to * |constant_str| and return 0. */ warn("signed underflow: '%s'", constant_str); *endptr = constant_str; res = 0; } } return res; } long int parse_constant(char *constant_str, char **endptr) { long int value = 0; char *group, *lastpos = constant_str; char *original_constant_str = constant_str; /* * Try to parse constants separated by pipes. Note that since * |constant_str| is an atom, there can be no spaces between the * constant and the pipe. Constants can be either a named constant * defined in libconstants.gen.c or a number parsed with strtol(3). * * If there is an error parsing any of the constants, the whole process * fails. */ while ((group = tokenize(&constant_str, "|")) != NULL) { char *end = group; value |= parse_single_constant(group, &end); if (end == group) { lastpos = original_constant_str; value = 0; break; } lastpos = end; } if (endptr) *endptr = lastpos; return value; } /* * parse_size, specified as a string with a decimal number in bytes, * possibly with one 1-character suffix like "10K" or "6G". * Assumes both pointers are non-NULL. * * Returns 0 on success, negative errno on failure. * Only writes to result on success. */ int parse_size(size_t *result, const char *sizespec) { const char prefixes[] = "KMGTPE"; size_t i, multiplier = 1, nsize, size = 0; unsigned long long parsed; const size_t len = strlen(sizespec); char *end; if (len == 0 || sizespec[0] == '-') return -EINVAL; for (i = 0; i < sizeof(prefixes); ++i) { if (sizespec[len - 1] == prefixes[i]) { #if __WORDSIZE == 32 if (i >= 3) return -ERANGE; #endif multiplier = 1024; while (i-- > 0) multiplier *= 1024; break; } } /* We only need size_t but strtoul(3) is too small on IL32P64. */ parsed = strtoull(sizespec, &end, 10); if (parsed == ULLONG_MAX) return -errno; if (parsed >= SIZE_MAX) return -ERANGE; if ((multiplier != 1 && end != sizespec + len - 1) || (multiplier == 1 && end != sizespec + len)) return -EINVAL; size = (size_t)parsed; nsize = size * multiplier; if (nsize / multiplier != size) return -ERANGE; *result = nsize; return 0; } char *strip(char *s) { char *end; while (*s && isblank(*s)) s++; end = s + strlen(s) - 1; while (end >= s && *end && (isblank(*end) || *end == '\n')) end--; *(end + 1) = '\0'; return s; } char *tokenize(char **stringp, const char *delim) { char *ret = NULL; /* If the string is NULL or empty, there are no tokens to be found. */ if (stringp == NULL || *stringp == NULL || **stringp == '\0') return NULL; /* * If the delimiter is NULL or empty, * the full string makes up the only token. */ if (delim == NULL || *delim == '\0') { ret = *stringp; *stringp = NULL; return ret; } char *found; while (**stringp != '\0') { found = strstr(*stringp, delim); if (!found) { /* * The delimiter was not found, so the full string * makes up the only token, and we're done. */ ret = *stringp; *stringp = NULL; break; } if (found != *stringp) { /* There's a non-empty token before the delimiter. */ *found = '\0'; ret = *stringp; *stringp = found + strlen(delim); break; } /* * The delimiter was found at the start of the string, * skip it and keep looking for a non-empty token. */ *stringp += strlen(delim); } return ret; } char *path_join(const char *external_path, const char *internal_path) { char *path; size_t pathlen; /* One extra char for '/' and one for '\0', hence + 2. */ pathlen = strlen(external_path) + strlen(internal_path) + 2; path = malloc(pathlen); snprintf(path, pathlen, "%s/%s", external_path, internal_path); return path; } void *consumebytes(size_t length, char **buf, size_t *buflength) { char *p = *buf; if (length > *buflength) return NULL; *buf += length; *buflength -= length; return p; } char *consumestr(char **buf, size_t *buflength) { size_t len = strnlen(*buf, *buflength); if (len == *buflength) /* There's no null-terminator. */ return NULL; return consumebytes(len + 1, buf, buflength); }