/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* perf720 src/perf/perfagent/usr/samples/perfagent/server/SpmiSupl.c 1.5 */
/*                                                                        */
/*                                                                        */
/*                                                                        */
/* OBJECT CODE ONLY SOURCE MATERIALS                                      */
/*                                                                        */
/* COPYRIGHT International Business Machines Corp. 1992,1993              */
/* All Rights Reserved                                                    */
/*                                                                        */
/* The source code for this program is not published or otherwise         */
/* divested of its trade secrets, irrespective of what has been           */
/* deposited with the U.S. Copyright Office.                              */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
static char *Sccs_id = "@(#)19  1.3  src/perf/perfagent/usr/samples/perfagent/server/SpmiSupl.c, perfagent, perf411, 9430C411a 7/29/94 17:32:57";

/*
 * COMPONENT_NAME: (PERFAGENT) - Performance Agent
 *
 * FUNCTIONS:      Sample code
 *
 * ORIGINS:        30
 *
 * (C) COPYRIGHT International Business Machines Corp. 1992, 1993
 * All Rights Reserved
 * Licensed Material - Property of IBM
 *
 * US Government Users Restricted Rights - Use, duplication or
 * disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
 *

 THE SOURCE CODE EXAMPLES PROVIDED BY IBM ARE ONLY INTENDED TO ASSIST IN THE
 DEVELOPMENT OF A WORKING SOFTWARE PROGRAM.  THE SOURCE CODE EXAMPLES DO NOT
 FUNCTION AS WRITTEN:  ADDITIONAL CODE IS REQUIRED.  IN ADDITION, THE SOURCE
 CODE EXAMPLES MAY NOT COMPILE AND/OR BIND SUCCESSFULLY AS WRITTEN.

 INTERNATIONAL BUSINESS MACHINES CORPORATION PROVIDES THE SOURCE CODE
 EXAMPLES, BOTH INDIVIDUALLY AND AS ONE OR MORE GROUPS, "AS IS" WITHOUT
 WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT
 LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
 OF THE SOURCE CODE EXAMPLES, BOTH INDIVIDUALLY AND AS ONE OR MORE GROUPS,
 IS WITH YOU.  SHOULD ANY PART OF THE SOURCE CODE EXAMPLES PROVE
 DEFECTIVE, YOU (AND NOT IBM OR AN AUTHORIZED RISC System/6000* WORKSTATION
 DEALER) ASSUME THE ENTIRE COST OF ALL NECESSARY SERVICING, REPAIR OR
 CORRECTION.

 IBM does not warrant that the contents of the source code examples, whether
 individually or as one or more groups, will meet your requirements or that
 the source code examples are error-free.

 * RISC System/6000 is a trademark of International Business Machines
   Corporation.
*/

#include <stdio.h>
#include <sys/signal.h>
#include <sys/Spmidef.h>

#ifdef _AIX
#define CONST  const
#else
#define CONST
#endif

extern char SpmiErrmsg[];

/*
	The data area where statistics are passed to the Spmi interface
	must be defined as a structure (not typedef'ed).  The structure
	can reside in local memory and be copied to shared memory whenever
	new statistics values are calculated -- or it can be updated
	directly in shared memory as this module does.

	Please note that shared memory is NOT reserved for the entire
	structure size unless the last field in the structure is referenced
	in the table of statistics referring to the structure.  For example,
	the structure "dat" defines 10 4-byte long integers but only the
	first four are referenced in the statistics table.  The shared
	memory area reserved is thus 4x4 = 16 bytes.  Attempts to reference
	the six last elements in shared memory will cause a segmentation fault
	or destroy other data areas.

	The two structures below are used as data area definitions for
	the sample program.
*/

struct dat
{
	u_long		a;
	u_long		b;
	u_long		c;
	u_long		d;
	u_long		e;
	u_long		f;
	u_long		g;
	u_long		h;
	u_long		i;
	u_long		j;
};

struct inst
{
	float		a;
	u_long		b;
};


