/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/systeminfo.h>
#include <exacct.h>
#include <exacct_impl.h>
#include <sys/exacct_impl.h>
#include <fcntl.h>
#include <unistd.h>
#include <strings.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <thread.h>
#include <pthread.h>
static int exacct_errval = 0;
/*
* extended accounting file access routines
*
* exacct_ops.c implements the library-specific routines of libexacct: the
* operations associated with file access and record traversal. (The
* complementary routines which permit hierarchy building and record packing
* are provided in exacct_core.c, which is used by both libexacct and the
* kernel.) At its heart are the unpack, get, and next routines, which
* navigate the packed records produced by ea_pack_object.
*/
/*
* Group stack manipulation code. As groups can be nested, we need a mechanism
* for saving and restoring the current position within the outer groups. This
* state stack is stored within the ea_file_impl_t structure, in the ef_depth,
* ef_ndeep and ef_mxdeep members. On error all these functions set
* exacct_error and return -1.
*/
/*
* If the stack is NULL, create and initialise it.
* If is is not NULL, check it still has space - if not, double its size.
*/
{
if ((f->ef_depth =
== NULL) {
/* exacct_errno set above. */
return (-1);
}
f->ef_mxdeep = DEFAULT_ENTRIES;
f->ef_ndeep = -1;
if ((newstack =
== NULL) {
/* exacct_errno set above. */
return (-1);
}
f->ef_mxdeep * sizeof (ea_file_depth_t));
f->ef_mxdeep * sizeof (ea_file_depth_t));
f->ef_mxdeep *= 2;
}
return (0);
}
/*
* Free a stack.
*/
{
}
f->ef_mxdeep = 0;
f->ef_ndeep = -1;
}
/*
* Add a new group onto the stack, pushing down one frame. nobj is the number
* of items in the group. We have to read this many objects before popping
* back up to an enclosing group - see next_object() and previous_object()
* below.
*/
{
if (stack_check(f) != 0) {
stack_free(f);
/* exacct_errno set above. */
return (-1);
}
f->ef_ndeep++;
return (0);
}
/*
* Step forwards along the objects within the current group. If we are still
* within a group, return 1. If we have reached the end of the current group,
* unwind the stack back up to the nearest enclosing group that still has
* unprocessed objects and return 0. On EOF or error, set exacct_error
* accordingly and return -1. xread() is required so that this function can
* work either on files or memory buffers.
*/
static int
ea_file_impl_t *f,
{
/*
* If the stack is empty we are not in a group, so there will be no
* stack manipulation to do and no large backskips to step over.
*/
if (f->ef_ndeep < 0) {
return (0);
}
/*
* Otherwise we must be in a group. If there are objects left in the
* group, move onto the next one in the group and return.
*/
return (1);
/*
* If we are at the end of a group we need to move backwards up the
* stack, consuming the large backskips as we go, until we find a group
* that still contains unprocessed items, or until we have unwound back
* off the bottom of the stack (i.e. out of all the groups).
*/
} else {
while (f->ef_ndeep >= 0 &&
/* Read the large backskip. */
f->ef_ndeep--;
sizeof (scratch32)) {
return (-1);
}
}
return (0);
}
}
/*
* Step backwards along the objects within the current group. If we are still
* within a group, return 1. If we have reached the end of the current group,
* unwind the stack back up to the enclosing group and return 0.
*/
{
/*
* If the stack is empty we are not in a group, so there will be no
* stack manipulation to do.
*/
if (f->ef_ndeep < 0) {
return (0);
}
/*
* Otherwise we must be in a group. If there are objects left in the
* group, move onto the previous one in the group and return.
*/
return (1);
/* Otherwise, step one level back up the group stack. */
} else {
f->ef_ndeep--;
return (0);
}
}
/*
* them to be used on either a file handle or a memory buffer.
*/
static size_t
{
}
return (retval);
}
static size_t
{
if (f->ef_bufsize == 0 && sz != 0)
return ((size_t)0);
if (f->ef_bufsize < sz)
sz = f->ef_bufsize;
f->ef_bufsize -= sz;
return (sz);
}
static off_t
{
}
static off_t
{
if (f->ef_bufsize == 0 && adv != 0)
return (-1);
if (f->ef_bufsize < adv)
adv = f->ef_bufsize;
f->ef_bufsize -= adv;
return (0);
}
/*ARGSUSED*/
static void *
{
return (NULL);
}
static void *
{
return (f->ef_buf);
}
/*
* Public API
*/
void
{
if (thr_main()) {
return;
}
(void) thr_keycreate_once(&errkey, 0);
}
int
ea_error(void)
{
if (thr_main())
return (exacct_errval);
if (errkey == THR_ONCE_KEY)
return (EXR_OK);
}
/*
* ea_next_object(), ea_previous_object(), and ea_get_object() are written such
* that the file cursor is always located on an object boundary.
*/
{
/*
* If ef_advance is zero, then we are executing after a get or previous
* operation and do not move to the next or previous object. Otherwise,
* advance to the next available item. Note that ef_advance does NOT
* include the large backskip at the end of a object, this being dealt
* with by the depth stack handling in stack_next_object.
*/
if (f->ef_advance != 0) {
return (EO_ERROR);
}
/* exacct_error set above. */
return (EO_ERROR);
}
}
f->ef_advance = 0;
/* Read the catalog tag */
if (ret == 0) {
return (EO_ERROR);
} else if (ret < sizeof (ea_catalog_t)) {
return (EO_ERROR);
}
backup = sizeof (ea_catalog_t);
/* Figure out the offset to just before the large backskip. */
case EXT_GROUP:
f->ef_advance = sizeof (uint32_t);
/* FALLTHROUGH */
case EXT_STRING:
case EXT_EXACCT_OBJECT:
case EXT_RAW:
< sizeof (ea_size_t)) {
return (EO_ERROR);
}
/* Note: len already includes the size of the backskip. */
f->ef_advance += sizeof (ea_catalog_t) +
break;
case EXT_UINT8:
sizeof (uint32_t);
break;
case EXT_UINT16:
sizeof (uint32_t);
break;
case EXT_UINT32:
sizeof (uint32_t);
break;
case EXT_UINT64:
sizeof (uint32_t);
break;
case EXT_DOUBLE:
f->ef_advance = sizeof (ea_catalog_t) + sizeof (double) +
sizeof (uint32_t);
break;
default:
return (EO_ERROR);
}
/* Reposition to the start of this object. */
f->ef_advance = 0;
return (EO_ERROR);
}
}
{
int r;
} else {
}
return (EO_ERROR);
}
sizeof (uint32_t)) {
if (r == 0) {
} else {
}
return (EO_ERROR);
}
/*
* A backskip of 0 means that the current record can't be skipped over.
* This will be true for the header record, and for records longer than
* 2^32.
*/
if (bkskip == 0) {
return (EO_ERROR);
}
(void) stack_previous_object(f);
/*
* If we attempted to seek past BOF, then the file was
* corrupt, as we can only trust the backskip we read.
*/
} else {
}
return (EO_ERROR);
}
f->ef_advance = 0;
}
/*
* xget_object() contains the logic for extracting an individual object from a
* packed buffer, which it consumes using xread() and xseek() operations
* provided by the caller. flags may be set to either EUP_ALLOC, in which case
* new memory is allocated for the variable length items unpacked, or
* EUP_NOALLOC, in which case item data pointer indicate locations within the
* buffer, using the provided xpos() function. EUP_NOALLOC is generally not
* useful for callers representing interaction with actual file streams, and
* should not be specified thereby.
*/
static ea_object_type_t
ea_file_impl_t *f,
void *(*xpos)(ea_file_impl_t *),
int flags)
{
void *buf;
size_t r;
/* Read the catalog tag. */
return (EO_ERROR);
} else if (r != sizeof (ea_catalog_t)) {
return (EO_ERROR);
}
/*
* If this is a record group, we treat it separately: only record
* groups cause us to allocate new depth frames.
*/
/* Read size field, and number of objects. */
return (EO_ERROR);
}
exacct_order64(&sz);
sizeof (uint32_t)) {
return (EO_ERROR);
}
/* Now read the group's small backskip. */
sizeof (uint32_t)) {
return (EO_ERROR);
}
/* Push a new depth stack frame. */
/* exacct_error set above */
return (EO_ERROR);
}
/*
* If the group has no items, we now need to position to the
* end of the group, because there will be no subsequent calls
* to process the group, it being empty.
*/
/* exacct_error set above. */
return (EO_ERROR);
}
}
f->ef_advance = 0;
}
/*
* Otherwise we are reading an item.
*/
case EXT_STRING:
case EXT_EXACCT_OBJECT:
case EXT_RAW:
return (EO_ERROR);
}
exacct_order64(&sz);
/*
* Subtract backskip value from size.
*/
return (EO_ERROR);
}
} else {
/* exacct_error set above. */
return (EO_ERROR);
return (EO_ERROR);
}
}
/*
* Maintain our consistent convention that string lengths
* include the terminating NULL character.
*/
break;
case EXT_UINT8:
sizeof (uint8_t)) {
return (EO_ERROR);
}
break;
case EXT_UINT16:
sizeof (uint16_t)) {
return (EO_ERROR);
}
break;
case EXT_UINT32:
sizeof (uint32_t)) {
return (EO_ERROR);
}
break;
case EXT_UINT64:
sizeof (uint64_t)) {
return (EO_ERROR);
}
break;
case EXT_DOUBLE:
sizeof (double)) {
return (EO_ERROR);
}
break;
default:
/*
* We've encountered an unknown type value. Flag the error and
* exit.
*/
return (EO_ERROR);
}
/*
* Advance over current large backskip value,
* and position at the start of the next object.
*/
return (EO_ERROR);
}
/* exacct_error set above. */
return (EO_ERROR);
}
f->ef_advance = 0;
}
{
}
/*
* unpack_group() recursively unpacks record groups from the buffer tucked
* within the passed ea_file, and attaches them to grp.
*/
static int
{
int i;
/*
* Set the group's object count to zero, as we will rebuild it via the
* individual object attachments.
*/
for (i = 0; i < nobjs; i++) {
/* exacct_errno set above. */
return (-1);
}
/* exacct_errno set above. */
return (-1);
}
/* exacct_errno set above. */
return (-1);
}
}
return (-1);
}
return (0);
}
/*
* ea_unpack_object() can be considered as a finite series of get operations on
* a given buffer, that rebuilds the hierarchy of objects compacted by a pack
* operation. Because there is complex state associated with the group depth,
* ea_unpack_object() must complete as one operation on a given buffer.
*/
{
return (EO_ERROR);
}
/* Set up the structures needed for unpacking */
/* exacct_errno set above. */
return (EO_ERROR);
}
/* Unpack the first object in the buffer - this should succeed. */
stack_free(&fake);
/* exacct_errno set above. */
return (EO_ERROR);
}
stack_free(&fake);
/* exacct_errno set above. */
return (EO_ERROR);
}
stack_free(&fake);
/* exacct_errno set above. */
return (EO_ERROR);
}
/*
* There may be other objects in the buffer - if so, chain them onto
* the end of the list. We have reached the end of the list when
* xget_object() returns -1 with exacct_error set to EXR_EOF.
*/
for (;;) {
stack_free(&fake);
/* exacct_errno set above. */
return (EO_ERROR);
}
stack_free(&fake);
return (first_obj_type);
} else {
/* exacct_error set above. */
return (EO_ERROR);
}
}
stack_free(&fake);
/* exacct_errno set above. */
return (EO_ERROR);
}
}
}
int
{
void *buf;
/*
* If we weren't opened for writing, this call fails.
*/
return (-1);
}
/* Pack with a null buffer to get the size. */
/* exacct_error set above. */
return (-1);
}
/* exacct_error set above. */
return (-1);
}
return (-1);
}
return (0);
}
/*
* validate_header() must be kept in sync with write_header(), given below, and
*/
static int
{
int saw_creator = 0;
int saw_version = 0;
int saw_type = 0;
int saw_hostname = 0;
int n;
goto error_case;
}
if (hdr_grp.eo_catalog !=
goto error_case;
}
goto error_case;
}
switch (scratch_obj.eo_catalog) {
goto error_case;
}
saw_version++;
break;
EXACCT_HDR_STR) != 0) {
goto error_case;
}
saw_type++;
break;
f->ef_creator =
if (f->ef_creator == NULL) {
goto error_case;
}
saw_creator++;
break;
/* The hostname is an optional field. */
f->ef_hostname =
if (f->ef_hostname == NULL) {
goto error_case;
}
saw_hostname++;
break;
default:
/* ignore unrecognized header members */
break;
}
}
goto error_case;
}
return (0);
}
if (saw_hostname)
ea_strfree(f->ef_hostname);
if (saw_creator)
ea_strfree(f->ef_creator);
return (-1);
}
static int
{
void *buf;
(void *)&version, 0) == -1 ||
goto cleanup1;
}
(void) ea_set_group(&hdr_grp,
/* Get the required size by passing a null buffer. */
goto cleanup1;
}
goto cleanup2;
}
/*
* To prevent reading the header when reading the file backwards,
* set the large backskip of the header group to 0 (last 4 bytes).
*/
bskip = 0;
sizeof (bskip));
goto cleanup2;
}
}
const char *
{
}
const char *
{
}
int
{
bzero(f, sizeof (*f));
/* Initialize depth stack. */
if (stack_check(f) == -1) {
/* exacct_error set above. */
goto error1;
}
/*
* 1. If we are O_CREAT, then we will need to write a header
* after opening name.
*/
goto error2;
}
/* exacct_error set above. */
goto error2;
}
goto error3;
}
/* exacct_error set above. */
goto error3;
}
/*
* 2. If we are not O_CREAT, but are RDWR or WRONLY, we need to
* seek to EOF so that appends will succeed.
*/
goto error2;
}
/* exacct_error set above. */
goto error2;
}
}
goto error2;
}
/*
* 3. This is an undefined manner for opening an exacct file.
*/
goto error2;
/*
* 4a. If we are RDONLY, then we are in a position such that
* either a ea_get_object or an ea_next_object will succeed. If
* aflags was set to EO_TAIL, seek to the end of the file.
*/
} else {
goto error2;
}
/* exacct_error set above. */
goto error2;
}
}
/*
* 4b. Handle the "open at end" option, for consumers who want
* to go backwards through the file (i.e. lastcomm).
*/
goto error2;
}
}
}
return (0);
/* Error cleanup code */
ea_strfree(f->ef_creator);
stack_free(f);
bzero(f, sizeof (*f));
return (-1);
}
int
{
int fd;
/*
* If overwriting an existing file, make sure to truncate it
* to prevent the file being created corrupt.
*/
return (-1);
}
return (-1);
}
return (0);
}
/*
* ea_close() performs all appropriate close operations on the open exacct file,
* including releasing any memory allocated while parsing the file.
*/
int
{
if (f->ef_creator != NULL)
ea_strfree(f->ef_creator);
if (f->ef_hostname != NULL)
ea_strfree(f->ef_hostname);
return (-1);
}
return (0);
}
/*
* Empty the input buffer and clear any underlying EOF or error bits set on the
* underlying FILE. This can be used by any library clients who wish to handle
* files that are in motion or who wish to seek the underlying file descriptor.
*/
void
{
}
/*
* Copy an ea_object_t. Note that in the case of a group, just the group
* object will be copied, and not its list of members. To recursively copy
* a group or a list of items use ea_copy_tree().
*/
{
/* Allocate a new object and copy to it. */
return (NULL);
}
case EO_GROUP:
break;
case EO_ITEM:
/* Items containing pointers need special treatment. */
case EXT_STRING:
return (NULL);
}
}
break;
case EXT_RAW:
return (NULL);
}
}
break;
case EXT_EXACCT_OBJECT:
return (NULL);
}
}
break;
default:
/* Other item types require no special handling. */
break;
}
break;
default:
return (NULL);
}
return (dst);
}
/*
* Recursively copy a list of ea_object_t. All the elements in the eo_next
* list will be copied, and any group objects will be recursively copied.
*/
{
/* Allocate a new object and copy to it. */
return (NULL);
}
/* Groups need the object list copying. */
return (NULL);
}
}
/* Remember the list head the first time round. */
}
/* Link together if not at the list head. */
}
}
return (ret_obj);
}
/*
* Read in the specified number of objects, returning the same data
* structure that would have originally been passed to ea_write().
*/
{
while (nobj--) {
/* Allocate space for the new object. */
/* Read it in. */
}
return (NULL);
}
/* Link it into the list. */
}
}
/* Recurse if the object is a group with contents. */
/* exacct_error set above. */
return (NULL);
}
}
}
return (first_obj);
}