/* do: "sh -x bdupdate.c" to make the executable */

/*
 * v1.0 - released to Eric Youngdale 17 jan 1994
 * v1.1 - added exit() statements after two loops, DEBUG, argv0 diags
 */

#undef DEBUG

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <limits.h>
#include <signal.h>
#include <linux/unistd.h>

/*
 * bdupdate: an attempt to unify the update and bdflush programs; this
 * should be a drop-in replacement for "update", but calls bdflush (if
 * it works) every 5 seconds, too.
 *
 * If invoked with an argument, it prints stats as-per bdflush.
 *
 * - alec <Alec.Muffett@UK.Sun.COM>
 */

_syscall2(int, bdflush, int, func, int, data);

char *bdparam[] =
{
    "Max fraction of LRU list to examine for dirty blocks",
    "Max number of dirty blocks to write each time bdflush activated",
    "Num of clean buffers to be loaded onto free list by refill_freelist",
    "Dirty block threshold for activating bdflush in refill_freelist",
    "Percentage of cache to scan for free clusters",
    "Time for data buffers to age before flushing",
    "Time for non-data (dir, bitmap, etc) buffers to age before flushing",
    "Time buffer cache load average constant",
    "LAV ratio (used to determine threshold for buffer fratricide)."
    (char *) 0
};

void
inststr(char *dst, char *src)
{
    if (strlen(src) <= strlen(dst))
    {
	char *ptr;

	for (ptr = dst; *ptr; *(ptr++) = '\0');

	strcpy(dst, src);
    } else
    {
	strncpy(dst, src, strlen(dst));
    }
}

int
main(int argc, char *argv[])
{
    int i;

    if (argc > 1)		/* print out the current parameters */
    {
	for (i = 0; bdparam[i]; i++)
	{
	    int j;
	    int data;
	    int bdflag;

	    bdflag = 2 + (i << 1);

	    j = bdflush(bdflag, (int) &data);

	    printf("%d: %5d %s\n", i, data, bdparam[i]);

	    if (j)
	    {
		fprintf(stderr, "bdupdate:(%d) bdflush(%08x,&d)=%d %d\n",
			i, bdflag, j, errno);
		break;
	    }
	}

	exit(0);
    }

#ifndef DEBUG
    for (i = 0; i < OPEN_MAX; i++)
    {
	close(i);
    }
#endif

    chdir("/");

    signal(SIGTERM, SIG_IGN);
    signal(SIGINT, SIG_IGN);
    signal(SIGCLD, SIG_IGN);

    if (!fork())		/* fork off a daemon */
    {
	inststr(argv[0], "bdflush (daemon)");

	if (i = bdflush(0, 0))	/* should never return */
	{
#ifdef DEBUG			/* this should not happen */
	    fprintf(stderr, "bdupdate: bdflush(0,0)=%d %d\n", i, errno);
#endif
	}
	exit(1);
    }

    if (!fork())		/* fork off a daemon */
    {
	int has_bdflush;

	inststr(argv[0], "update (bdflush)");

	has_bdflush = 1;

	for (;;)		/* flush expired buffers */
	{
	    if (has_bdflush)
	    {
		sleep(5);

		if (i = bdflush(1, 0))
		{
		    has_bdflush = 0;	/* oopsie! */

		    inststr(argv[0], "update (sync)");
#ifdef DEBUG
		    fprintf(stderr, "bdupdate: bdflush(1,0)=%d %d\n", i, errno);
#endif
		}
	    } else
	    {
		sleep(30);

		sync();
	    }
	}

	exit(1);
    }
    /* parent exits */
    return (0);
}


