/*
 * Copyright (c) 1997-2001 Silicon Graphics, Inc.  All Rights Reserved.
 *
 * Exercise pmNewContext() for archives close to the NOFILE max fd limit.
 * For incident: 504616
 */

#include <stdio.h>
#include <unistd.h>
#include <strings.h>
#include <errno.h>
#include <sys/resource.h>
#include <pcp/pmapi.h>
#include <pcp/impl.h>

#include "localconfig.h"

#if PCP_VER < 2200
#define PRINTF_P_PFX ""
#endif

static char	*sfx[] = { "0", "index", "meta" };

int
main(int argc, char **argv)
{
    int		c;
    int		sts;
    int		errflag = 0;
    int		i;
    int		j;
    int		max_ctx;
    int		max_nofile;
    int		numopen = 0;			/* pander to gcc */
    int		ctx = -1;			/* pander to gcc */
    int		last_ctx;
    char	buf[100];
    char	lbuf[100];
    struct rlimit	top;
    char	*start = NULL;
    char	*end;

    __pmSetProgname(pmProgname);

    while ((c = getopt(argc, argv, "D:?")) != EOF) {
	switch (c) {

	case 'D':	/* debug flag */
	    sts = __pmParseDebug(optarg);
	    if (sts < 0) {
		fprintf(stderr, "%s: unrecognized debug flag specification (%s)\n",
		    pmProgname, optarg);
		errflag++;
	    }
	    else
		pmDebug |= sts;
	    break;

	case '?':
	default:
	    errflag++;
	    break;
	}
    }

    sts = getrlimit(RLIMIT_NOFILE, &top);

    for (max_nofile = 30; max_nofile > 20; max_nofile--) {
	top.rlim_cur = max_nofile;
	sts = setrlimit(RLIMIT_NOFILE, &top);
	if (sts < 0) {
	    fprintf(stderr, "setrlimit(NOFILE=%d) failed: %s\n", max_nofile, strerror(errno));
	    exit(1);
	}
	sts = dup(0);
	if (sts < 0) {
	    fprintf(stderr, "dup(0) failed: %s\n", strerror(errno));
	    exit(1);
	}
	max_ctx = (max_nofile + 2 - sts) / 3;
	close(sts);

	printf("max fd: %d max ctx#: %d\n", max_nofile, max_ctx);
	last_ctx = -1;

	for (i = 0; i <= max_ctx; i++) {

	    for (j = 0; j < 3; j++) {
		sprintf(lbuf, "qa-tmp-%d.%s", i, sfx[j]);
		sprintf(buf, "%s.%s", argv[optind], sfx[j]);
		sts = link(buf, lbuf);
		if (sts < 0) {
		    fprintf(stderr, "link %s -> %s failed: %s\n",
			lbuf, buf, strerror(errno));
		    break;
		}
	    }

	    sprintf(lbuf, "qa-tmp-%d", i);
	    ctx = pmNewContext(PM_CONTEXT_ARCHIVE, lbuf);

	    for (j = 0; j < 3; j++) {
		sprintf(lbuf, "qa-tmp-%d.%s", i, sfx[j]);
		sts = unlink(lbuf);
		if (sts < 0) {
		    fprintf(stderr, "unlink %s failed: %s\n",
			lbuf, strerror(errno));
		    break;
		}
	    }

	    if (ctx < 0) {
		printf("pmNewContext(): %s\n", pmErrStr(ctx));
		if (i != max_ctx && i != max_ctx-1)
		    printf("Error: failure after ctx# %d, expected after %d or %d\n", last_ctx, max_ctx, max_ctx-1);
		break;
	    }
	    else
		numopen++;
	    last_ctx = ctx;
	}

	if (ctx >= 0)
	    printf("Error: pmNewContext() did not fail?\n");

	for (i = 0; i <= last_ctx; i++)
	    pmDestroyContext(i);

	if (start == NULL) {
	    start = sbrk(0);
	    numopen = 0;
	}
    }

    end = sbrk(0);

    if (end - start > 16*1024) {
	printf("Memory leak? after first pass, %ld bytes per archive open-close\n",
	    (long)((end - start) / numopen));
	printf("start: " PRINTF_P_PFX "%p end: " PRINTF_P_PFX "%p diff: %ld numopen: %d\n", start, end,
		(long)(end - start), numopen);
    }
    
    return 0;
}
