⬢ DragonFlyBSD Kernel Audit
DF-5308 / diag.c
← back to finding ↓ download raw
/* diag: how does read() on the ugen node behave, and does N blocked
 * readers saturate the fifo refcount (ioctl -> ENXIO)? */
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

int
main(int argc, char **argv)
{
	const char *node = argc > 1 ? argv[1] : "/dev/usb/0.2.1";
	int n = argc > 2 ? atoi(argv[2]) : 254;
	int fd, i, x, err;
	pid_t pid, pids[512];
	char buf[8];

	setvbuf(stdout, NULL, _IONBF, 0);
	fd = open(node, O_RDONLY);
	if (fd < 0) {
		printf("open: %s\n", strerror(errno));
		return (1);
	}
	/* single read behavior with alarm */
	pid = fork();
	if (pid == 0) {
		alarm(4);
		i = read(fd, buf, sizeof(buf));
		if (i < 0)
			_exit(100 + (errno & 0x1f));
		_exit(i);
	}
	waitpid(pid, &i, 0);
	if (WIFSIGNALED(i))
		printf("single read: blocked (SIGALRM) -> ref held while asleep\n");
	else if (WEXITSTATUS(i) >= 100)
		printf("single read: error errno=%d\n", WEXITSTATUS(i) - 100);
	else
		printf("single read: returned %d bytes\n", WEXITSTATUS(i));

	/* saturation */
	for (i = 0; i < n; i++) {
		pids[i] = fork();
		if (pids[i] == 0) {
			for (;;)
				read(fd, buf, sizeof(buf));
			_exit(0);
		}
	}
	for (i = 0; i < 20; i++) {
		usleep(250000);
		err = ioctl(fd, 0x12345678, &x);
		printf("t+%dms: ioctl=%d errno=%d\n", (i + 1) * 250, err, errno);
		if (err != 0)
			break;
	}
	for (i = 0; i < n; i++)
		kill(pids[i], 9);
	for (i = 0; i < n; i++)
		waitpid(pids[i], NULL, 0);
	err = ioctl(fd, 0x12345678, &x);
	printf("after drain: ioctl=%d errno=%d\n", err, errno);
	return (0);
}