xref: /onnv-gate/usr/src/cmd/perl/contrib/Sun/Solaris/Exacct/Exacct.xs (revision 12388:1bc8d55b0dfd)
10Sstevel@tonic-gate /*
2*12388SJohn.Sonnenschein@Sun.COM  * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved.
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * Exacct.xs contains XS code for creating various exacct-related constants,
50Sstevel@tonic-gate  * and for providing wrappers around exacct error handling and
60Sstevel@tonic-gate  * accounting-related system calls.  It also contains commonly-used utility
70Sstevel@tonic-gate  * code shared by its sub-modules.
80Sstevel@tonic-gate  */
90Sstevel@tonic-gate 
100Sstevel@tonic-gate #include <string.h>
110Sstevel@tonic-gate #include "exacct_common.xh"
120Sstevel@tonic-gate 
130Sstevel@tonic-gate /*
140Sstevel@tonic-gate  * Pull in the file generated by extract_defines.  This contains a table
150Sstevel@tonic-gate  * of numeric constants and their string equivalents which have been extracted
160Sstevel@tonic-gate  * from the various exacct header files by the extract_defines script.
170Sstevel@tonic-gate  */
180Sstevel@tonic-gate #include "ExacctDefs.xi"
190Sstevel@tonic-gate 
200Sstevel@tonic-gate /*
210Sstevel@tonic-gate  * Object stash pointers - caching these speeds up the creation and
220Sstevel@tonic-gate  * typechecking of perl objects by removing the need to do a hash lookup.
230Sstevel@tonic-gate  * The peculiar variable names are so that typemaps can generate the correct
240Sstevel@tonic-gate  * package name using the typemap '$Package' variable as the root of the name.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate HV *Sun_Solaris_Exacct_Catalog_stash;
270Sstevel@tonic-gate HV *Sun_Solaris_Exacct_File_stash;
280Sstevel@tonic-gate HV *Sun_Solaris_Exacct_Object_Item_stash;
290Sstevel@tonic-gate HV *Sun_Solaris_Exacct_Object_Group_stash;
300Sstevel@tonic-gate HV *Sun_Solaris_Exacct_Object__Array_stash;
310Sstevel@tonic-gate 
320Sstevel@tonic-gate /*
330Sstevel@tonic-gate  * Pointer to part of the hash tree built by define_catalog_constants in
340Sstevel@tonic-gate  * Catalog.xs.  This is used by catalog_id_str() when mapping from a catalog
350Sstevel@tonic-gate  * to an id string.
360Sstevel@tonic-gate  */
370Sstevel@tonic-gate HV *IdValueHash = NULL;
380Sstevel@tonic-gate 
390Sstevel@tonic-gate /*
400Sstevel@tonic-gate  * Last buffer size used for packing and unpacking exacct objects.
410Sstevel@tonic-gate  */
420Sstevel@tonic-gate static int last_bufsz = 0;
430Sstevel@tonic-gate 
440Sstevel@tonic-gate /*
450Sstevel@tonic-gate  * Common utility code.  This is placed here instead of in the sub-modules to
460Sstevel@tonic-gate  * reduce the number of cross-module linker dependencies that are required,
470Sstevel@tonic-gate  * although most of the code more properly belongs in the sub-modules.
480Sstevel@tonic-gate  */
490Sstevel@tonic-gate 
500Sstevel@tonic-gate /*
510Sstevel@tonic-gate  * This function populates the various stash pointers used by the ::Exacct
520Sstevel@tonic-gate  * module.  It is called from each of the module BOOT sections to ensure the
530Sstevel@tonic-gate  * stash pointers are initialised on startup.
540Sstevel@tonic-gate  */
550Sstevel@tonic-gate void
init_stashes(void)560Sstevel@tonic-gate init_stashes(void)
570Sstevel@tonic-gate {
580Sstevel@tonic-gate 	if (Sun_Solaris_Exacct_Catalog_stash == NULL) {
590Sstevel@tonic-gate 		Sun_Solaris_Exacct_Catalog_stash =
600Sstevel@tonic-gate 		    gv_stashpv(PKGBASE "::Catalog", TRUE);
610Sstevel@tonic-gate 		Sun_Solaris_Exacct_File_stash =
620Sstevel@tonic-gate 		    gv_stashpv(PKGBASE "::File", TRUE);
630Sstevel@tonic-gate 		Sun_Solaris_Exacct_Object_Item_stash =
640Sstevel@tonic-gate 		    gv_stashpv(PKGBASE "::Object::Item", TRUE);
650Sstevel@tonic-gate 		Sun_Solaris_Exacct_Object_Group_stash =
660Sstevel@tonic-gate 		    gv_stashpv(PKGBASE "::Object::Group", TRUE);
670Sstevel@tonic-gate 		Sun_Solaris_Exacct_Object__Array_stash =
680Sstevel@tonic-gate 		    gv_stashpv(PKGBASE "::Object::_Array", TRUE);
690Sstevel@tonic-gate 	}
700Sstevel@tonic-gate }
710Sstevel@tonic-gate 
720Sstevel@tonic-gate /*
730Sstevel@tonic-gate  * This function populates the @_Constants array in the specified package
740Sstevel@tonic-gate  * based on the values extracted from the exacct header files by the
750Sstevel@tonic-gate  * extract_defines script and written to the .xi file which is included above.
760Sstevel@tonic-gate  * It also creates a const sub for each constant that returns the associcated
770Sstevel@tonic-gate  * value.  It should be called from the BOOT sections of modules that export
780Sstevel@tonic-gate  * constants.
790Sstevel@tonic-gate  */
800Sstevel@tonic-gate #define	CONST_NAME "::_Constants"
810Sstevel@tonic-gate void
define_constants(const char * pkg,constval_t * cvp)820Sstevel@tonic-gate define_constants(const char *pkg, constval_t *cvp)
830Sstevel@tonic-gate {
840Sstevel@tonic-gate 	HV		*stash;
850Sstevel@tonic-gate 	char		*name;
860Sstevel@tonic-gate 	AV		*constants;
870Sstevel@tonic-gate 
880Sstevel@tonic-gate 	/* Create the new perl @_Constants variable. */
890Sstevel@tonic-gate 	stash = gv_stashpv(pkg, TRUE);
900Sstevel@tonic-gate 	name = New(0, name, strlen(pkg) + sizeof (CONST_NAME), char);
910Sstevel@tonic-gate 	PERL_ASSERT(name != NULL);
920Sstevel@tonic-gate 	strcpy(name, pkg);
930Sstevel@tonic-gate 	strcat(name, CONST_NAME);
940Sstevel@tonic-gate 	constants = perl_get_av(name, TRUE);
950Sstevel@tonic-gate 	Safefree(name);
960Sstevel@tonic-gate 
970Sstevel@tonic-gate 	/* Populate @_Constants from the contents of the generated array. */
980Sstevel@tonic-gate 	for (; cvp->name != NULL; cvp++) {
990Sstevel@tonic-gate 		newCONSTSUB(stash, (char *)cvp->name, newSVuv(cvp->value));
1000Sstevel@tonic-gate 		av_push(constants, newSVpvn((char *)cvp->name, cvp->len));
1010Sstevel@tonic-gate 	}
1020Sstevel@tonic-gate }
1030Sstevel@tonic-gate #undef CONST_NAME
1040Sstevel@tonic-gate 
1050Sstevel@tonic-gate /*
1060Sstevel@tonic-gate  * Return a new Catalog object - only accepts an integer catalog value.
1070Sstevel@tonic-gate  * Use this purely for speed when creating Catalog objects from other XS code.
1080Sstevel@tonic-gate  * All other Catalog object creation should be done with the perl new() method.
1090Sstevel@tonic-gate  */
1100Sstevel@tonic-gate SV*
new_catalog(uint32_t cat)1110Sstevel@tonic-gate new_catalog(uint32_t cat)
1120Sstevel@tonic-gate {
1130Sstevel@tonic-gate 	SV *iv, *ref;
1140Sstevel@tonic-gate 
1150Sstevel@tonic-gate 	iv = newSVuv(cat);
1160Sstevel@tonic-gate 	ref = newRV_noinc(iv);
1170Sstevel@tonic-gate 	sv_bless(ref, Sun_Solaris_Exacct_Catalog_stash);
1180Sstevel@tonic-gate 	SvREADONLY_on(iv);
1190Sstevel@tonic-gate 	return (ref);
1200Sstevel@tonic-gate }
1210Sstevel@tonic-gate 
1220Sstevel@tonic-gate /*
1230Sstevel@tonic-gate  * Return the integer catalog value from the passed Catalog or IV.
1240Sstevel@tonic-gate  * Calls croak() if the SV is not of the correct type.
1250Sstevel@tonic-gate  */
1260Sstevel@tonic-gate ea_catalog_t
catalog_value(SV * catalog)1270Sstevel@tonic-gate catalog_value(SV *catalog)
1280Sstevel@tonic-gate {
1290Sstevel@tonic-gate 	SV	*sv;
1300Sstevel@tonic-gate 
1310Sstevel@tonic-gate 	/* If a reference, dereference and check it is a Catalog. */
1320Sstevel@tonic-gate 	if (SvROK(catalog)) {
1330Sstevel@tonic-gate 		sv = SvRV(catalog);
1340Sstevel@tonic-gate 		if (SvIOK(sv) &&
1350Sstevel@tonic-gate 		    SvSTASH(sv) == Sun_Solaris_Exacct_Catalog_stash) {
1360Sstevel@tonic-gate 			return (SvIV(sv));
1370Sstevel@tonic-gate 		} else {
1380Sstevel@tonic-gate 			croak("Parameter is not a Catalog or integer");
1390Sstevel@tonic-gate 		}
1400Sstevel@tonic-gate 
1410Sstevel@tonic-gate 	/* For a plain IV, just return the value. */
1420Sstevel@tonic-gate 	} else if (SvIOK(catalog)) {
1430Sstevel@tonic-gate 		return (SvIV(catalog));
1440Sstevel@tonic-gate 
1450Sstevel@tonic-gate 	/* Anything else is an error */
1460Sstevel@tonic-gate 	} else {
1470Sstevel@tonic-gate 		croak("Parameter is not a Catalog or integer");
1480Sstevel@tonic-gate 	}
1490Sstevel@tonic-gate }
1500Sstevel@tonic-gate 
1510Sstevel@tonic-gate /*
1520Sstevel@tonic-gate  * Return the string value of the id subfield of an ea_catalog_t.
1530Sstevel@tonic-gate  */
1540Sstevel@tonic-gate char *
catalog_id_str(ea_catalog_t catalog)1550Sstevel@tonic-gate catalog_id_str(ea_catalog_t catalog)
1560Sstevel@tonic-gate {
1570Sstevel@tonic-gate 	static ea_catalog_t	cat_val = ~0U;
1580Sstevel@tonic-gate 	static HV		*cat_hash = NULL;
1590Sstevel@tonic-gate 	ea_catalog_t		cat;
1600Sstevel@tonic-gate 	ea_catalog_t		id;
1610Sstevel@tonic-gate 	char			key[12];    /* Room for dec(2^32) digits. */
1620Sstevel@tonic-gate 	SV			**svp;
1630Sstevel@tonic-gate 
1640Sstevel@tonic-gate 	cat = catalog & EXC_CATALOG_MASK;
1650Sstevel@tonic-gate 	id = catalog & EXD_DATA_MASK;
1660Sstevel@tonic-gate 
1670Sstevel@tonic-gate 	/* Fetch the correct id subhash if the catalog has changed. */
1680Sstevel@tonic-gate 	if (cat_val != cat) {
1690Sstevel@tonic-gate 		snprintf(key, sizeof (key), "%d", cat);
1700Sstevel@tonic-gate 		PERL_ASSERT(IdValueHash != NULL);
1710Sstevel@tonic-gate 		svp = hv_fetch(IdValueHash, key, strlen(key), FALSE);
1720Sstevel@tonic-gate 		if (svp == NULL) {
1730Sstevel@tonic-gate 			cat_val = ~0U;
1740Sstevel@tonic-gate 			cat_hash = NULL;
1750Sstevel@tonic-gate 		} else {
1760Sstevel@tonic-gate 			HV *hv;
1770Sstevel@tonic-gate 
1780Sstevel@tonic-gate 			cat_val = cat;
1790Sstevel@tonic-gate 			hv = (HV *)SvRV(*svp);
1800Sstevel@tonic-gate 			PERL_ASSERT(hv != NULL);
1810Sstevel@tonic-gate 			svp = hv_fetch(hv, "value", 5, FALSE);
1820Sstevel@tonic-gate 			PERL_ASSERT(svp != NULL);
1830Sstevel@tonic-gate 			cat_hash = (HV *)SvRV(*svp);
1840Sstevel@tonic-gate 			PERL_ASSERT(cat_hash != NULL);
1850Sstevel@tonic-gate 		}
1860Sstevel@tonic-gate 	}
1870Sstevel@tonic-gate 
1880Sstevel@tonic-gate 	/* If we couldn't find the hash, it is a catalog we don't know about. */
1890Sstevel@tonic-gate 	if (cat_hash == NULL) {
1900Sstevel@tonic-gate 		return ("UNKNOWN_ID");
1910Sstevel@tonic-gate 	}
1920Sstevel@tonic-gate 
1930Sstevel@tonic-gate 	/* Fetch the value from the selected catalog and return it. */
1940Sstevel@tonic-gate 	snprintf(key, sizeof (key), "%d", id);
1950Sstevel@tonic-gate 	svp = hv_fetch(cat_hash, key, strlen(key), TRUE);
1960Sstevel@tonic-gate 	if (svp == NULL) {
1970Sstevel@tonic-gate 		return ("UNKNOWN_ID");
1980Sstevel@tonic-gate 	}
1990Sstevel@tonic-gate 	return (SvPVX(*svp));
2000Sstevel@tonic-gate }
2010Sstevel@tonic-gate 
2020Sstevel@tonic-gate /*
2030Sstevel@tonic-gate  * Create a new ::Object by wrapping an ea_object_t in a perl SV.  This is used
2040Sstevel@tonic-gate  * to wrap exacct records that have been read from a file, or packed records
2050Sstevel@tonic-gate  * that have been inflated.
2060Sstevel@tonic-gate  */
2070Sstevel@tonic-gate SV *
new_xs_ea_object(ea_object_t * ea_obj)2080Sstevel@tonic-gate new_xs_ea_object(ea_object_t *ea_obj)
2090Sstevel@tonic-gate {
2100Sstevel@tonic-gate 	xs_ea_object_t	*xs_obj;
2110Sstevel@tonic-gate 	SV		*sv_obj;
2120Sstevel@tonic-gate 
2130Sstevel@tonic-gate 	/* Allocate space - use perl allocator. */
2140Sstevel@tonic-gate 	New(0, xs_obj, 1, xs_ea_object_t);
2150Sstevel@tonic-gate 	PERL_ASSERT(xs_obj != NULL);
2160Sstevel@tonic-gate 	xs_obj->ea_obj = ea_obj;
2170Sstevel@tonic-gate 	xs_obj->perl_obj = NULL;
2180Sstevel@tonic-gate 	sv_obj = NEWSV(0, 0);
2190Sstevel@tonic-gate 	PERL_ASSERT(sv_obj != NULL);
2200Sstevel@tonic-gate 
2210Sstevel@tonic-gate 	/*
2220Sstevel@tonic-gate 	 * Initialise according to the type of the passed exacct object,
2230Sstevel@tonic-gate 	 * and bless the perl object into the appropriate class.
2240Sstevel@tonic-gate 	 */
2250Sstevel@tonic-gate 	if (ea_obj->eo_type == EO_ITEM) {
2260Sstevel@tonic-gate 		if ((ea_obj->eo_catalog & EXT_TYPE_MASK) == EXT_EXACCT_OBJECT) {
2270Sstevel@tonic-gate 			INIT_EMBED_ITEM_FLAGS(xs_obj);
2280Sstevel@tonic-gate 		} else {
2290Sstevel@tonic-gate 			INIT_PLAIN_ITEM_FLAGS(xs_obj);
2300Sstevel@tonic-gate 		}
2310Sstevel@tonic-gate 		sv_setiv(newSVrv(sv_obj, NULL), PTR2IV(xs_obj));
2320Sstevel@tonic-gate 		sv_bless(sv_obj, Sun_Solaris_Exacct_Object_Item_stash);
2330Sstevel@tonic-gate 	} else {
2340Sstevel@tonic-gate 		INIT_GROUP_FLAGS(xs_obj);
2350Sstevel@tonic-gate 		sv_setiv(newSVrv(sv_obj, NULL), PTR2IV(xs_obj));
2360Sstevel@tonic-gate 		sv_bless(sv_obj, Sun_Solaris_Exacct_Object_Group_stash);
2370Sstevel@tonic-gate 	}
2380Sstevel@tonic-gate 
2390Sstevel@tonic-gate 	/*
2400Sstevel@tonic-gate 	 * We are passing back a pointer masquerading as a perl IV,
2410Sstevel@tonic-gate 	 * so make sure it can't be modified.
2420Sstevel@tonic-gate 	 */
2430Sstevel@tonic-gate 	SvREADONLY_on(SvRV(sv_obj));
2440Sstevel@tonic-gate 	return (sv_obj);
2450Sstevel@tonic-gate }
2460Sstevel@tonic-gate 
2470Sstevel@tonic-gate /*
2480Sstevel@tonic-gate  * Convert the perl form of an ::Object into the corresponding exacct form.
2490Sstevel@tonic-gate  * This is used prior to writing an ::Object to a file, or passing it to
2500Sstevel@tonic-gate  * putacct.  This is only required for embedded items and groups - for normal
2510Sstevel@tonic-gate  * items it is a no-op.
2520Sstevel@tonic-gate  */
2530Sstevel@tonic-gate ea_object_t *
deflate_xs_ea_object(SV * sv)2540Sstevel@tonic-gate deflate_xs_ea_object(SV *sv)
2550Sstevel@tonic-gate {
2560Sstevel@tonic-gate 	xs_ea_object_t	*xs_obj;
2570Sstevel@tonic-gate 	ea_object_t	*ea_obj;
2580Sstevel@tonic-gate 
2590Sstevel@tonic-gate 	/* Get the source xs_ea_object_t. */
2600Sstevel@tonic-gate 	PERL_ASSERT(sv != NULL);
2610Sstevel@tonic-gate 	sv = SvRV(sv);
2620Sstevel@tonic-gate 	PERL_ASSERT(sv != NULL);
2630Sstevel@tonic-gate 	xs_obj = INT2PTR(xs_ea_object_t *, SvIV(sv));
2640Sstevel@tonic-gate 	PERL_ASSERT(xs_obj != NULL);
2650Sstevel@tonic-gate 	ea_obj = xs_obj->ea_obj;
2660Sstevel@tonic-gate 	PERL_ASSERT(ea_obj != NULL);
2670Sstevel@tonic-gate 
2680Sstevel@tonic-gate 	/* Break any list this object is a part of. */
2690Sstevel@tonic-gate 	ea_obj->eo_next = NULL;
2700Sstevel@tonic-gate 
2710Sstevel@tonic-gate 	/* Deal with Items containing embedded Objects. */
2720Sstevel@tonic-gate 	if (IS_EMBED_ITEM(xs_obj)) {
2730Sstevel@tonic-gate 		xs_ea_object_t	*child_xs_obj;
2740Sstevel@tonic-gate 		SV		*perl_obj;
2750Sstevel@tonic-gate 		size_t		bufsz;
2760Sstevel@tonic-gate 
2770Sstevel@tonic-gate 		/* Get the underlying perl object an deflate that in turn. */
2780Sstevel@tonic-gate 		perl_obj = xs_obj->perl_obj;
2790Sstevel@tonic-gate 		PERL_ASSERT(perl_obj != NULL);
2800Sstevel@tonic-gate 		deflate_xs_ea_object(perl_obj);
2810Sstevel@tonic-gate 		perl_obj = SvRV(perl_obj);
2820Sstevel@tonic-gate 		PERL_ASSERT(perl_obj != NULL);
2830Sstevel@tonic-gate 		child_xs_obj = INT2PTR(xs_ea_object_t *, SvIV(perl_obj));
2840Sstevel@tonic-gate 		PERL_ASSERT(child_xs_obj->ea_obj != NULL);
2850Sstevel@tonic-gate 
2860Sstevel@tonic-gate 		/* Free any existing object contents. */
2870Sstevel@tonic-gate 		if (ea_obj->eo_item.ei_object != NULL) {
2880Sstevel@tonic-gate 			ea_free(ea_obj->eo_item.ei_object,
2890Sstevel@tonic-gate 			    ea_obj->eo_item.ei_size);
2900Sstevel@tonic-gate 			ea_obj->eo_item.ei_object = NULL;
2910Sstevel@tonic-gate 			ea_obj->eo_item.ei_size = 0;
2920Sstevel@tonic-gate 		}
2930Sstevel@tonic-gate 
2940Sstevel@tonic-gate 		/*  Pack the object. */
2950Sstevel@tonic-gate 		while (1) {
2960Sstevel@tonic-gate 			/* Use the last buffer size as a best guess. */
2970Sstevel@tonic-gate 			if (last_bufsz != 0) {
2980Sstevel@tonic-gate 				ea_obj->eo_item.ei_object =
2990Sstevel@tonic-gate 				    ea_alloc(last_bufsz);
3000Sstevel@tonic-gate 				PERL_ASSERT(ea_obj->eo_item.ei_object != NULL);
3010Sstevel@tonic-gate 			} else {
3020Sstevel@tonic-gate 				ea_obj->eo_item.ei_object = NULL;
3030Sstevel@tonic-gate 			}
3040Sstevel@tonic-gate 
3050Sstevel@tonic-gate 			/*
3060Sstevel@tonic-gate 			 * Pack the object.  If the buffer is too small,
3070Sstevel@tonic-gate 			 * we will go around again with the correct size.
3080Sstevel@tonic-gate 			 * If unsucessful, we will bail.
3090Sstevel@tonic-gate 			 */
3100Sstevel@tonic-gate 			if ((bufsz = ea_pack_object(child_xs_obj->ea_obj,
3110Sstevel@tonic-gate 			    ea_obj->eo_item.ei_object, last_bufsz)) == -1) {
3120Sstevel@tonic-gate 				ea_free(ea_obj->eo_item.ei_object, last_bufsz);
3130Sstevel@tonic-gate 				ea_obj->eo_item.ei_object = NULL;
3140Sstevel@tonic-gate 				return (NULL);
3150Sstevel@tonic-gate 			} else if (bufsz > last_bufsz) {
3160Sstevel@tonic-gate 				ea_free(ea_obj->eo_item.ei_object, last_bufsz);
3170Sstevel@tonic-gate 				last_bufsz = bufsz;
3180Sstevel@tonic-gate 				continue;
3190Sstevel@tonic-gate 			} else {
3200Sstevel@tonic-gate 				ea_obj->eo_item.ei_size = bufsz;
3210Sstevel@tonic-gate 				break;
3220Sstevel@tonic-gate 			}
3230Sstevel@tonic-gate 		}
3240Sstevel@tonic-gate 
3250Sstevel@tonic-gate 	/* Deal with Groups. */
3260Sstevel@tonic-gate 	} else if (IS_GROUP(xs_obj)) {
3270Sstevel@tonic-gate 		MAGIC		*mg;
3280Sstevel@tonic-gate 		AV		*av;
3290Sstevel@tonic-gate 		int		len, i;
3300Sstevel@tonic-gate 		xs_ea_object_t	*ary_xs;
3310Sstevel@tonic-gate 		ea_object_t	*ary_ea, *prev_ea;
3320Sstevel@tonic-gate 
3330Sstevel@tonic-gate 		/* Find the AV underlying the tie. */
3340Sstevel@tonic-gate 		mg = mg_find(SvRV(xs_obj->perl_obj), 'P');
3350Sstevel@tonic-gate 		PERL_ASSERT(mg != NULL);
3360Sstevel@tonic-gate 		av = (AV*)SvRV(mg->mg_obj);
3370Sstevel@tonic-gate 		PERL_ASSERT(av != NULL);
3380Sstevel@tonic-gate 
3390Sstevel@tonic-gate 		/*
3400Sstevel@tonic-gate 		 * Step along the AV, deflating each object and linking it into
3410Sstevel@tonic-gate 		 * the exacct group item list.
3420Sstevel@tonic-gate 		 */
3430Sstevel@tonic-gate 		prev_ea = ary_ea = NULL;
3440Sstevel@tonic-gate 		len = av_len(av) + 1;
3450Sstevel@tonic-gate 		ea_obj->eo_group.eg_nobjs = 0;
3460Sstevel@tonic-gate 		ea_obj->eo_group.eg_objs = NULL;
3470Sstevel@tonic-gate 		for (i = 0; i < len; i++) {
3480Sstevel@tonic-gate 			/*
3490Sstevel@tonic-gate 			 * Get the source xs_ea_object_t.  If the current slot
3500Sstevel@tonic-gate 			 * in the array is empty, skip it.
3510Sstevel@tonic-gate 			 */
3520Sstevel@tonic-gate 			SV	**ary_svp;
3530Sstevel@tonic-gate 			if ((ary_svp = av_fetch(av, i, FALSE)) == NULL) {
3540Sstevel@tonic-gate 				continue;
3550Sstevel@tonic-gate 			}
3560Sstevel@tonic-gate 			PERL_ASSERT(*ary_svp != NULL);
3570Sstevel@tonic-gate 
3580Sstevel@tonic-gate 			/* Deflate it. */
3590Sstevel@tonic-gate 			ary_ea = deflate_xs_ea_object(*ary_svp);
3600Sstevel@tonic-gate 			PERL_ASSERT(ary_ea != NULL);
3610Sstevel@tonic-gate 
3620Sstevel@tonic-gate 			/* Link into the list. */
3630Sstevel@tonic-gate 			ary_ea->eo_next = NULL;
3640Sstevel@tonic-gate 			if (ea_obj->eo_group.eg_objs == NULL) {
3650Sstevel@tonic-gate 				ea_obj->eo_group.eg_objs = ary_ea;
3660Sstevel@tonic-gate 			}
3670Sstevel@tonic-gate 			ea_obj->eo_group.eg_nobjs++;
3680Sstevel@tonic-gate 			if (prev_ea != NULL) {
3690Sstevel@tonic-gate 				prev_ea->eo_next = ary_ea;
3700Sstevel@tonic-gate 			}
3710Sstevel@tonic-gate 			prev_ea = ary_ea;
3720Sstevel@tonic-gate 		}
3730Sstevel@tonic-gate 	}
3740Sstevel@tonic-gate 	return (ea_obj);
3750Sstevel@tonic-gate }
3760Sstevel@tonic-gate 
3770Sstevel@tonic-gate /*
3780Sstevel@tonic-gate  * Private Sun::Solaris::Exacct utility code.
3790Sstevel@tonic-gate  */
3800Sstevel@tonic-gate 
3810Sstevel@tonic-gate /*
3820Sstevel@tonic-gate  * Return a string representation of an ea_error.
3830Sstevel@tonic-gate  */
3840Sstevel@tonic-gate static const char *
error_str(int eno)3850Sstevel@tonic-gate error_str(int eno)
3860Sstevel@tonic-gate {
3870Sstevel@tonic-gate 	switch (eno) {
3880Sstevel@tonic-gate 	case EXR_OK:
3890Sstevel@tonic-gate 		return ("no error");
3900Sstevel@tonic-gate 	case EXR_SYSCALL_FAIL:
3910Sstevel@tonic-gate 		return ("system call failed");
3920Sstevel@tonic-gate 	case EXR_CORRUPT_FILE:
3930Sstevel@tonic-gate 		return ("corrupt file");
3940Sstevel@tonic-gate 	case EXR_EOF:
3950Sstevel@tonic-gate 		return ("end of file");
3960Sstevel@tonic-gate 	case EXR_NO_CREATOR:
3970Sstevel@tonic-gate 		return ("no creator");
3980Sstevel@tonic-gate 	case EXR_INVALID_BUF:
3990Sstevel@tonic-gate 		return ("invalid buffer");
4000Sstevel@tonic-gate 	case EXR_NOTSUPP:
4010Sstevel@tonic-gate 		return ("not supported");
4020Sstevel@tonic-gate 	case EXR_UNKN_VERSION:
4030Sstevel@tonic-gate 		return ("unknown version");
4040Sstevel@tonic-gate 	case EXR_INVALID_OBJ:
4050Sstevel@tonic-gate 		return ("invalid object");
4060Sstevel@tonic-gate 	default:
4070Sstevel@tonic-gate 		return ("unknown error");
4080Sstevel@tonic-gate 	}
4090Sstevel@tonic-gate }
4100Sstevel@tonic-gate 
4110Sstevel@tonic-gate /*
4120Sstevel@tonic-gate  * The XS code exported to perl is below here.  Note that the XS preprocessor
4130Sstevel@tonic-gate  * has its own commenting syntax, so all comments from this point on are in
4140Sstevel@tonic-gate  * that form.
4150Sstevel@tonic-gate  */
4160Sstevel@tonic-gate 
4170Sstevel@tonic-gate MODULE = Sun::Solaris::Exacct PACKAGE = Sun::Solaris::Exacct
4180Sstevel@tonic-gate PROTOTYPES: ENABLE
4190Sstevel@tonic-gate 
4200Sstevel@tonic-gate  #
4210Sstevel@tonic-gate  # Define the stash pointers if required and create and populate @_Constants.
4220Sstevel@tonic-gate  #
4230Sstevel@tonic-gate BOOT:
4240Sstevel@tonic-gate 	init_stashes();
4250Sstevel@tonic-gate 	define_constants(PKGBASE, constants);
4260Sstevel@tonic-gate 
4270Sstevel@tonic-gate  #
4280Sstevel@tonic-gate  # Return the last exacct error as a dual-typed SV.  In a numeric context the
4290Sstevel@tonic-gate  # SV will evaluate to the value of an EXR_* constant, in string context to a
4300Sstevel@tonic-gate  # error message.
4310Sstevel@tonic-gate  #
4320Sstevel@tonic-gate SV*
4330Sstevel@tonic-gate ea_error()
4340Sstevel@tonic-gate PREINIT:
4350Sstevel@tonic-gate 	int		eno;
4360Sstevel@tonic-gate 	const char	*msg;
4370Sstevel@tonic-gate CODE:
4380Sstevel@tonic-gate 	eno = ea_error();
4390Sstevel@tonic-gate 	msg = error_str(eno);
4400Sstevel@tonic-gate 	RETVAL = newSViv(eno);
4410Sstevel@tonic-gate 	sv_setpv(RETVAL, (char*) msg);
4420Sstevel@tonic-gate 	SvIOK_on(RETVAL);
4430Sstevel@tonic-gate OUTPUT:
4440Sstevel@tonic-gate 	RETVAL
4450Sstevel@tonic-gate 
4460Sstevel@tonic-gate  #
4470Sstevel@tonic-gate  # Return a string describing the last error to be encountered.  If the value
4480Sstevel@tonic-gate  # returned by ea_error is EXR_SYSCALL_FAIL, a string describing the value of
4490Sstevel@tonic-gate  # errno will be returned.  For all other values returned by ea_error() a string
4500Sstevel@tonic-gate  # describing the exacct error will be returned.
4510Sstevel@tonic-gate  #
4520Sstevel@tonic-gate char*
4530Sstevel@tonic-gate ea_error_str()
4540Sstevel@tonic-gate PREINIT:
4550Sstevel@tonic-gate 	int	eno;
4560Sstevel@tonic-gate CODE:
4570Sstevel@tonic-gate 	eno = ea_error();
4580Sstevel@tonic-gate 	if (eno == EXR_SYSCALL_FAIL) {
4590Sstevel@tonic-gate 		RETVAL = strerror(errno);
4600Sstevel@tonic-gate 		if (RETVAL == NULL) {
4610Sstevel@tonic-gate 			RETVAL = "unknown system error";
4620Sstevel@tonic-gate 		}
4630Sstevel@tonic-gate 	} else {
4640Sstevel@tonic-gate 		RETVAL = (char*) error_str(eno);
4650Sstevel@tonic-gate 	}
4660Sstevel@tonic-gate OUTPUT:
4670Sstevel@tonic-gate 	RETVAL
4680Sstevel@tonic-gate 
4690Sstevel@tonic-gate  #
4700Sstevel@tonic-gate  # Return an accounting record for the specified task or process. idtype is
4710Sstevel@tonic-gate  # either P_TASKID or P_PID and id is a process or task id.
4720Sstevel@tonic-gate  #
4730Sstevel@tonic-gate SV*
4740Sstevel@tonic-gate getacct(idtype, id)
4750Sstevel@tonic-gate 	idtype_t	idtype;
4760Sstevel@tonic-gate 	id_t		id;
4770Sstevel@tonic-gate PREINIT:
4780Sstevel@tonic-gate 	int		bufsz;
4790Sstevel@tonic-gate 	char		*buf;
4800Sstevel@tonic-gate 	ea_object_t	*ea_obj;
4810Sstevel@tonic-gate CODE:
4820Sstevel@tonic-gate 	/* Get the required accounting buffer. */
4830Sstevel@tonic-gate 	while (1) {
4840Sstevel@tonic-gate 		/* Use the last buffer size as a best guess. */
4850Sstevel@tonic-gate 		if (last_bufsz != 0) {
4860Sstevel@tonic-gate 			buf = ea_alloc(last_bufsz);
4870Sstevel@tonic-gate 			PERL_ASSERT(buf != NULL);
4880Sstevel@tonic-gate 		} else {
4890Sstevel@tonic-gate 			buf = NULL;
4900Sstevel@tonic-gate 		}
4910Sstevel@tonic-gate 
4920Sstevel@tonic-gate 		/*
4930Sstevel@tonic-gate 		 * get the accounting record.  If the buffer is too small,
4940Sstevel@tonic-gate 		 * we will go around again with the correct size.
4950Sstevel@tonic-gate 		 * If unsucessful, we will bail.
4960Sstevel@tonic-gate 		 */
4970Sstevel@tonic-gate 		if ((bufsz = getacct(idtype, id, buf, last_bufsz)) == -1) {
4980Sstevel@tonic-gate 			if (last_bufsz != 0) {
4990Sstevel@tonic-gate 				ea_free(buf, last_bufsz);
5000Sstevel@tonic-gate 			}
5010Sstevel@tonic-gate 			XSRETURN_UNDEF;
5020Sstevel@tonic-gate 		} else if (bufsz > last_bufsz) {
5030Sstevel@tonic-gate 			ea_free(buf, last_bufsz);
5040Sstevel@tonic-gate 			last_bufsz = bufsz;
5050Sstevel@tonic-gate 			continue;
5060Sstevel@tonic-gate 		} else {
5070Sstevel@tonic-gate 			break;
5080Sstevel@tonic-gate 		}
5090Sstevel@tonic-gate 	}
5100Sstevel@tonic-gate 
5110Sstevel@tonic-gate 	/* Unpack the buffer. */
5120Sstevel@tonic-gate 	if (ea_unpack_object(&ea_obj, EUP_ALLOC, buf, bufsz) == -1) {
5130Sstevel@tonic-gate 		ea_free(buf, last_bufsz);
5140Sstevel@tonic-gate 		XSRETURN_UNDEF;
5150Sstevel@tonic-gate 	}
5160Sstevel@tonic-gate 	ea_free(buf, last_bufsz);
5170Sstevel@tonic-gate 	RETVAL = new_xs_ea_object(ea_obj);
5180Sstevel@tonic-gate OUTPUT:
5190Sstevel@tonic-gate 	RETVAL
5200Sstevel@tonic-gate 
5210Sstevel@tonic-gate  #
5220Sstevel@tonic-gate  # Write an accounting record into the system accounting file. idtype is
5230Sstevel@tonic-gate  # either P_TASKID or P_PID and id is a process or task id.  value may be either
5240Sstevel@tonic-gate  # an ::Exacct::Object, in which case it will be packed and inserted in the
5250Sstevel@tonic-gate  # file, or a SV which will be converted to a string and inserted into the file.
5260Sstevel@tonic-gate  #
5270Sstevel@tonic-gate SV*
5280Sstevel@tonic-gate putacct(idtype, id, value)
5290Sstevel@tonic-gate 	idtype_t	idtype;
5300Sstevel@tonic-gate 	id_t		id;
5310Sstevel@tonic-gate 	SV		*value;
5320Sstevel@tonic-gate PREINIT:
5330Sstevel@tonic-gate 	HV		*stash;
5340Sstevel@tonic-gate 	unsigned int	bufsz;
5350Sstevel@tonic-gate 	int		flags, ret;
5360Sstevel@tonic-gate 	char		*buf;
5370Sstevel@tonic-gate CODE:
5380Sstevel@tonic-gate 	/* If it is an ::Object::Item or ::Object::Group, pack it. */
5390Sstevel@tonic-gate 	stash = SvROK(value) ? SvSTASH(SvRV(value)) : NULL;
5400Sstevel@tonic-gate 	if (stash == Sun_Solaris_Exacct_Object_Item_stash ||
5410Sstevel@tonic-gate 	    stash == Sun_Solaris_Exacct_Object_Group_stash) {
5420Sstevel@tonic-gate 		ea_object_t	*obj;
5430Sstevel@tonic-gate 
5440Sstevel@tonic-gate 		/* Deflate the object. */
5450Sstevel@tonic-gate 		if ((obj = deflate_xs_ea_object(value)) == NULL) {
5460Sstevel@tonic-gate 			XSRETURN_NO;
5470Sstevel@tonic-gate 		}
5480Sstevel@tonic-gate 
5490Sstevel@tonic-gate 		/*  Pack the object. */
5500Sstevel@tonic-gate 		while (1) {
5510Sstevel@tonic-gate 			/* Use the last buffer size as a best guess. */
5520Sstevel@tonic-gate 			if (last_bufsz != 0) {
5530Sstevel@tonic-gate 				buf = ea_alloc(last_bufsz);
5540Sstevel@tonic-gate 				PERL_ASSERT(buf != NULL);
5550Sstevel@tonic-gate 			} else {
5560Sstevel@tonic-gate 				buf = NULL;
5570Sstevel@tonic-gate 			}
5580Sstevel@tonic-gate 
5590Sstevel@tonic-gate 			/*
5600Sstevel@tonic-gate 			 * Pack the object.  If the buffer is too small, we
5610Sstevel@tonic-gate 			 * will go around again with the correct size.
5620Sstevel@tonic-gate 			 * If unsucessful, we will bail.
5630Sstevel@tonic-gate 			 */
5640Sstevel@tonic-gate 			if ((bufsz = ea_pack_object(obj, buf, last_bufsz))
5650Sstevel@tonic-gate 			    == -1) {
5660Sstevel@tonic-gate 				if (last_bufsz != 0) {
5670Sstevel@tonic-gate 					ea_free(buf, last_bufsz);
5680Sstevel@tonic-gate 				}
5690Sstevel@tonic-gate 				XSRETURN_NO;
5700Sstevel@tonic-gate 			} else if (bufsz > last_bufsz) {
5710Sstevel@tonic-gate 				ea_free(buf, last_bufsz);
5720Sstevel@tonic-gate 				last_bufsz = bufsz;
5730Sstevel@tonic-gate 				continue;
5740Sstevel@tonic-gate 			} else {
5750Sstevel@tonic-gate 				break;
5760Sstevel@tonic-gate 			}
5770Sstevel@tonic-gate 		}
5780Sstevel@tonic-gate 		flags = EP_EXACCT_OBJECT;
5790Sstevel@tonic-gate 
5800Sstevel@tonic-gate 	/* Otherwise treat it as normal SV - convert to a string. */
5810Sstevel@tonic-gate 	} else {
5820Sstevel@tonic-gate 		buf = SvPV(value, bufsz);
5830Sstevel@tonic-gate 		flags = EP_RAW;
5840Sstevel@tonic-gate 	}
5850Sstevel@tonic-gate 
5860Sstevel@tonic-gate 	/* Call putacct to write the buffer */
5870Sstevel@tonic-gate 	RETVAL = putacct(idtype, id, buf, bufsz, flags) == 0
5880Sstevel@tonic-gate 	    ? &PL_sv_yes : &PL_sv_no;
5890Sstevel@tonic-gate 
5900Sstevel@tonic-gate 	/*  Clean up if we allocated a buffer. */
5910Sstevel@tonic-gate 	if (flags == EP_EXACCT_OBJECT) {
5920Sstevel@tonic-gate 		ea_free(buf, last_bufsz);
5930Sstevel@tonic-gate 	}
5940Sstevel@tonic-gate OUTPUT:
5950Sstevel@tonic-gate 	RETVAL
5960Sstevel@tonic-gate 
5970Sstevel@tonic-gate  #
5980Sstevel@tonic-gate  # Write an accounting record for the specified task or process.  idtype is
5990Sstevel@tonic-gate  # either P_TASKID or P_PID, id is a process or task id and flags is either
6000Sstevel@tonic-gate  # EW_PARTIAL or EW_INTERVAL.
6010Sstevel@tonic-gate  #
6020Sstevel@tonic-gate int
6030Sstevel@tonic-gate wracct(idtype, id, flags)
6040Sstevel@tonic-gate 	idtype_t	idtype;
6050Sstevel@tonic-gate 	id_t		id;
6060Sstevel@tonic-gate 	int		flags;
607