/*
	The following two tables of type (struct SpmiRawStat) define
	two sets of statistics.  You must define one table for each set
	of statistics you have.  A set of statistics is defines as all
	statistics that have identical path names, except for the
	name of the statistic itself.  For example,

		DDS/IBM/Bingo/players/losers and
		DDS/IBM/Bingo/players/winners

	belong to the same set of statistics, while

		DDS/IBM/Bingo/players/losers and
		DDS/IBM/Blackjack/players/losers

	belong to two different sets.  When the (struct SpmiRawStat)
	entry is defined in a dynamic data supplier program, the last
	field in the structure need not be specified or must be
	specified as NULL.  Other fields must be filled in as follows:

		Field		Format		Contents
		-----		--------		--------------------------------------
		  1		char[32]		Short name of statistic
		  2		char[64]		Description of statistic
		  3		numeric		Lower range for plotting
		  4		numeric		Upper range for plotting
		  5		ValType		See Spmidef.h
		  6		DataType		Data format to deliver to consumer (see Spmidef.h)
		  7		numeric		The ASN.1 number assigned to the statistic
		  8.1		structure	Name of defined statistics structure
		  8.2		fieldname	Name of data field in statistics structure
		  8.3		DataType		Data format of field in statistics structure
*/

static CONST struct SpmiRawStat PUStats[] = {
{  "gadgets", "Fake counter value", 0, 100, SiCounter,
	SiLong, 1, SZ_OFF(dat, a, SiULong)},
{  "widgets", "Another fake counter value", 0, 100, SiCounter,
	SiLong, 2, SZ_OFF(dat, b, SiULong)},
};

static CONST struct SpmiRawStat FakeMemStats[] = {
{  "level", "Fake quantity value", 0, 100, SiQuantity,
	SiLong, 1, SZ_OFF(dat, c, SiULong)},
{  "queue", "Another fake quantity value", 0, 100, SiQuantity,
	SiLong, 2, SZ_OFF(dat, d, SiULong)},
};

/*
	The following statistics table define two statistics that can be
	instantiated dynamically.  To be so, they must be referenced from
	at least one context definition in the table of instantiable
	context table.  The table may be referenced from multiple contexts
	some of which may be static contexts.
*/
static CONST struct SpmiRawStat InstStats[] = {
{  "problems", "Fake counter value", 0, 100, SiCounter,
	SiLong, 1, SZ_OFF(inst, a, SiLong)},
{  "solutions", "Another fake counter value", 0, 100, SiCounter,
	SiLong, 2, SZ_OFF(inst, b, SiLong)},
};

/*
	The following table defines the tree structure of contexts as
	defined by this module.  Each context is defined by one table
	entry.  The fields in the contexts are:

		Field		Format		Contents
		-----		--------		--------------------------------------
		  1		char[64]		Full path name of the context to create
		  2		char[64]		Description of context
		  3		numeric		ASN.1 number to be assigned to the context
		  4		pointer		Pointer to statistics table, NULL if none
		  5		numeric		Count of elements in above statistics table
									Use STAT_L to find number of elements.
		  7		pointer		Must be specified as NULL
		  8		numeric		Must be specified as 0 (zero)
		  9		pointer		Must be specified as NULL
		 10		SiInstFreq	See Spmidef.h

*/
static CONST cx_create cx_table[] = {
	{"DDS/IBM", "IBM-defined Dynamic Data Suppliers", 2, 0,
      NULL, 0, NULL, 0, NULL, SiNoInst},
   {"DDS/IBM/TEST", "Bogus Context Number 1", 210, 0,
      (struct SpmiRawStat *)PUStats, STAT_L(PUStats), NULL, 0, NULL, SiNoInst},
   {"DDS/IBM/MORETEST", "Bogus Context Number 2", 211, 0,
      NULL, 0, NULL, 0, NULL, SiNoInst},
   {"DDS/IBM/MORETEST/SubTest", "Bogus Context Number 3", 212, 0,
      (struct SpmiRawStat *)FakeMemStats, STAT_L(FakeMemStats),
		NULL, 0, NULL, SiNoInst},
};

