You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
300 lines
7.3 KiB
300 lines
7.3 KiB
/* 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 <ctype.h> |
|
#include <errno.h> |
|
#include <limits.h> |
|
#include <stdint.h> |
|
#include <stdio.h> |
|
#include <string.h> |
|
|
|
#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 <syslog.h> |
|
* 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); |
|
}
|
|
|