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 <stdio.h>
8#include <string.h>
9
10#include "util.h"
11
12#include "libconstants.h"
13#include "libsyscalls.h"
14
15/*
16 * These are syscalls used by the syslog() C library call.  You can find them
17 * by running a simple test program.  See below for x86_64 behavior:
18 * $ cat test.c
19 * #include <syslog.h>
20 * main() { syslog(0, "foo"); }
21 * $ gcc test.c -static
22 * $ strace ./a.out
23 * ...
24 * socket(PF_FILE, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 3 <- look for socket connection
25 * connect(...)                                    <- important
26 * sendto(...)                                     <- important
27 * exit_group(0)                                   <- finish!
28 */
29#if defined(__x86_64__)
30#if defined(__ANDROID__)
31const char *log_syscalls[] = {"socket", "connect", "fcntl", "writev"};
32#else
33const char *log_syscalls[] = {"connect", "sendto"};
34#endif
35#elif defined(__i386__)
36#if defined(__ANDROID__)
37const char *log_syscalls[] = {"socketcall", "writev", "fcntl64",
38			      "clock_gettime"};
39#else
40const char *log_syscalls[] = {"socketcall", "time"};
41#endif
42#elif defined(__arm__)
43#if defined(__ANDROID__)
44const char *log_syscalls[] = {"clock_gettime", "connect", "fcntl64", "socket",
45			      "writev"};
46#else
47const char *log_syscalls[] = {"connect", "gettimeofday", "send"};
48#endif
49#elif defined(__aarch64__)
50#if defined(__ANDROID__)
51const char *log_syscalls[] = {"connect", "fcntl", "sendto", "socket", "writev"};
52#else
53const char *log_syscalls[] = {"connect", "send"};
54#endif
55#elif defined(__powerpc__) || defined(__ia64__) || defined(__hppa__) ||        \
56      defined(__sparc__) || defined(__mips__)
57const char *log_syscalls[] = {"connect", "send"};
58#else
59#error "Unsupported platform"
60#endif
61
62const size_t log_syscalls_len = sizeof(log_syscalls)/sizeof(log_syscalls[0]);
63
64long int parse_single_constant(char *constant_str, char **endptr);
65
66int lookup_syscall(const char *name)
67{
68	const struct syscall_entry *entry = syscall_table;
69	for (; entry->name && entry->nr >= 0; ++entry)
70		if (!strcmp(entry->name, name))
71			return entry->nr;
72	return -1;
73}
74
75const char *lookup_syscall_name(int nr)
76{
77	const struct syscall_entry *entry = syscall_table;
78	for (; entry->name && entry->nr >= 0; ++entry)
79		if (entry->nr == nr)
80			return entry->name;
81	return NULL;
82}
83
84long int parse_constant(char *constant_str, char **endptr)
85{
86	long int value = 0;
87	char *group, *lastpos = constant_str;
88	char *original_constant_str = constant_str;
89
90	/*
91	 * Try to parse constants separated by pipes.  Note that since
92	 * |constant_str| is an atom, there can be no spaces between the
93	 * constant and the pipe.  Constants can be either a named constant
94	 * defined in libconstants.gen.c or a number parsed with strtol.
95	 *
96	 * If there is an error parsing any of the constants, the whole process
97	 * fails.
98	 */
99	while ((group = tokenize(&constant_str, "|")) != NULL) {
100		char *end = group;
101		value |= parse_single_constant(group, &end);
102		if (end == group) {
103			lastpos = original_constant_str;
104			value = 0;
105			break;
106		}
107		lastpos = end;
108	}
109	if (endptr)
110		*endptr = lastpos;
111	return value;
112}
113
114long int parse_single_constant(char *constant_str, char **endptr)
115{
116	const struct constant_entry *entry = constant_table;
117	for (; entry->name; ++entry) {
118		if (!strcmp(entry->name, constant_str)) {
119			if (endptr)
120				*endptr = constant_str + strlen(constant_str);
121
122			return entry->value;
123		}
124	}
125
126	return strtol(constant_str, endptr, 0);
127}
128
129char *strip(char *s)
130{
131	char *end;
132	while (*s && isblank(*s))
133		s++;
134	end = s + strlen(s) - 1;
135	while (end >= s && *end && (isblank(*end) || *end == '\n'))
136		end--;
137	*(end + 1) = '\0';
138	return s;
139}
140
141char *tokenize(char **stringp, const char *delim)
142{
143	char *ret = NULL;
144
145	/* If the string is NULL or empty, there are no tokens to be found. */
146	if (stringp == NULL || *stringp == NULL || **stringp == '\0')
147		return NULL;
148
149	/*
150	 * If the delimiter is NULL or empty,
151	 * the full string makes up the only token.
152	 */
153	if (delim == NULL || *delim == '\0') {
154		ret = *stringp;
155		*stringp = NULL;
156		return ret;
157	}
158
159	char *found;
160	while (**stringp != '\0') {
161		found = strstr(*stringp, delim);
162
163		if (!found) {
164			/*
165			 * The delimiter was not found, so the full string
166			 * makes up the only token, and we're done.
167			 */
168			ret = *stringp;
169			*stringp = NULL;
170			break;
171		}
172
173		if (found != *stringp) {
174			/* There's a non-empty token before the delimiter. */
175			*found = '\0';
176			ret = *stringp;
177			*stringp = found + strlen(delim);
178			break;
179		}
180
181		/*
182		 * The delimiter was found at the start of the string,
183		 * skip it and keep looking for a non-empty token.
184		 */
185		*stringp += strlen(delim);
186	}
187
188	return ret;
189}
190