/*
	The table below defines two contexts that may be instantiated
   multiple times.
*/
static CONST cx_create inst_table[] = {
   {"DDS/IBM/MORETEST/INST1", "Instantiable Context Number 1", 215, 0,
      (struct SpmiRawStat *)InstStats, STAT_L(InstStats),
		NULL, 0, NULL, SiNoInst},
   {"DDS/IBM/MORETEST/INST2", "Instantiable Context Number 2", 216, 0,
      (struct SpmiRawStat *)InstStats, STAT_L(InstStats),
		NULL, 0, NULL, SiNoInst},
};

static int			CxCount = CX_L(cx_table);	/* Count of contexts defined	*/
static int			InstCount = CX_L(inst_table);/* Count of contexts defined*/
static SpmiShare	*dataarea = NULL;				/* Shared memory pointer		*/
static struct dat	*d = NULL;						/* Pointer to stats data area	*/
static struct inst *pt1 = NULL;					/* Pointer to stats data area	*/
static struct inst *pt2 = NULL;					/* Pointer to stats data area	*/
static int			i;
static struct timezone tzone;

/*
	This function will make sure the shared memory allocated by this
	module is released before the module exits.  The function is called
	whenever the module is about to exit.
*/
#ifdef _NO_PROTO
void SpmiStopMe(sig)
int	sig;
#else
void SpmiStopMe(int sig)
#endif
{
	if (sig)
		printf("Killed by signal %d\n", sig);
	dataarea = NULL;
	SpmiExit();
	exit(0);
}

