/*
 * DF-4760 — holder for /dev/vinum/controld (the vinum daemon device).
 *
 * free_vinum() (sys/dev/raid/vinum/vinum.c:215-219) contains:
 *
 *     while ((vinum_conf.flags & (VF_STOPPING | VF_DAEMONOPEN))
 *         == (VF_STOPPING | VF_DAEMONOPEN)) {
 *         queue_daemon_request(daemonrq_return, di);   // kmalloc every pass
 *         tsleep(&vinumclose, 0, "vstop", 1);          // 1 tick
 *     }
 *
 * Opening /dev/vinum/controld (root-only, 0600) sets VF_DAEMONOPEN but
 * does NOT start the kernel vinum_daemon thread; and even if that thread
 * is running, its very first daemonrq_return makes it drain the queue and
 * EXIT (vinumdaemon.c:150-162) — so after at most one consumed request,
 * nothing ever drains the queue again. Every subsequent loop iteration
 * (100/s) kmalloc()s a struct daemonq that is never freed, and the loop
 * only ends when this holder closes the device.
 *
 * While this process holds the fd, `kldunload vinum` therefore:
 *   1. hangs forever in the free_vinum() loop (tsleep channel "vstop"), and
 *   2. leaks kernel memory at ~100 * sizeof(struct daemonq) bytes/second.
 *
 * Build: cc -O -o hold_daemon hold_daemon.c
 * Run:   ./hold_daemon <seconds-to-hold>
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

int
main(int argc, char **argv)
{
	int hold = (argc > 1) ? atoi(argv[1]) : 30;
	int fd = open("/dev/vinum/controld", O_RDWR);

	if (fd < 0) {
		perror("open /dev/vinum/controld");
		return 1;
	}
	printf("[holder] opened /dev/vinum/controld fd=%d (VF_DAEMONOPEN set); "
	       "holding for %ds\n", fd, hold);
	fflush(stdout);
	sleep(hold);
	printf("[holder] closing now - free_vinum loop should exit\n");
	fflush(stdout);
	close(fd);
	sleep(1);
	printf("[holder] done\n");
	fflush(stdout);
	return 0;
}
