| /* 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); | 
 | } |