/*
 * DF-3178 statfs probe — figure out which syscall df(1) wedges on against
 * the census-lie hpfs mount.  Each probe is wrapped in alarm(10) so the
 * process survives to tell which one timed out.
 *
 * Build: cc -O2 -o statfs_probe statfs_probe.c
 */
#include <sys/mount.h>
#include <errno.h>
#include <fcntl.h>
#include <err.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

static sig_atomic_t timed_out;

static void
alarm_handler(int sig __unused)
{
	timed_out = 1;
}

static void
probe(const char *name, void (*fn)(void), void (*cleanup)(void))
{
	timed_out = 0;
	fflush(stdout);
	printf("%-22s ... ", name);
	fflush(stdout);
	alarm(10);
	fn();
	alarm(0);
	if (timed_out) {
		printf("TIMEOUT (kernel wedged in this syscall)\n");
		if (cleanup)
			cleanup();
	} else {
		printf("returned\n");
	}
	fflush(stdout);
}

static const char *P;

static void
do_statfs(void)
{
	struct statfs sb;
	if (statfs(P, &sb) < 0)
		printf("errno=%d ", errno);
	else
		printf("f_blocks=%llu f_bsize=%lu f_bfree=%llu ",
		    (unsigned long long)sb.f_blocks,
		    (unsigned long)sb.f_bsize,
		    (unsigned long long)sb.f_bfree);
}

static void
do_getfsstat(void)
{
	struct statfs *sfs;
	int i, n = getfsstat(NULL, 0, MNT_NOWAIT);
	if (n < 0) {
		printf("errno=%d ", errno);
		return;
	}
	sfs = calloc(n, sizeof(*sfs));
	n = getfsstat(sfs, n * sizeof(*sfs), MNT_NOWAIT);
	printf("%d mounts ", n);
	for (i = 0; i < n; i++)
		if (strcmp(sfs[i].f_fstypename, "hpfs") == 0)
			printf("[hpfs: f_blocks=%llu mntfrom=%s] ",
			    (unsigned long long)sfs[i].f_blocks,
			    sfs[i].f_mntfromname);
	free(sfs);
}

static void
do_open(void)
{
	int fd = open(P, O_RDONLY);
	if (fd < 0)
		printf("errno=%d ", errno);
	else {
		printf("open OK ");
		close(fd);
	}
}

static void
do_close_sync(void)
{
	sync();
}

int
main(int argc, char **argv)
{
	if (argc != 2) {
		fprintf(stderr, "usage: %s <path>\n", argv[0]);
		return 2;
	}
	P = argv[1];
	signal(SIGALRM, alarm_handler);
	printf("pid=%d path=%s\n", getpid(), P);
	probe("open(mnt)", do_open, NULL);
	probe("statfs(mnt)", do_statfs, NULL);
	probe("getfsstat", do_getfsstat, NULL);
	printf("ALL DONE\n");
	return 0;
}
