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 <ctype.h> 7 #include <errno.h> 8 #include <fcntl.h> 9 #include <limits.h> 10 #include <stdint.h> 11 #include <stdio.h> 12 #include <string.h> 13 #include <sys/stat.h> 14 #include <sys/types.h> 15 16 #include "util.h" 17 18 #include "libconstants.h" 19 #include "libsyscalls.h" 20 21 /* 22 * These are syscalls used by the syslog() C library call. You can find them 23 * by running a simple test program. See below for x86_64 behavior: 24 * $ cat test.c 25 * #include <syslog.h> 26 * main() { syslog(0, "foo"); } 27 * $ gcc test.c -static 28 * $ strace ./a.out 29 * ... 30 * socket(PF_FILE, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 3 <- look for socket connection 31 * connect(...) <- important 32 * sendto(...) <- important 33 * exit_group(0) <- finish! 34 */ 35 #if defined(__x86_64__) 36 #if defined(__ANDROID__) 37 const char *log_syscalls[] = {"socket", "connect", "fcntl", "writev"}; 38 #else 39 const char *log_syscalls[] = {"connect", "sendto"}; 40 #endif 41 #elif defined(__i386__) 42 #if defined(__ANDROID__) 43 const char *log_syscalls[] = {"socketcall", "writev", "fcntl64", 44 "clock_gettime"}; 45 #else 46 const char *log_syscalls[] = {"socketcall", "time"}; 47 #endif 48 #elif defined(__arm__) 49 #if defined(__ANDROID__) 50 const char *log_syscalls[] = {"clock_gettime", "connect", "fcntl64", "socket", 51 "writev"}; 52 #else 53 const char *log_syscalls[] = {"connect", "gettimeofday", "send"}; 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[] = {"connect", "send"}; 60 #endif 61 #elif defined(__powerpc__) || defined(__ia64__) || defined(__hppa__) || \ 62 defined(__sparc__) || defined(__mips__) 63 const char *log_syscalls[] = {"connect", "send"}; 64 #else 65 #error "Unsupported platform" 66 #endif 67 68 const size_t log_syscalls_len = ARRAY_SIZE(log_syscalls); 69 70 int lookup_syscall(const char *name) 71 { 72 const struct syscall_entry *entry = syscall_table; 73 for (; entry->name && entry->nr >= 0; ++entry) 74 if (!strcmp(entry->name, name)) 75 return entry->nr; 76 return -1; 77 } 78 79 const char *lookup_syscall_name(int nr) 80 { 81 const struct syscall_entry *entry = syscall_table; 82 for (; entry->name && entry->nr >= 0; ++entry) 83 if (entry->nr == nr) 84 return entry->name; 85 return NULL; 86 } 87 88 long int parse_single_constant(char *constant_str, char **endptr) 89 { 90 const struct constant_entry *entry = constant_table; 91 long int res = 0; 92 for (; entry->name; ++entry) { 93 if (!strcmp(entry->name, constant_str)) { 94 if (endptr) 95 *endptr = constant_str + strlen(constant_str); 96 97 return entry->value; 98 } 99 } 100 101 errno = 0; 102 res = strtol(constant_str, endptr, 0); 103 if (errno == ERANGE) { 104 if (res == LONG_MAX) { 105 /* See if the constant fits in an unsigned long int. */ 106 errno = 0; 107 res = strtoul(constant_str, endptr, 0); 108 if (errno == ERANGE) { 109 /* 110 * On unsigned overflow, use the same convention 111 * as when strtol(3) finds no digits: set 112 * |*endptr| to |constant_str| and return 0. 113 */ 114 warn("unsigned overflow: '%s'", constant_str); 115 *endptr = constant_str; 116 res = 0; 117 } 118 } else if (res == LONG_MIN) { 119 /* 120 * Same for signed underflow: set |*endptr| to 121 * |constant_str| and return 0. 122 */ 123 warn("signed underflow: '%s'", constant_str); 124 *endptr = constant_str; 125 res = 0; 126 } 127 } 128 return res; 129 } 130 131 long int parse_constant(char *constant_str, char **endptr) 132 { 133 long int value = 0; 134 char *group, *lastpos = constant_str; 135 char *original_constant_str = constant_str; 136 137 /* 138 * Try to parse constants separated by pipes. Note that since 139 * |constant_str| is an atom, there can be no spaces between the 140 * constant and the pipe. Constants can be either a named constant 141 * defined in libconstants.gen.c or a number parsed with strtol(3). 142 * 143 * If there is an error parsing any of the constants, the whole process 144 * fails. 145 */ 146 while ((group = tokenize(&constant_str, "|")) != NULL) { 147 char *end = group; 148 value |= parse_single_constant(group, &end); 149 if (end == group) { 150 lastpos = original_constant_str; 151 value = 0; 152 break; 153 } 154 lastpos = end; 155 } 156 if (endptr) 157 *endptr = lastpos; 158 return value; 159 } 160 161 /* 162 * parse_size, specified as a string with a decimal number in bytes, 163 * possibly with one 1-character suffix like "10K" or "6G". 164 * Assumes both pointers are non-NULL. 165 * 166 * Returns 0 on success, negative errno on failure. 167 * Only writes to result on success. 168 */ 169 int parse_size(size_t *result, const char *sizespec) 170 { 171 const char prefixes[] = "KMGTPE"; 172 size_t i, multiplier = 1, nsize, size = 0; 173 unsigned long long parsed; 174 const size_t len = strlen(sizespec); 175 char *end; 176 177 if (len == 0 || sizespec[0] == '-') 178 return -EINVAL; 179 180 for (i = 0; i < sizeof(prefixes); ++i) { 181 if (sizespec[len - 1] == prefixes[i]) { 182 #if __WORDSIZE == 32 183 if (i >= 3) 184 return -ERANGE; 185 #endif 186 multiplier = 1024; 187 while (i-- > 0) 188 multiplier *= 1024; 189 break; 190 } 191 } 192 193 /* We only need size_t but strtoul(3) is too small on IL32P64. */ 194 parsed = strtoull(sizespec, &end, 10); 195 if (parsed == ULLONG_MAX) 196 return -errno; 197 if (parsed >= SIZE_MAX) 198 return -ERANGE; 199 if ((multiplier != 1 && end != sizespec + len - 1) || 200 (multiplier == 1 && end != sizespec + len)) 201 return -EINVAL; 202 size = (size_t)parsed; 203 204 nsize = size * multiplier; 205 if (nsize / multiplier != size) 206 return -ERANGE; 207 *result = nsize; 208 return 0; 209 } 210 211 char *strip(char *s) 212 { 213 char *end; 214 while (*s && isblank(*s)) 215 s++; 216 end = s + strlen(s) - 1; 217 while (end >= s && *end && (isblank(*end) || *end == '\n')) 218 end--; 219 *(end + 1) = '\0'; 220 return s; 221 } 222 223 char *tokenize(char **stringp, const char *delim) 224 { 225 char *ret = NULL; 226 227 /* If the string is NULL or empty, there are no tokens to be found. */ 228 if (stringp == NULL || *stringp == NULL || **stringp == '\0') 229 return NULL; 230 231 /* 232 * If the delimiter is NULL or empty, 233 * the full string makes up the only token. 234 */ 235 if (delim == NULL || *delim == '\0') { 236 ret = *stringp; 237 *stringp = NULL; 238 return ret; 239 } 240 241 char *found; 242 while (**stringp != '\0') { 243 found = strstr(*stringp, delim); 244 245 if (!found) { 246 /* 247 * The delimiter was not found, so the full string 248 * makes up the only token, and we're done. 249 */ 250 ret = *stringp; 251 *stringp = NULL; 252 break; 253 } 254 255 if (found != *stringp) { 256 /* There's a non-empty token before the delimiter. */ 257 *found = '\0'; 258 ret = *stringp; 259 *stringp = found + strlen(delim); 260 break; 261 } 262 263 /* 264 * The delimiter was found at the start of the string, 265 * skip it and keep looking for a non-empty token. 266 */ 267 *stringp += strlen(delim); 268 } 269 270 return ret; 271 } 272 273 char *path_join(const char *external_path, const char *internal_path) 274 { 275 char *path; 276 size_t pathlen; 277 278 /* One extra char for '/' and one for '\0', hence + 2. */ 279 pathlen = strlen(external_path) + strlen(internal_path) + 2; 280 path = malloc(pathlen); 281 snprintf(path, pathlen, "%s/%s", external_path, internal_path); 282 283 return path; 284 } 285 286 int write_proc_file(pid_t pid, const char *content, const char *basename) 287 { 288 int fd, ret; 289 size_t sz, len; 290 ssize_t written; 291 char filename[32]; 292 293 sz = sizeof(filename); 294 ret = snprintf(filename, sz, "/proc/%d/%s", pid, basename); 295 if (ret < 0 || (size_t)ret >= sz) { 296 warn("failed to generate %s filename", basename); 297 return -1; 298 } 299 300 fd = open(filename, O_WRONLY | O_CLOEXEC); 301 if (fd < 0) { 302 pwarn("failed to open '%s'", filename); 303 return -errno; 304 } 305 306 len = strlen(content); 307 written = write(fd, content, len); 308 if (written < 0) { 309 pwarn("failed to write '%s'", filename); 310 return -1; 311 } 312 313 if ((size_t)written < len) { 314 warn("failed to write %zu bytes to '%s'", len, filename); 315 return -1; 316 } 317 close(fd); 318 return 0; 319 } 320 321 int write_pid_to_path(pid_t pid, const char *path) 322 { 323 FILE *fp = fopen(path, "w"); 324 325 if (!fp) { 326 pwarn("failed to open '%s'", path); 327 return -errno; 328 } 329 if (fprintf(fp, "%d\n", (int)pid) < 0) { 330 /* fprintf(3) does not set errno on failure. */ 331 warn("fprintf(%s) failed", path); 332 return -1; 333 } 334 if (fclose(fp)) { 335 pwarn("fclose(%s) failed", path); 336 return -errno; 337 } 338 339 return 0; 340 } 341 342 void *consumebytes(size_t length, char **buf, size_t *buflength) 343 { 344 char *p = *buf; 345 if (length > *buflength) 346 return NULL; 347 *buf += length; 348 *buflength -= length; 349 return p; 350 } 351 352 char *consumestr(char **buf, size_t *buflength) 353 { 354 size_t len = strnlen(*buf, *buflength); 355 if (len == *buflength) 356 /* There's no null-terminator. */ 357 return NULL; 358 return consumebytes(len + 1, buf, buflength); 359 } 360