/*
	Module (daemon) main function.
*/
int main()
{
	/*
		Call the function SpmiDdsInit() to allocate shared memory and
		contact the Spmi interface.  If this succeeds, the function
		will return with the address of the shared memory.

		The first two arguments are the table of static contexts
		to create and the count of elements in that table.  The next
		two arguments are the table of instantiable contexts and the
		count of elements in that table.  The latter to can be NULL
		and zero if your data supplier module does not require
		contexts to be added or deleted on the fly.

		The last argument is the name of a file, which either (1) must
		exist and be writeable by the user executing this code, or (2)
		can be created by the user.  It is used to generate a key for
		the shared memory area and must, of course, be UNIQUE between
		data supplier medules.
	*/
#ifdef _AIX
	dataarea = SpmiDdsInit((cx_create *)cx_table, CxCount,
				 (cx_create *)inst_table, InstCount, "/etc/SpmiSupl_hook");
#else
	dataarea = SpmiDdsInit((cx_create *)cx_table, CxCount,
				 (cx_create *)inst_table, InstCount, "/etc/SpmiSupl_hook", 16*1024);
#endif
	if (!dataarea)
	{
		fprintf(stderr, "%s\n", SpmiErrmsg);
		exit(-1);
	}

	/*
		We got the shared memory and Spmi registered our presence.
		Now make sure we free the shared memory area if we get killed.
	*/
#ifdef _NO_PROTO
	signal(SIGTERM, (void(*)())SpmiStopMe);
	signal(SIGSEGV, (void(*)())SpmiStopMe);
	signal(SIGINT,  (void(*)())SpmiStopMe);
	signal(SIGHUP,  (void(*)())SpmiStopMe);
	signal(SIGQUIT, (void(*)())SpmiStopMe);
	signal(SIGILL,  (void(*)())SpmiStopMe);
	signal(SIGSYS,  (void(*)())SpmiStopMe);
#else
	signal(SIGTERM, (void(*)(int))SpmiStopMe);
	signal(SIGSEGV, (void(*)(int))SpmiStopMe);
	signal(SIGINT,  (void(*)(int))SpmiStopMe);
	signal(SIGHUP,  (void(*)(int))SpmiStopMe);
	signal(SIGQUIT, (void(*)(int))SpmiStopMe);
	signal(SIGILL,  (void(*)(int))SpmiStopMe);
	signal(SIGSYS,  (void(*)(int))SpmiStopMe);
#endif /* _NO_PROTO */

	/*
		The field SiShArea in shared memory has the address of the
		area where you are supposed to deliver your statistics.
		In this sample module, we simply set a pointer to point
		at this data area.
	*/
	d = (struct dat *)&dataarea->SiShArea[0];

	/*
		It is normal and recommended that you initialize the data area
		with values that make sense, even before anybody uses the data.
		This includes setting the time stamp at the time you do it.
	*/
	gettimeofday(&dataarea->SiShT, &tzone);
	d->a = 22;
	d->b = 42;
	d->c = 28;
	d->d = 62;

	/*
		The module now runs as long as the flag SiShGoAway is false.
		Data consumer programs may set this flag if abnormal conditions
		are detected.
		For each iteration, the module produces whatever statistics it
		can supply.  In this module everything is bogus, and the loop
		is a sleep loop.  In a real data supplier module, you could
		use any timer function to drive you, or you could depend on
		some external function waking you up.

		For each time the module updates the data area, the time must
		be set as shown in the gettimeofday() call.
	*/
	while(!dataarea->SiShGoAway)
	{
#ifdef _AIX
      if (dataarea->SiShSubscrib)
         usleep(dataarea->SiShInterval * 1000);
      else
         sleep(5);
#else
		sleep(1);
#endif
		gettimeofday(&dataarea->SiShT, &tzone);
		i = dataarea->SiShT.tv_sec & 0xff;
		d->a += i;
		i = dataarea->SiShT.tv_sec & 0xf;
		d->b += i;
		i = dataarea->SiShT.tv_sec & 0x20f;
		d->c += i;
		d->c = d->c & 0xffff;
		i = dataarea->SiShT.tv_sec & 0x7f;
		d->d += i;
		d->d = d->d & 0xffff;

		/*
			-------------------------------------------------------------
			The lines below are concerned with a context that will be
			dynamically added and deleted.  The data area used is pointed
			to by "pt1". 
			-------------------------------------------------------------

			The first piece of code ADDS a context.  Notice that you must
			supply the following arguments:
				
				1.	Index of context in instantiable context table.
				2.	Name of parent context.
				3.	Name of this context.
				4.	Descriptive text of this context.

			The function call returns the address of the data area in
			shared memory.
		*/
		if (((dataarea->SiShT.tv_sec % 59) == 0) && (!pt1))
		{
			if (!(pt1 = (struct inst *)SpmiDdsAddCx(1, "DDS/IBM/MORETEST/Bingo",
					"Dynamically added", 1)))
				printf("Failed to add context \"Bingo\"\n");
			else
				printf("Context \"Bingo\" added, addr = %08x\n", (u_long)pt1);
		}

		/*
			This piece of code DELETES a previously added context.  The
			only argument supplied is the previously returned data area
			address of the context.  If the operation is successful, a
			zero is returned; otherwise non-zero.
		*/
		if (((dataarea->SiShT.tv_sec % 61) == 0) && (pt1))
		{
			printf("Trying to delete context, addr = %08x\n", (u_long)pt1);
			if ((i = SpmiDdsDelCx((char *)pt1)))
				printf("Failed to delete context, addr = %08x\n", (u_long)pt1);
			else
			{
				printf("Succeeded in deleting context, addr = %08x\n", (u_long)pt1);
				pt1 = NULL;
			}
		}

		/*
			This piece of code updates the data values in the data area
			with bogus values.  Notice that this happens directly in
			shared memory.
		*/
		if (pt1)
		{
			pt1->a = d->a / 100;
			pt1->b = d->b / 50;
		}

		/*
			-------------------------------------------------------------
			The lines below are concerned with a context that will be
			dynamically added but never deleted.  The data area used is
			pointed to by "pt2".  Its only purpose is to illustrate that you
			can instantiate multiple contexts with different parents and
			with the same or different statistics.
			-------------------------------------------------------------
		*/
		if (((dataarea->SiShT.tv_sec % 29) == 0) && (!pt2))
		{
			if (!(pt2 = (struct inst *)SpmiDdsAddCx(0, "DDS/IBM/TEST/bongo",
					"Dynamically added", 10)))
				printf("Failed to add context \"bongo\"\n");
			else
				printf("Context \"bongo\" added, addr = %08x\n", (u_long)pt2);
		}
		if (pt2)
		{
			pt2->a = pt2->a + (d->a & 0xff);
			pt2->b = pt2->b + (d->b & 0xff);
		}
	}

	if (dataarea->SiShGoAway)
		printf("Exiting because SiShGoAway is set\n");
	else
		printf("Exiting because loop was broken, somehow\n");
	
	SpmiStopMe(0);
}