File Index
Files and directories
Sign in
FILE

proc.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/net/tcp_ao/lib/proc.c C
  1// SPDX-License-Identifier: GPL-2.0
  2#include <inttypes.h>
  3#include <pthread.h>
  4#include <stdio.h>
  5#include "../../../../../include/linux/compiler.h"
  6#include "../../../../../include/linux/kernel.h"
  7#include "aolib.h"
  8
  9struct netstat_counter {
 10	uint64_t val;
 11	char *name;
 12};
 13
 14struct netstat {
 15	char *header_name;
 16	struct netstat *next;
 17	size_t counters_nr;
 18	struct netstat_counter *counters;
 19};
 20
 21static struct netstat *lookup_type(struct netstat *ns,
 22		const char *type, size_t len)
 23{
 24	while (ns != NULL) {
 25		size_t cmp = max(len, strlen(ns->header_name));
 26
 27		if (!strncmp(ns->header_name, type, cmp))
 28			return ns;
 29		ns = ns->next;
 30	}
 31	return NULL;
 32}
 33
 34static struct netstat *lookup_get(struct netstat *ns,
 35				  const char *type, const size_t len)
 36{
 37	struct netstat *ret;
 38
 39	ret = lookup_type(ns, type, len);
 40	if (ret != NULL)
 41		return ret;
 42
 43	ret = malloc(sizeof(struct netstat));
 44	if (!ret)
 45		test_error("malloc()");
 46
 47	ret->header_name = strndup(type, len);
 48	if (ret->header_name == NULL)
 49		test_error("strndup()");
 50	ret->next = ns;
 51	ret->counters_nr = 0;
 52	ret->counters = NULL;
 53
 54	return ret;
 55}
 56
 57static struct netstat *lookup_get_column(struct netstat *ns, const char *line)
 58{
 59	char *column;
 60
 61	column = strchr(line, ':');
 62	if (!column)
 63		test_error("can't parse netstat file");
 64
 65	return lookup_get(ns, line, column - line);
 66}
 67
 68static void netstat_read_type(FILE *fnetstat, struct netstat **dest, char *line)
 69{
 70	struct netstat *type = lookup_get_column(*dest, line);
 71	const char *pos = line;
 72	size_t i, nr_elems = 0;
 73	char tmp;
 74
 75	while ((pos = strchr(pos, ' '))) {
 76		nr_elems++;
 77		pos++;
 78	}
 79
 80	*dest = type;
 81	type->counters = reallocarray(type->counters,
 82				type->counters_nr + nr_elems,
 83				sizeof(struct netstat_counter));
 84	if (!type->counters)
 85		test_error("reallocarray()");
 86
 87	pos = strchr(line, ' ') + 1;
 88
 89	if (fscanf(fnetstat, "%[^ :]", type->header_name) == EOF)
 90		test_error("fscanf(%s)", type->header_name);
 91	if (fread(&tmp, 1, 1, fnetstat) != 1 || tmp != ':')
 92		test_error("Unexpected netstat format (%c)", tmp);
 93
 94	for (i = type->counters_nr; i < type->counters_nr + nr_elems; i++) {
 95		struct netstat_counter *nc = &type->counters[i];
 96		const char *new_pos = strchr(pos, ' ');
 97		const char *fmt = " %" PRIu64;
 98
 99		if (new_pos == NULL)
100			new_pos = strchr(pos, '\n');
101
102		nc->name = strndup(pos, new_pos - pos);
103		if (nc->name == NULL)
104			test_error("strndup()");
105
106		if (unlikely(!strcmp(nc->name, "MaxConn")))
107			fmt = " %" PRId64; /* MaxConn is signed, RFC 2012 */
108		if (fscanf(fnetstat, fmt, &nc->val) != 1)
109			test_error("fscanf(%s)", nc->name);
110		pos = new_pos + 1;
111	}
112	type->counters_nr += nr_elems;
113
114	if (fread(&tmp, 1, 1, fnetstat) != 1 || tmp != '\n')
115		test_error("Unexpected netstat format");
116}
117
118static const char *snmp6_name = "Snmp6";
119static void snmp6_read(FILE *fnetstat, struct netstat **dest)
120{
121	struct netstat *type = lookup_get(*dest, snmp6_name, strlen(snmp6_name));
122	char *counter_name;
123	size_t i;
124
125	for (i = type->counters_nr;; i++) {
126		struct netstat_counter *nc;
127		uint64_t counter;
128
129		if (fscanf(fnetstat, "%ms", &counter_name) == EOF)
130			break;
131		if (fscanf(fnetstat, "%" PRIu64, &counter) == EOF)
132			test_error("Unexpected snmp6 format");
133		type->counters = reallocarray(type->counters, i + 1,
134					sizeof(struct netstat_counter));
135		if (!type->counters)
136			test_error("reallocarray()");
137		nc = &type->counters[i];
138		nc->name = counter_name;
139		nc->val = counter;
140	}
141	type->counters_nr = i;
142	*dest = type;
143}
144
145struct netstat *netstat_read(void)
146{
147	struct netstat *ret = 0;
148	size_t line_sz = 0;
149	char *line = NULL;
150	FILE *fnetstat;
151
152	/*
153	 * Opening thread-self instead of /proc/net/... as the latter
154	 * points to /proc/self/net/ which instantiates thread-leader's
155	 * net-ns, see:
156	 * commit 155134fef2b6 ("Revert "proc: Point /proc/{mounts,net} at..")
157	 */
158	errno = 0;
159	fnetstat = fopen("/proc/thread-self/net/netstat", "r");
160	if (fnetstat == NULL)
161		test_error("failed to open /proc/net/netstat");
162
163	while (getline(&line, &line_sz, fnetstat) != -1)
164		netstat_read_type(fnetstat, &ret, line);
165	fclose(fnetstat);
166
167	errno = 0;
168	fnetstat = fopen("/proc/thread-self/net/snmp", "r");
169	if (fnetstat == NULL)
170		test_error("failed to open /proc/net/snmp");
171
172	while (getline(&line, &line_sz, fnetstat) != -1)
173		netstat_read_type(fnetstat, &ret, line);
174	fclose(fnetstat);
175
176	errno = 0;
177	fnetstat = fopen("/proc/thread-self/net/snmp6", "r");
178	if (fnetstat == NULL)
179		test_error("failed to open /proc/net/snmp6");
180
181	snmp6_read(fnetstat, &ret);
182	fclose(fnetstat);
183
184	free(line);
185	return ret;
186}
187
188void netstat_free(struct netstat *ns)
189{
190	while (ns != NULL) {
191		struct netstat *prev = ns;
192		size_t i;
193
194		free(ns->header_name);
195		for (i = 0; i < ns->counters_nr; i++)
196			free(ns->counters[i].name);
197		free(ns->counters);
198		ns = ns->next;
199		free(prev);
200	}
201}
202
203static inline void
204__netstat_print_diff(uint64_t a, struct netstat *nsb, size_t i)
205{
206	if (unlikely(!strcmp(nsb->header_name, "MaxConn"))) {
207		test_print("%8s %25s: %" PRId64 " => %" PRId64,
208				nsb->header_name, nsb->counters[i].name,
209				a, nsb->counters[i].val);
210		return;
211	}
212
213	test_print("%8s %25s: %" PRIu64 " => %" PRIu64, nsb->header_name,
214			nsb->counters[i].name, a, nsb->counters[i].val);
215}
216
217void netstat_print_diff(struct netstat *nsa, struct netstat *nsb)
218{
219	size_t i, j;
220
221	while (nsb != NULL) {
222		if (unlikely(strcmp(nsb->header_name, nsa->header_name))) {
223			for (i = 0; i < nsb->counters_nr; i++)
224				__netstat_print_diff(0, nsb, i);
225			nsb = nsb->next;
226			continue;
227		}
228
229		if (nsb->counters_nr < nsa->counters_nr)
230			test_error("Unexpected: some counters disappeared!");
231
232		for (j = 0, i = 0; i < nsb->counters_nr; i++) {
233			if (strcmp(nsb->counters[i].name, nsa->counters[j].name)) {
234				__netstat_print_diff(0, nsb, i);
235				continue;
236			}
237
238			if (nsa->counters[j].val == nsb->counters[i].val) {
239				j++;
240				continue;
241			}
242
243			__netstat_print_diff(nsa->counters[j].val, nsb, i);
244			j++;
245		}
246		if (j != nsa->counters_nr)
247			test_error("Unexpected: some counters disappeared!");
248
249		nsb = nsb->next;
250		nsa = nsa->next;
251	}
252}
253
254uint64_t netstat_get(struct netstat *ns, const char *name, bool *not_found)
255{
256	if (not_found)
257		*not_found = false;
258
259	while (ns != NULL) {
260		size_t i;
261
262		for (i = 0; i < ns->counters_nr; i++) {
263			if (!strcmp(name, ns->counters[i].name))
264				return ns->counters[i].val;
265		}
266
267		ns = ns->next;
268	}
269
270	if (not_found)
271		*not_found = true;
272	return 0;
273}
~/ Read-only file preview.