root/usr/src/cmd/fs.d/nfs/nfslog/nfslog_elf.c
/*
 * 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
 * or http://www.opensolaris.org/os/licensing.
 * 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 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * nfs log - read buffer file and print structs in user-readable form
 */

#define _REENTRANT

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <strings.h>
#include <time.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/utsname.h>
#include <errno.h>
#include <time.h>
#include <limits.h>
#include <libintl.h>
#include <pwd.h>
#include <netdb.h>
#include <syslog.h>
#include <rpc/rpc.h>
#include <netconfig.h>
#include <netdir.h>
#include <nfs/nfs_sec.h>
#include <nfs/export.h>
#include <rpc/auth.h>
#include <rpc/svc.h>
#include <rpc/xdr.h>
#include <rpc/clnt.h>
#include <nfs/nfs.h>
#include <nfs/nfs_log.h>
#include "fhtab.h"
#include "nfslogd.h"

static char     empty_name[4] = "-";

static char ftype3_names[NF3FIFO + 1][20] = {
        "\"none\"", "\"file\"", "\"dir\"", "\"blk device\"",
        "\"chr device\"", "\"link\"", "\"socket\"", "\"fifo\""
};

#define NFSL_FTYPE3(ftype)                                              \
        ((((ftype) >= 0) && ((ftype) <= NF3FIFO)) ?                     \
        ftype3_names[ftype] : empty_name)

static char createmode3_names[EXCLUSIVE + 1][20] = {
        "\"unchecked", "\"guarded\"", "\"exclusive\""
};

#define NFSL_CREATEMODE3(createmode)                                    \
        ((((createmode) >= 0) && ((createmode) <= EXCLUSIVE)) ?         \
        createmode3_names[createmode] : empty_name)

static char     auth_flavor_name[RPCSEC_GSS + 1][20] = {
        "\"auth_null\"", "\"auth_unix\"", "\"auth_short\"", "\"auth_des\"",
        "\"auth_kerb\"", "\"none\"", "\"rpcsec_gss\""
};

#define NFSL_AUTH_FLAVOR_PRINT(auth_flavor)                             \
        (((auth_flavor) <= RPCSEC_GSS) ?                                \
        auth_flavor_name[auth_flavor] : empty_name)

#define NFSL_ERR_CNT            31      /* Actual err numbers */

/*
 * Two arrays - one short ints containing err codes, the other the strings
 * (merged codes for both v2 and v3
 */
static char     nfsl_status_name[NFSL_ERR_CNT][30] = {
        "\"ok\"", "\"perm\"", "\"noent\"", "\"io\"",
        "\"nxio\"", "\"access\"", "\"exist\"", "\"xdev\"",
        "\"nodev\"", "\"notdir\"", "\"isdir\"", "\"inval\"",
        "\"fbig\"", "\"nospc\"", "\"rofs\"", "\"mlink\"",
        "\"notsupp\"", "\"nametoolong\"", "\"notempty\"", "\"dquot\"",
        "\"stale\"", "\"remote\"", "\"wflush\"", "\"badhandle\"",
        "\"not_sync\"", "\"bad_cookie\"", "\"notsupp\"", "\"toosmall\"",
        "\"serverfault\"", "\"badtype\"", "\"jukebox\"",
};

static short    nfsl_status[NFSL_ERR_CNT] = {
        0, 1, 2, 5, 6, 13, 17, 18,
        19, 20, 21, 22, 27, 28, 30, 31,
        45, 63, 66, 69, 70, 71, 99, 10001,
        10002, 10003, 10004, 10005, 10006, 10007, 10008
};

/* list of open elf files */
static struct nfsl_log_file     *elf_file_list = NULL;

/* Imported functions */
extern void bcopy(const void *s1, void *s2, size_t n);

/* Static functions */
static void nfsl_log_file_free(struct nfsl_log_file *elfrec);
static void nfsl_log_file_add(struct nfsl_log_file *elfrec,
        struct nfsl_log_file **elf_listp);
static struct nfsl_log_file *nfsl_log_file_find(struct nfsl_log_file *elfrec,
        struct nfsl_log_file *elf_list);
static struct nfsl_log_file *nfsl_log_file_del(struct nfsl_log_file *elfrec,
        struct nfsl_log_file **elf_listp);

static char *nfsl_get_time(time_t tt);
static char *nfsl_get_date(time_t tt);
static char *nfsl_get_date_nq(time_t tt);
static int nfsl_write_elfbuf(struct nfsl_log_file *elfrec);
static void nfsl_ipaddr_print(struct nfsl_log_file *, struct netbuf *);
static void nfsl_elf_record_header_print(struct nfsl_log_file *,
                nfslog_record_header *, char *, char *,
                struct nfsl_proc_disp *, char *);
static void nfsl_elf_buffer_header_print(struct nfsl_log_file *,
                nfslog_buffer_header *);
static struct nfsl_proc_disp *nfsl_find_elf_dispatch(
                nfslog_request_record *, char **);
static void nfsl_elf_rpc_print(struct nfsl_log_file *,
                nfslog_request_record *, struct nfsl_proc_disp *,
                char *, char *, char *);
static void nfslog_size3_print(struct nfsl_log_file *, set_size3 *);

static void nfslog_null_args(struct nfsl_log_file *, caddr_t *);
static void nfslog_null_res(struct nfsl_log_file *, caddr_t *);


/*
 * NFS VERSION 2
 */

/* Functions for elf print of the arguments */
static void nfslog_fhandle_print(struct nfsl_log_file *, fhandle_t *);
static void nfslog_diropargs_print(struct nfsl_log_file *, nfslog_diropargs *);
static void nfslog_setattrargs_print(struct nfsl_log_file *,
        nfslog_setattrargs *);
static void nfslog_sattr_print(struct nfsl_log_file *,
        nfslog_sattr *);
static void nfslog_nfsreadargs_print(struct nfsl_log_file *,
        nfslog_nfsreadargs *);
static void nfslog_writeargs_print(struct nfsl_log_file *,
        nfslog_writeargs *);
static void nfslog_writeresult_print(struct nfsl_log_file *,
        nfslog_writeresult *, bool_t);
static void nfslog_creatargs_print(struct nfsl_log_file *,
        nfslog_createargs *);
static void nfslog_rddirargs_print(struct nfsl_log_file *, nfslog_rddirargs *);
static void nfslog_linkargs_print(struct nfsl_log_file *, nfslog_linkargs *);
static void nfslog_rnmargs_print(struct nfsl_log_file *, nfslog_rnmargs *);
static void nfslog_symlinkargs_print(struct nfsl_log_file *,
        nfslog_symlinkargs *);

static void nfslog_sharefsargs_print(struct nfsl_log_file *,
        nfslog_sharefsargs *);
static void nfslog_getfhargs_print(struct nfsl_log_file *,
        nfslog_getfhargs *);

/* Functions for elf print of the response */
static void nfslog_nfsstat_print(struct nfsl_log_file *, enum nfsstat *,
        bool_t);
static void nfslog_diropres_print(struct nfsl_log_file *, nfslog_diropres *,
        bool_t);
static void nfslog_rdlnres_print(struct nfsl_log_file *, nfslog_rdlnres *,
        bool_t);
static void nfslog_rdresult_print(struct nfsl_log_file *,
        nfslog_rdresult *, bool_t);
static void nfslog_rddirres_print(struct nfsl_log_file *, nfslog_rddirres *,
        bool_t);

/*
 * NFS VERSION 3
 */

/* Functions for elf print of the arguments */
static void nfslog_fh3_print(struct nfsl_log_file *, nfs_fh3 *);
static void nfslog_diropargs3_print(struct nfsl_log_file *,
        nfslog_diropargs3 *);
static void nfslog_SETATTR3args_print(struct nfsl_log_file *,
        nfslog_SETATTR3args *);
static void nfslog_READ3args_print(struct nfsl_log_file *, nfslog_READ3args *);
static void nfslog_WRITE3args_print(struct nfsl_log_file *,
        nfslog_WRITE3args *);
static void nfslog_CREATE3args_print(struct nfsl_log_file *,
        nfslog_CREATE3args *);
static void nfslog_MKDIR3args_print(struct nfsl_log_file *,
        nfslog_MKDIR3args *);
static void nfslog_SYMLINK3args_print(struct nfsl_log_file *,
        nfslog_SYMLINK3args *);
static void nfslog_MKNOD3args_print(struct nfsl_log_file *,
        nfslog_MKNOD3args *);
static void nfslog_REMOVE3args_print(struct nfsl_log_file *,
        nfslog_REMOVE3args *);
static void nfslog_RMDIR3args_print(struct nfsl_log_file *,
        nfslog_RMDIR3args *);
static void nfslog_RENAME3args_print(struct nfsl_log_file *,
        nfslog_RENAME3args *);
static void nfslog_LINK3args_print(struct nfsl_log_file *,
        nfslog_LINK3args *);
static void nfslog_COMMIT3args_print(struct nfsl_log_file *,
        nfslog_COMMIT3args *);
static void nfslog_READDIRPLUS3args_print(struct nfsl_log_file *,
        nfslog_READDIRPLUS3args *);

/* Functions for elf print of the response */
static void nfslog_nfsstat3_print(struct nfsl_log_file *,
        nfsstat3 *, bool_t);
static void nfslog_LOOKUP3res_print(struct nfsl_log_file *,
        nfslog_LOOKUP3res *, bool_t);
static void nfslog_READLINK3res_print(struct nfsl_log_file *,
        nfslog_READLINK3res *, bool_t);
static void nfslog_READ3res_print(struct nfsl_log_file *,
        nfslog_READ3res *, bool_t);
static void nfslog_WRITE3res_print(struct nfsl_log_file *,
        nfslog_WRITE3res *, bool_t);
static void nfslog_CREATE3res_print(struct nfsl_log_file *,
        nfslog_CREATE3res *, bool_t);
static void nfslog_MKDIR3res_print(struct nfsl_log_file *,
        nfslog_MKDIR3res *, bool_t);
static void nfslog_SYMLINK3res_print(struct nfsl_log_file *,
        nfslog_SYMLINK3res *, bool_t);
static void nfslog_MKNOD3res_print(struct nfsl_log_file *,
        nfslog_MKNOD3res *, bool_t);
static void nfslog_READDIRPLUS3res_print(struct nfsl_log_file *,
        nfslog_READDIRPLUS3res *, bool_t);

extern int debug;
static bool_t nfsl_print_fh = FALSE;            /* print file handles? */

#define DFLT_BUFFERSIZE         8192
#define DFLT_OVFSIZE            3072    /* Maximum logged or buffered size */

static char hostname[MAXHOSTNAMELEN];   /* name of host */


/*
 * Define the actions taken per prog/vers/proc:
 *
 * In some cases, the nl types are the same as the nfs types and a simple
 * bcopy should suffice. Rather that define tens of identical procedures,
 * simply define these to bcopy. Similarly this takes care of different
 * procs that use same parameter struct.
 */

static struct nfsl_proc_disp nfsl_elf_proc_v2[] = {
        /*
         * NFS VERSION 2
         */

        /* RFS_NULL = 0 */
        {nfslog_null_args, nfslog_null_res, "\"null\""},

        /* RFS_GETATTR = 1 */
        {nfslog_fhandle_print, nfslog_nfsstat_print, "\"getattr\""},

        /* RFS_SETATTR = 2 */
        {nfslog_setattrargs_print, nfslog_nfsstat_print, "\"setattr\""},

        /* RFS_ROOT = 3 *** NO LONGER SUPPORTED *** */
        {nfslog_null_args, nfslog_null_res, "\"root\""},

        /* RFS_LOOKUP = 4 */
        {nfslog_diropargs_print, nfslog_diropres_print, "\"lookup\""},

        /* RFS_READLINK = 5 */
        {nfslog_fhandle_print, nfslog_rdlnres_print, "\"readlink\""},

        /* RFS_READ = 6 */
        {nfslog_nfsreadargs_print, nfslog_rdresult_print, "\"read\""},

        /* RFS_WRITECACHE = 7 *** NO LONGER SUPPORTED *** */
        {nfslog_null_args, nfslog_null_res, "\"writecache\""},

        /* RFS_WRITE = 8 */
        {nfslog_writeargs_print, nfslog_writeresult_print, "\"write\""},

        /* RFS_CREATE = 9 */
        {nfslog_creatargs_print, nfslog_diropres_print, "\"create\""},

        /* RFS_REMOVE = 10 */
        {nfslog_diropargs_print, nfslog_nfsstat_print, "\"remove\""},

        /* RFS_RENAME = 11 */
        {nfslog_rnmargs_print, nfslog_nfsstat_print, "\"rename\""},

        /* RFS_LINK = 12 */
        {nfslog_linkargs_print, nfslog_nfsstat_print, "\"link\""},

        /* RFS_SYMLINK = 13 */
        {nfslog_symlinkargs_print, nfslog_nfsstat_print, "\"symlink\""},

        /* RFS_MKDIR = 14 */
        {nfslog_creatargs_print, nfslog_diropres_print, "\"mkdir\""},

        /* RFS_RMDIR = 15 */
        {nfslog_diropargs_print, nfslog_nfsstat_print, "\"rmdir\""},

        /* RFS_READDIR = 16 */
        {nfslog_rddirargs_print, nfslog_rddirres_print, "\"readdir\""},

        /* RFS_STATFS = 17 */
        {nfslog_fhandle_print, nfslog_nfsstat_print, "\"statfs\""},
};


/*
 * NFS VERSION 3
 */

static struct nfsl_proc_disp nfsl_elf_proc_v3[] = {

        /* NFSPROC3_NULL = 0 */
        {nfslog_null_args, nfslog_null_res, "\"null\""},

        /* NFSPROC3_GETATTR = 1 */
        {nfslog_fh3_print, nfslog_nfsstat3_print, "\"getattr\""},

        /* NFSPROC3_SETATTR = 2 */
        {nfslog_SETATTR3args_print, nfslog_nfsstat3_print, "\"setattr\""},

        /* NFSPROC3_LOOKUP = 3 */
        {nfslog_diropargs3_print, nfslog_LOOKUP3res_print, "\"lookup\""},

        /* NFSPROC3_ACCESS = 4 */
        {nfslog_fh3_print, nfslog_nfsstat3_print, "\"access\""},

        /* NFSPROC3_READLINK = 5 */
        {nfslog_fh3_print, nfslog_READLINK3res_print, "\"readlink\""},

        /* NFSPROC3_READ = 6 */
        {nfslog_READ3args_print, nfslog_READ3res_print, "\"read\""},

        /* NFSPROC3_WRITE = 7 */
        {nfslog_WRITE3args_print, nfslog_WRITE3res_print, "\"write\""},

        /* NFSPROC3_CREATE = 8 */
        {nfslog_CREATE3args_print, nfslog_CREATE3res_print, "\"create\""},

        /* NFSPROC3_MKDIR = 9 */
        {nfslog_MKDIR3args_print, nfslog_MKDIR3res_print, "\"mkdir\""},

        /* NFSPROC3_SYMLINK = 10 */
        {nfslog_SYMLINK3args_print, nfslog_SYMLINK3res_print, "\"symlink\""},

        /* NFSPROC3_MKNOD = 11 */
        {nfslog_MKNOD3args_print, nfslog_MKNOD3res_print, "\"mknod\""},

        /* NFSPROC3_REMOVE = 12 */
        {nfslog_REMOVE3args_print, nfslog_nfsstat3_print, "\"remove\""},

        /* NFSPROC3_RMDIR = 13 */
        {nfslog_RMDIR3args_print, nfslog_nfsstat3_print, "\"rmdir\""},

        /* NFSPROC3_RENAME = 14 */
        {nfslog_RENAME3args_print, nfslog_nfsstat3_print, "\"rename\""},

        /* NFSPROC3_LINK = 15 */
        {nfslog_LINK3args_print, nfslog_nfsstat3_print, "\"link\""},

        /* NFSPROC3_READDIR = 16 */
        {nfslog_fh3_print, nfslog_nfsstat3_print, "\"readdir\""},

        /* NFSPROC3_READDIRPLUS = 17 */
        {nfslog_READDIRPLUS3args_print, nfslog_READDIRPLUS3res_print,
                "\"readdirplus\""},

        /* NFSPROC3_FSSTAT = 18 */
        {nfslog_fh3_print, nfslog_nfsstat3_print, "\"fsstat\""},

        /* NFSPROC3_FSINFO = 19 */
        {nfslog_fh3_print, nfslog_nfsstat3_print, "\"fsinfo\""},

        /* NFSPROC3_PATHCONF = 20 */
        {nfslog_fh3_print, nfslog_nfsstat3_print, "\"pathconf\""},

        /* NFSPROC3_COMMIT = 21 */
        {nfslog_COMMIT3args_print, nfslog_nfsstat3_print, "\"commit\""},
};

/*
 * NFSLOG VERSION 1
 */

static struct nfsl_proc_disp nfsl_log_elf_proc_v1[] = {

        /* NFSLOG_NULL = 0 */
        {nfslog_null_args, nfslog_null_res, "\"null\""},

        /* NFSLOG_SHARE = 1 */
        {nfslog_sharefsargs_print, nfslog_nfsstat_print, "\"log_share\""},

        /* NFSLOG_UNSHARE = 2 */
        {nfslog_sharefsargs_print, nfslog_nfsstat_print, "\"log_unshare\""},

        /* NFSLOG_LOOKUP = 3 */
        {nfslog_diropargs3_print, nfslog_LOOKUP3res_print, "\"lookup\""},

        /* NFSLOG_GETFH = 4 */
        {nfslog_getfhargs_print, nfslog_nfsstat_print, "\"log_getfh\""},
};

static struct nfsl_vers_disp nfsl_elf_vers_disptable[] = {
        {sizeof (nfsl_elf_proc_v2) / sizeof (nfsl_elf_proc_v2[0]),
            nfsl_elf_proc_v2},
        {sizeof (nfsl_elf_proc_v3) / sizeof (nfsl_elf_proc_v3[0]),
            nfsl_elf_proc_v3},
};

static struct nfsl_vers_disp nfsl_log_elf_vers_disptable[] = {
        {sizeof (nfsl_log_elf_proc_v1) / sizeof (nfsl_log_elf_proc_v1[0]),
            nfsl_log_elf_proc_v1},
};

static struct nfsl_prog_disp nfsl_elf_dispatch_table[] = {
        {NFS_PROGRAM,
            NFS_VERSMIN,
            sizeof (nfsl_elf_vers_disptable) /
                sizeof (nfsl_elf_vers_disptable[0]),
            nfsl_elf_vers_disptable, "nfs"},
        {NFSLOG_PROGRAM,
            NFSLOG_VERSMIN,
            sizeof (nfsl_log_elf_vers_disptable) /
                sizeof (nfsl_log_elf_vers_disptable[0]),
            nfsl_log_elf_vers_disptable, "nfslog"},
};

static int      nfsl_elf_dispatch_table_arglen =
                        sizeof (nfsl_elf_dispatch_table) /
                        sizeof (nfsl_elf_dispatch_table[0]);

static char *
nfslog_get_status(short status)
{
        int     low, mid, high;
        short   errstat;

        /* Usually status is 0... */
        if (status == 0)
                return (nfsl_status_name[0]);

        low = 0;
        high = NFSL_ERR_CNT;
        mid = NFSL_ERR_CNT / 2;
        /* binary search for status string */
        while (((errstat = nfsl_status[mid]) != status) && (low < mid) &&
                (mid < high)) {
                if (errstat > status) { /* search bottom half */
                        high = mid;
                } else {                /* search upper half */
                        low = mid;
                }
                mid = low + ((high - low) / 2);
        }
        if (errstat == status) {        /* found it */
                return (nfsl_status_name[mid]);
        }
        return (NULL);
}

/* nfsl_get_time - return string with time formatted as hh:mm:ss */
static char *
nfsl_get_time(time_t tt)
{
        static char     timestr[20];
        static time_t   lasttime;
        struct tm       tmst;

        if (tt == lasttime)
                return (timestr);
        if (localtime_r(&tt, &tmst) == NULL) {
                return (empty_name);
        }
        (void) sprintf(timestr, "%02d:%02d:%02d",
                tmst.tm_hour, tmst.tm_min, tmst.tm_sec);
        lasttime = tt;
        return (timestr);
}

/* nfsl_get_date - return date string formatted as "yyyy-mm-dd hh:mm:ss" */
static char *
nfsl_get_date(time_t tt)
{
        static char     timestr[30];
        static time_t   lasttime;
        struct tm       tmst;

        if (tt == lasttime)
                return (timestr);
        if (localtime_r(&tt, &tmst) == NULL) {
                return (empty_name);
        }
        (void) sprintf(timestr, "\"%04d-%02d-%02d %02d:%02d:%02d\"",
                tmst.tm_year + 1900, tmst.tm_mon + 1, tmst.tm_mday,
                tmst.tm_hour, tmst.tm_min, tmst.tm_sec);
        lasttime = tt;
        return (timestr);
}

/*
 * nfsl_get_date_nq - return date string formatted as yyyy-mm-dd hh:mm:ss
 * (no quotes)
 */
static char *
nfsl_get_date_nq(time_t tt)
{
        static char     timestr[30];
        static time_t   lasttime;
        struct tm       tmst;

        if (tt == lasttime)
                return (timestr);
        if (localtime_r(&tt, &tmst) == NULL) {
                return (empty_name);
        }
        (void) sprintf(timestr, "%04d-%02d-%02d %02d:%02d:%02d",
                tmst.tm_year + 1900, tmst.tm_mon + 1, tmst.tm_mday,
                tmst.tm_hour, tmst.tm_min, tmst.tm_sec);
        return (timestr);
}

/* write log buffer out to file */
static int
nfsl_write_elfbuf(struct nfsl_log_file *elfrec)
{
        int     rc;
        char    *elfbuf = elfrec->buf;
        int     elfbufoffset = elfrec->bufoffset;

        if (debug > 1)
                (void) printf("nfsl_write_elfbuf: bufoffset %d\n",
                        elfbufoffset);
        if (elfbufoffset <= 0)
                return (0);
        elfbuf[elfbufoffset] = '\0';
        if ((rc = fputs(elfbuf, elfrec->fp)) < 0) {
                syslog(LOG_ERR, gettext("Write to %s failed: %s\n"),
                        elfrec->path, strerror(errno));
                return (-1);
        }
        if (rc != elfbufoffset) {
                syslog(LOG_ERR, gettext("Write %d bytes to %s returned %d\n"),
                        elfbufoffset, elfrec->path, rc);
                return (-1);
        }
        elfrec->bufoffset = 0;
        return (0);
}

/*ARGSUSED*/
static void
nfslog_null_args(struct nfsl_log_file *elfrec, caddr_t *nfsl_args)
{
}

/*ARGSUSED*/
static void
nfslog_null_res(struct nfsl_log_file *elfrec, caddr_t *nfsl_res)
{
}

static void
nfslog_fh3_print(struct nfsl_log_file *elfrec, nfs_fh3 *fh3)
{
        if (!nfsl_print_fh)
                return;
        if (fh3->fh3_length == sizeof (fhandle_t)) {
                nfslog_fhandle_print(elfrec, (fhandle_t *)&fh3->fh3_u.data);
        } else {
                nfslog_opaque_print_buf(fh3->fh3_u.data, fh3->fh3_length,
                        elfrec->buf, &elfrec->bufoffset,
                        DFLT_BUFFERSIZE + DFLT_OVFSIZE);
        }
}

/*
 * NFS VERSION 2
 */


/* Functions that elf print the arguments */

static void
nfslog_fhandle_print(struct nfsl_log_file *elfrec, fhandle_t *args)
{
        if (!nfsl_print_fh)
                return;
        nfslog_opaque_print_buf(args, sizeof (*args),
                        elfrec->buf, &elfrec->bufoffset,
                        DFLT_BUFFERSIZE + DFLT_OVFSIZE);
}

static void
nfslog_diropargs_print(struct nfsl_log_file *elfrec, nfslog_diropargs *args)
{
        char    *elfbuf = elfrec->buf;
        int     elfbufoffset = elfrec->bufoffset;

        if (nfsl_print_fh) {
                nfslog_fhandle_print(elfrec, &args->da_fhandle);
                elfbufoffset = elfrec->bufoffset;
                if (args->da_name != NULL) {
                        elfbufoffset += sprintf(&elfbuf[elfbufoffset],
                                " \"%s\"", args->da_name);
                } else {
                        elfbufoffset += sprintf(&elfbuf[elfbufoffset], " %s",
                                empty_name);
                }
        }
        elfrec->bufoffset = elfbufoffset;
}

static void
nfslog_sattr_print(struct nfsl_log_file *elfrec, nfslog_sattr *args)
{
        char    *elfbuf = elfrec->buf;
        int     elfbufoffset = elfrec->bufoffset;

/* BEGIN CSTYLED */
        if (args->sa_mode != (uint32_t)-1) {
                elfbufoffset += sprintf(&elfbuf[elfbufoffset],
                        " \"mode=0%o\"", args->sa_mode);
        }
        if (args->sa_uid != (uint32_t)-1) {
                elfbufoffset += sprintf(&elfbuf[elfbufoffset],
                        " \"uid=0x%x\"", args->sa_uid);
        }
        if (args->sa_gid != (uint32_t)-1) {
                elfbufoffset += sprintf(&elfbuf[elfbufoffset],
                        " \"gid=0x%x\"", args->sa_gid);
        }
        if (args->sa_size != (uint32_t)-1) {
                elfbufoffset += sprintf(&elfbuf[elfbufoffset],
                        " \"size=0x%x\"", args->sa_size);
        }
        if (args->sa_atime.tv_sec != (uint32_t)-1) {
                elfbufoffset += sprintf(&elfbuf[elfbufoffset],
                        " \"atime=%s\"",
                    nfsl_get_date_nq((time_t)args->sa_atime.tv_sec));
        }
        if (args->sa_mtime.tv_sec != (uint32_t)-1) {
                elfbufoffset += sprintf(&elfbuf[elfbufoffset],
                        " \"mtime=%s\"",
                    nfsl_get_date_nq((time_t)args->sa_mtime.tv_sec));
        }
/* END CSTYLED */
        elfrec->bufoffset = elfbufoffset;
}

static void
nfslog_setattrargs_print(struct nfsl_log_file *elfrec, nfslog_setattrargs *args)
{
        nfslog_fhandle_print(elfrec, &args->saa_fh);
        nfslog_sattr_print(elfrec, &args->saa_sa);
}

static void
nfslog_nfsreadargs_print(struct nfsl_log_file *elfrec,
        nfslog_nfsreadargs *args)
{
        char    *elfbuf = elfrec->buf;
        int     elfbufoffset;

        nfslog_fhandle_print(elfrec, &args->ra_fhandle);
        elfbufoffset = elfrec->bufoffset;
        elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x",
                args->ra_offset);
        elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x",
                args->ra_count);
        elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x",
                args->ra_totcount);
        elfrec->bufoffset = elfbufoffset;
}

static void
nfslog_writeargs_print(struct nfsl_log_file *elfrec, nfslog_writeargs *args)
{
        char    *elfbuf = elfrec->buf;
        int     elfbufoffset = elfrec->bufoffset;

        nfslog_fhandle_print(elfrec, &args->waargs_fhandle);
        elfbufoffset = elfrec->bufoffset;
        elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x",
                args->waargs_begoff);
        elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x",
                args->waargs_offset);
        elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x",
                args->waargs_totcount);
        elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " 0x%x",
                args->waargs_count);
}

static void
nfslog_writeresult_print(struct nfsl_log_file *elfrec, nfslog_writeresult *res,
        bool_t print_status)
{
        char    *elfbuf = elfrec->buf;
        int     elfbufoffset = elfrec->bufoffset;

        if (print_status) {
                nfslog_nfsstat_print(elfrec, &res->wr_status, print_status);
        } else if (res->wr_status == NFS_OK) {
                elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x",
                        res->nfslog_writeresult_u.wr_size);
                elfrec->bufoffset = elfbufoffset;
        }
}

static void
nfslog_creatargs_print(struct nfsl_log_file *elfrec, nfslog_createargs *args)
{
        nfslog_diropargs_print(elfrec, &args->ca_da);
        nfslog_sattr_print(elfrec, &args->ca_sa);
}


static void
nfslog_rddirargs_print(struct nfsl_log_file *elfrec, nfslog_rddirargs *args)
{
        char    *elfbuf = elfrec->buf;
        int     elfbufoffset;

        nfslog_fhandle_print(elfrec, &args->rda_fh);
        elfbufoffset = elfrec->bufoffset;
        elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x",
                args->rda_offset);
        elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x",
                args->rda_count);
        elfrec->bufoffset = elfbufoffset;
}

static void
nfslog_rnmargs_print(struct nfsl_log_file *elfrec, nfslog_rnmargs *args)
{
        nfslog_diropargs_print(elfrec, &args->rna_from);
        nfslog_diropargs_print(elfrec, &args->rna_to);
}

static void
nfslog_linkargs_print(struct nfsl_log_file *elfrec, nfslog_linkargs *args)
{
        nfslog_fhandle_print(elfrec, &args->la_from);
        nfslog_diropargs_print(elfrec, &args->la_to);
}

static void
nfslog_symlinkargs_print(struct nfsl_log_file *elfrec, nfslog_symlinkargs *args)
{
        char    *elfbuf = elfrec->buf;
        int     elfbufoffset;

        nfslog_diropargs_print(elfrec, &args->sla_from);
        elfbufoffset = elfrec->bufoffset;
        elfbufoffset += sprintf(&elfbuf[elfbufoffset], " \"%s\"",
                args->sla_tnm);
        elfrec->bufoffset = elfbufoffset;
        nfslog_sattr_print(elfrec, &args->sla_sa);
}

/*
 * SHARE/UNSHARE fs log args copy
 */
static void
nfslog_sharefsargs_print(struct nfsl_log_file *elfrec,
        nfslog_sharefsargs *args)
{
        unsigned int    elfbufoffset;
        char            *elfbuf = elfrec->buf;

        nfslog_fhandle_print(elfrec, &args->sh_fh_buf);

        elfbufoffset = elfrec->bufoffset;
        elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x",
                args->sh_flags);
        elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x",
                args->sh_anon);
        if (nfsl_print_fh) {
                if (args->sh_path != NULL) {
                        elfbufoffset += sprintf(&elfbuf[elfbufoffset],
                                " \"%s\"", args->sh_path);
                } else {
                        elfbufoffset += sprintf(&elfbuf[elfbufoffset], " %s",
                                empty_name);
                }
        }
        elfrec->bufoffset = elfbufoffset;
}

static void
nfslog_getfhargs_print(struct nfsl_log_file *elfrec,
        nfslog_getfhargs *args)
{
        unsigned int    elfbufoffset;
        char            *elfbuf = elfrec->buf;

        nfslog_fhandle_print(elfrec, &args->gfh_fh_buf);

        elfbufoffset = elfrec->bufoffset;
        if (nfsl_print_fh) {
                if (args->gfh_path != NULL) {
                        elfbufoffset += sprintf(&elfbuf[elfbufoffset],
                                " \"%s\"", args->gfh_path);
                } else {
                        elfbufoffset += sprintf(&elfbuf[elfbufoffset], " %s",
                                empty_name);
                }
        }
        elfrec->bufoffset = elfbufoffset;
}

static void
nfslog_nfsstat_print(struct nfsl_log_file *elfrec, enum nfsstat *res,
        bool_t print_status)
{
        if (print_status) {
                char    *statp = nfslog_get_status((short)(*res));

                if (statp != NULL)
                        elfrec->bufoffset +=
                                sprintf(&elfrec->buf[elfrec->bufoffset], " %s",
                                                statp);
                else
                        elfrec->bufoffset +=
                                sprintf(&elfrec->buf[elfrec->bufoffset], " %5d",
                                                *res);
        }
}

static void
nfslog_diropres_print(struct nfsl_log_file *elfrec, nfslog_diropres *res,
        bool_t print_status)
{
        if (print_status) {
                nfslog_nfsstat_print(elfrec, &res->dr_status, print_status);
        } else if (res->dr_status == NFS_OK) {
                nfslog_fhandle_print(elfrec,
                        &res->nfslog_diropres_u.dr_ok.drok_fhandle);
        }
}

static void
nfslog_rdlnres_print(struct nfsl_log_file *elfrec, nfslog_rdlnres *res,
        bool_t print_status)
{
        if (print_status) {
                nfslog_nfsstat_print(elfrec, &res->rl_status, print_status);
        } else if (res->rl_status == NFS_OK) {
                elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset],
                        " \"%s\"", res->nfslog_rdlnres_u.rl_ok);
        }
}

static void
nfslog_rdresult_print(struct nfsl_log_file *elfrec, nfslog_rdresult *res,
        bool_t print_status)
{
        if (print_status) {
                nfslog_nfsstat_print(elfrec, &res->r_status, print_status);
        } else if (res->r_status == NFS_OK) {
                elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset],
                        " 0x%x", res->nfslog_rdresult_u.r_ok.filesize);
                elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset],
                        " 0x%x", res->nfslog_rdresult_u.r_ok.rrok_count);
        }
}

static void
nfslog_rddirres_print(struct nfsl_log_file *elfrec, nfslog_rddirres *res,
        bool_t print_status)
{
        if (print_status) {
                nfslog_nfsstat_print(elfrec, &res->rd_status, print_status);
        } else if (res->rd_status == NFS_OK) {
                char    *elfbuf = elfrec->buf;
                int     elfbufoffset = elfrec->bufoffset;

                elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x",
                        res->nfslog_rddirres_u.rd_ok.rdok_offset);
                elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x",
                        res->nfslog_rddirres_u.rd_ok.rdok_size);
                elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x",
                        res->nfslog_rddirres_u.rd_ok.rdok_eof);
                elfrec->bufoffset = elfbufoffset;
        }
}

/*
 * NFS VERSION 3
 */

static void
nfslog_diropargs3_print(struct nfsl_log_file *elfrec,
        nfslog_diropargs3 *args)
{
        if (nfsl_print_fh) {
                nfslog_fh3_print(elfrec, &args->dir);
                elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset],
                        " \"%s\"", args->name);
        }
}

static void
nfslog_size3_print(struct nfsl_log_file *elfrec, set_size3 *args)
{
        char    *elfbuf = elfrec->buf;
        int     elfbufoffset = elfrec->bufoffset;

        if (args->set_it) {
                elfbufoffset += sprintf(&elfbuf[elfbufoffset],
                /* CSTYLED */
                        " \"size=0x%llx\"", args->size);
        }
        elfrec->bufoffset = elfbufoffset;
}

static void
nfslog_SETATTR3args_print(struct nfsl_log_file *elfrec,
        nfslog_SETATTR3args *args)
{
        nfslog_fh3_print(elfrec, &args->object);
        nfslog_size3_print(elfrec, &args->size);
}

static void
nfslog_READ3args_print(struct nfsl_log_file *elfrec, nfslog_READ3args *args)
{
        nfslog_fh3_print(elfrec, &args->file);
        elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " 0x%llx",
                args->offset);
        elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " 0x%x",
                args->count);
}

static void
nfslog_WRITE3args_print(struct nfsl_log_file *elfrec,
        nfslog_WRITE3args *args)
{
        char    *elfbuf = elfrec->buf;
        int     elfbufoffset;

        nfslog_fh3_print(elfrec, &args->file);
        elfbufoffset = elfrec->bufoffset;
        elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%llx",
                args->offset);
        elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x",
                args->count);
        elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x",
                args->stable);
        elfrec->bufoffset = elfbufoffset;
}

static void
nfslog_CREATE3args_print(struct nfsl_log_file *elfrec,
        nfslog_CREATE3args *args)
{
        nfslog_diropargs3_print(elfrec, &args->where);
        elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " %s",
                NFSL_CREATEMODE3(args->how.mode));
        if (args->how.mode != EXCLUSIVE) {
                nfslog_size3_print(elfrec,
                        &args->how.nfslog_createhow3_u.size);
        }
}

static void
nfslog_MKDIR3args_print(struct nfsl_log_file *elfrec,
        nfslog_MKDIR3args *args)
{
        nfslog_diropargs3_print(elfrec, &args->where);
}

static void
nfslog_SYMLINK3args_print(struct nfsl_log_file *elfrec,
        nfslog_SYMLINK3args *args)
{
        nfslog_diropargs3_print(elfrec, &args->where);
        elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset],
                " \"%s\"", args->symlink_data);
}

static void
nfslog_MKNOD3args_print(struct nfsl_log_file *elfrec,
        nfslog_MKNOD3args *args)
{
        nfslog_diropargs3_print(elfrec, &args->where);
        elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " %s",
                NFSL_FTYPE3(args->type));
}

static void
nfslog_REMOVE3args_print(struct nfsl_log_file *elfrec,
        nfslog_REMOVE3args *args)
{
        nfslog_diropargs3_print(elfrec, &args->object);
}

static void
nfslog_RMDIR3args_print(struct nfsl_log_file *elfrec,
        nfslog_RMDIR3args *args)
{
        nfslog_diropargs3_print(elfrec, &args->object);
}

static void
nfslog_RENAME3args_print(struct nfsl_log_file *elfrec,
        nfslog_RENAME3args *args)
{
        nfslog_diropargs3_print(elfrec, &args->from);
        nfslog_diropargs3_print(elfrec, &args->to);
}

static void
nfslog_LINK3args_print(struct nfsl_log_file *elfrec, nfslog_LINK3args *args)
{
        nfslog_fh3_print(elfrec, &args->file);
        nfslog_diropargs3_print(elfrec, &args->link);
}

static void
nfslog_READDIRPLUS3args_print(struct nfsl_log_file *elfrec,
        nfslog_READDIRPLUS3args *args)
{
        nfslog_fh3_print(elfrec, &args->dir);
        elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " 0x%x",
                args->dircount);
        elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " 0x%x",
                args->maxcount);
}

static void
nfslog_COMMIT3args_print(struct nfsl_log_file *elfrec,
        nfslog_COMMIT3args *args)
{
        nfslog_fh3_print(elfrec, &args->file);
        elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " 0x%llx",
                args->offset);
        elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], " 0x%x",
                args->count);
}

static void
nfslog_nfsstat3_print(struct nfsl_log_file *elfrec, enum nfsstat3 *res,
        bool_t print_status)
{
        if (print_status) {
                char    *statp = nfslog_get_status((short)(*res));

                if (statp != NULL)
                        elfrec->bufoffset +=
                                sprintf(&elfrec->buf[elfrec->bufoffset], " %s",
                                        statp);
                else
                        elfrec->bufoffset +=
                                sprintf(&elfrec->buf[elfrec->bufoffset], " %5d",
                                        *res);
        }
}

static void
nfslog_LOOKUP3res_print(struct nfsl_log_file *elfrec,
        nfslog_LOOKUP3res *res, bool_t print_status)
{
        if (print_status) {
                nfslog_nfsstat3_print(elfrec, &res->status, print_status);
        } else if (res->status == NFS3_OK) {
                nfslog_fh3_print(elfrec, &res->nfslog_LOOKUP3res_u.object);
        }
}

static void
nfslog_READLINK3res_print(struct nfsl_log_file *elfrec,
        nfslog_READLINK3res *res, bool_t print_status)
{
        if (print_status) {
                nfslog_nfsstat3_print(elfrec, &res->status, print_status);
        } else if (res->status == NFS3_OK) {
                elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset],
                        " %s", res->nfslog_READLINK3res_u.data);
        }
}

static void
nfslog_READ3res_print(struct nfsl_log_file *elfrec, nfslog_READ3res *res,
        bool_t print_status)
{
        if (print_status) {
                nfslog_nfsstat3_print(elfrec, &res->status, print_status);
        } else if (res->status == NFS3_OK) {
                char    *elfbuf = elfrec->buf;
                int     elfbufoffset = elfrec->bufoffset;

                elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%llx",
                        res->nfslog_READ3res_u.ok.filesize);
                elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x",
                        res->nfslog_READ3res_u.ok.count);
                elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x",
                        res->nfslog_READ3res_u.ok.eof);
                elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x",
                        res->nfslog_READ3res_u.ok.size);
                elfrec->bufoffset = elfbufoffset;
        }
}

static void
nfslog_WRITE3res_print(struct nfsl_log_file *elfrec, nfslog_WRITE3res *res,
        bool_t print_status)
{
        if (print_status) {
                nfslog_nfsstat3_print(elfrec, &res->status, print_status);
        } else if (res->status == NFS3_OK) {
                char    *elfbuf = elfrec->buf;
                int     elfbufoffset = elfrec->bufoffset;

                elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%llx",
                        res->nfslog_WRITE3res_u.ok.filesize);
                elfbufoffset += sprintf(&elfbuf[elfbufoffset],
                        " 0x%x", res->nfslog_WRITE3res_u.ok.count);
                elfbufoffset += sprintf(&elfrec->buf[elfbufoffset],
                        " 0x%x", res->nfslog_WRITE3res_u.ok.committed);
                elfrec->bufoffset = elfbufoffset;
        }
}

static void
nfslog_CREATE3res_print(struct nfsl_log_file *elfrec, nfslog_CREATE3res *res,
        bool_t print_status)
{
        if (print_status) {
                nfslog_nfsstat3_print(elfrec, &res->status, print_status);
        } else if (res->status == NFS3_OK) {
                if (res->nfslog_CREATE3res_u.ok.obj.handle_follows) {
                        nfslog_fh3_print(elfrec,
                                &res->nfslog_CREATE3res_u.ok.obj.handle);
                }
        }
}

static void
nfslog_MKDIR3res_print(struct nfsl_log_file *elfrec, nfslog_MKDIR3res *res,
        bool_t print_status)
{
        if (print_status) {
                nfslog_nfsstat3_print(elfrec, &res->status, print_status);
        } else if (res->status == NFS3_OK) {
                if (res->nfslog_MKDIR3res_u.obj.handle_follows) {
                        nfslog_fh3_print(elfrec,
                                &res->nfslog_MKDIR3res_u.obj.handle);
                }
        }
}

static void
nfslog_SYMLINK3res_print(struct nfsl_log_file *elfrec, nfslog_SYMLINK3res *res,
        bool_t print_status)
{
        if (print_status) {
                nfslog_nfsstat3_print(elfrec, &res->status, print_status);
        } else if (res->status == NFS3_OK) {
                if (res->nfslog_SYMLINK3res_u.obj.handle_follows) {
                        nfslog_fh3_print(elfrec,
                                &res->nfslog_SYMLINK3res_u.obj.handle);
                }
        }
}

static void
nfslog_MKNOD3res_print(struct nfsl_log_file *elfrec, nfslog_MKNOD3res *res,
        bool_t print_status)
{
        if (print_status) {
                nfslog_nfsstat3_print(elfrec, &res->status, print_status);
        } else if (res->status == NFS3_OK) {
                if (res->nfslog_MKNOD3res_u.obj.handle_follows) {
                        nfslog_fh3_print(elfrec,
                                &res->nfslog_MKNOD3res_u.obj.handle);
                }
        }
}

static void
nfslog_READDIRPLUS3res_print(struct nfsl_log_file *elfrec,
        nfslog_READDIRPLUS3res *res, bool_t print_status)
{
        if (print_status) {
                nfslog_nfsstat3_print(elfrec, &res->status, print_status);
        }
}

/*
 * **** End of table functions for logging specific procs ****
 *
 * Hereafter are the general logging management and dispatcher.
 */


/*
 * nfsl_ipaddr_print - extracts sender ip address from transport struct
 * and prints it in legible form.
 */
static void
nfsl_ipaddr_print(struct nfsl_log_file *elfrec, struct netbuf *ptr)
{
        struct hostent  *hp;
        extern char     *inet_ntop();
        int             size, sin_family, error;
        char            *elfbuf = elfrec->buf;
        char            *addrp;
        int             elfbufoffset = elfrec->bufoffset;

        if (ptr->len == 0) {
                elfbufoffset += sprintf(&elfbuf[elfbufoffset], " %s",
                        empty_name);
                elfbufoffset += sprintf(&elfbuf[elfbufoffset], " %s",
                        empty_name);
                elfrec->bufoffset = elfbufoffset;
                return;
        }
        elfbufoffset += sprintf(&elfbuf[elfbufoffset], " ");
        /* LINTED */
        sin_family = ((struct sockaddr_in *)ptr->buf)->sin_family;
        switch (sin_family) {
        case (AF_INET):
                /* LINTED */
                addrp = (char *)&((struct sockaddr_in *)ptr->buf)->sin_addr;
                size = sizeof (struct in_addr);
                break;
        case (AF_INET6):
                /* LINTED */
                addrp = (char *)&((struct sockaddr_in6 *)ptr->buf)->sin6_addr;
                size = sizeof (struct in6_addr);
                break;
        default:
                /* unknown protocol: print address in hex form */
                for (size = ptr->len, addrp = ptr->buf; size > 0; size--) {
                        elfbufoffset += sprintf(&elfbuf[elfbufoffset], "%02x",
                                *addrp);
                }
                elfbufoffset += sprintf(&elfbuf[elfbufoffset], " %s",
                        empty_name);
                elfrec->bufoffset = elfbufoffset;
                return;
        }
        if (inet_ntop(sin_family, addrp, &elfbuf[elfbufoffset],
                (size_t)(DFLT_BUFFERSIZE + DFLT_OVFSIZE - elfbufoffset))
                    == NULL) {
                /* Not enough space to print - should never happen */
                elfbuf[elfrec->bufoffset] = '\0';       /* just in case */
                return;
        }
        /* inet_ntop copied address into elfbuf, so update offset */
        elfbufoffset += strlen(&elfbuf[elfbufoffset]);
        /* get host name and log it as well */
        hp = getipnodebyaddr(addrp, size, sin_family, &error);
        if (hp != NULL) {
                elfbufoffset += sprintf(&elfbuf[elfbufoffset], " \"%s\"",
                        hp->h_name);
        } else {
                elfbufoffset += sprintf(&elfbuf[elfbufoffset], " %s",
                        empty_name);
        }
        elfrec->bufoffset = elfbufoffset;
}

static void
nfsl_elf_record_header_print(struct nfsl_log_file *elfrec,
        nfslog_record_header *lhp, char *principal_name, char *tag,
        struct nfsl_proc_disp *disp, char *progname)
{
        struct passwd   *pwp = NULL;
        char    *elfbuf = elfrec->buf;
        int     elfbufoffset = elfrec->bufoffset;

        /*
         * Fields: time bytes tag rpc-program rpc-version rpc-procedure
         *         auth-flavor s-user-name s-uid uid u-name gid net-id
         *   c-ip c-dns s-dns status rpcarg-path <arguments> <response>
         */
        elfbufoffset += sprintf(&elfbuf[elfbufoffset], "%s",
                nfsl_get_time((time_t)lhp->rh_timestamp.tv_sec));
        elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%x",
                lhp->rh_reclen);
        if ((tag != NULL) && (tag[0] != '\0')) {
                elfbufoffset += sprintf(&elfbuf[elfbufoffset], " \"%s\"", tag);
        } else {
                elfbufoffset += sprintf(&elfbuf[elfbufoffset], " %s",
                                        empty_name);
        }
        elfbufoffset += sprintf(&elfbuf[elfbufoffset], " %s 0x%x %s",
                                progname, lhp->rh_version, disp->procname);
        elfbufoffset += sprintf(&elfbuf[elfbufoffset], " %s",
                NFSL_AUTH_FLAVOR_PRINT(lhp->rh_auth_flavor));
        if ((principal_name != NULL) && (principal_name[0] != '\0')) {
                elfbufoffset += sprintf(&elfbuf[elfbufoffset], " \"%s\"",
                        principal_name);
                if ((pwp = getpwnam(principal_name)) != NULL) {
                        elfbufoffset += sprintf(&elfbuf[elfbufoffset],
                                " 0x%lx", pwp->pw_uid);
                } else {
                        elfbufoffset += sprintf(&elfbuf[elfbufoffset],
                                " %s", empty_name);
                }
        } else {
                elfbufoffset += sprintf(&elfbuf[elfbufoffset], " %s",
                        empty_name);
                elfbufoffset += sprintf(&elfbuf[elfbufoffset], " %s",
                        empty_name);
        }
        elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%lx", lhp->rh_uid);
        if (((pwp = getpwuid(lhp->rh_uid)) != NULL) && (pwp->pw_name != NULL)) {
                elfbufoffset += sprintf(&elfbuf[elfbufoffset], " \"%s\"",
                        pwp->pw_name);
        } else {
                elfbufoffset += sprintf(&elfbuf[elfbufoffset], " %s",
                        empty_name);
        }
        elfbufoffset += sprintf(&elfbuf[elfbufoffset], " 0x%lx", lhp->rh_gid);
        elfrec->bufoffset = elfbufoffset;
}

static void
nfsl_elf_buffer_header_print(struct nfsl_log_file *elfrec,
        nfslog_buffer_header *bufhdr)
{
        int     rc;
        struct utsname  name;
        char    *elfbuf = elfrec->buf;
        int     elfbufoffset = elfrec->bufoffset;

        rc = uname(&name);
        elfbufoffset += sprintf(&elfbuf[elfbufoffset],
                "#Version %d.0\n#Software \"%s\"\n",
                bufhdr->bh_version, ((rc >= 0) ? name.sysname : empty_name));
        elfbufoffset += sprintf(&elfbuf[elfbufoffset], "#Date %s\n",
                nfsl_get_date((time_t)bufhdr->bh_timestamp.tv_sec));
        elfbufoffset += sprintf(&elfbuf[elfbufoffset], "#Remark %s\n",
                empty_name);
        elfbufoffset += sprintf(&elfbuf[elfbufoffset],
                "#Fields: time bytes tag");
        elfbufoffset += sprintf(&elfbuf[elfbufoffset],
                " rpc-program rpc-version rpc-procedure");
        elfbufoffset += sprintf(&elfbuf[elfbufoffset],
                " auth-flavor s-user-name s-uid uid u-name gid net-id c-ip");
        elfbufoffset += sprintf(&elfbuf[elfbufoffset],
                " c-dns s-dns status rpcarg-path");
        elfbufoffset += sprintf(&elfbuf[elfbufoffset],
                " rpc-arguments... rpc-response...\n");
        elfrec->bufoffset = elfbufoffset;
}

/*
 * nfsl_find_elf_dispatch - get the dispatch struct for this request
 */
static struct nfsl_proc_disp *
nfsl_find_elf_dispatch(nfslog_request_record *logrec, char **prognamep)
{
        nfslog_record_header    *logrechdr = &logrec->re_header;
        struct nfsl_prog_disp   *progtable;     /* prog struct */
        struct nfsl_vers_disp   *verstable;     /* version struct */
        int                     i, vers;

        /* Find prog element - search because can't use prog as array index */
        for (i = 0; (i < nfsl_elf_dispatch_table_arglen) &&
            (logrechdr->rh_prognum != nfsl_elf_dispatch_table[i].nfsl_dis_prog);
                i++);
        if (i >= nfsl_elf_dispatch_table_arglen) {      /* program not logged */
                /* not an error */
                return (NULL);
        }
        progtable = &nfsl_elf_dispatch_table[i];
        /* Find vers element - no validity check - if here it's valid vers */
        vers = logrechdr->rh_version - progtable->nfsl_dis_versmin;
        verstable = &progtable->nfsl_dis_vers_table[vers];
        /* Find proc element - no validity check - if here it's valid proc */
        *prognamep = progtable->progname;
        return (&verstable->nfsl_dis_proc_table[logrechdr->rh_procnum]);
}

/*
 * nfsl_elf_rpc_print - Print the record buffer.
 */
static void
nfsl_elf_rpc_print(struct nfsl_log_file *elfrec,
        nfslog_request_record *logrec, struct nfsl_proc_disp *disp,
        char *progname, char *path1, char *path2)
{
        if (debug > 1) {
                (void) printf("%s %d %s", progname,
                        logrec->re_header.rh_version, disp->procname);
                (void) printf(": '%s', '%s'\n",
                        ((path1 != NULL) ? path1 : empty_name),
                        ((path2 != NULL) ? path2 : empty_name));
        }
        /*
         * XXXX programs using this file to get a usable record should
         * take "record" struct.
         */
        /*
         * Print the variable fields:
         *      principal name
         *      netid
         *      ip address
         *      rpc args
         *      rpc res
         * Use the displacements calculated earlier...
         */

        /*
         * Fields: time bytes tag rpc-program rpc-version rpc-procedure
         *         auth-flavor s-user-name s-uid uid u-name gid net-id c-ip
         *       c-dns s-dns status rpcarg-path <arguments> <response>
         */
        nfsl_elf_record_header_print(elfrec, &logrec->re_header,
                        logrec->re_principal_name, logrec->re_tag,
                        disp, progname);
        if ((logrec->re_netid != NULL) && (logrec->re_netid[0] != '\0')) {
                elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset],
                        " \"%s\"", logrec->re_netid);
        } else {
                elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset],
                        " %s", empty_name);
        }
        nfsl_ipaddr_print(elfrec, &logrec->re_ipaddr);
        elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset],
                " \"%s\"", hostname);
        /* Next is return status */
        (*disp->nfsl_dis_res)(elfrec, logrec->re_rpc_res, TRUE);
        /* Next is argpath */
        if (path1 != NULL) {
                elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset],
                        " \"%s\"", path1);
        } else {
                elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset],
                        " %s", empty_name);
        }
        /*
         * path2 is non-empty for rename/link type operations. If it is non-
         * empty print it here as it's a part of the args
         */
        if (path2 != NULL) {
                elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset],
                        " \"%s\"", path2);
        }
        /* Next print formatted rpc args */
        (*disp->nfsl_dis_args)(elfrec, logrec->re_rpc_arg);
        /* Next print formatted rpc res (minus status) */
        (*disp->nfsl_dis_res)(elfrec, logrec->re_rpc_res, FALSE);
        elfrec->bufoffset += sprintf(&elfrec->buf[elfrec->bufoffset], "\n");
}

/*
 * nfsl_log_file_add - add a new record to the list
 */
static void
nfsl_log_file_add(struct nfsl_log_file *elfrec,
        struct nfsl_log_file **elf_listp)
{
        elfrec->next = *elf_listp;
        elfrec->prev = NULL;
        if (*elf_listp != NULL) {
                (*elf_listp)->prev = elfrec;
        }
        *elf_listp = elfrec;
}

/*
 * nfsl_log_file_find - finds a record in the list, given a cookie (== elfrec)
 * Returns the record.
 */
static struct nfsl_log_file *
nfsl_log_file_find(struct nfsl_log_file *elfrec,
        struct nfsl_log_file *elf_list)
{
        struct nfsl_log_file    *rec;

        for (rec = elf_list; (rec != NULL) && (rec != elfrec);
                rec = rec->next);
        return (rec);
}

/*
 * nfsl_log_file_del - delete a record from the list, does not free rec.
 * Returns the deleted record.
 */
static struct nfsl_log_file *
nfsl_log_file_del(struct nfsl_log_file *elfrec,
        struct nfsl_log_file **elf_listp)
{
        struct nfsl_log_file    *rec;

        if ((rec = nfsl_log_file_find(elfrec, *elf_listp)) == NULL) {
                return (NULL);
        }
        if (rec->prev != NULL) {
                rec->prev->next = rec->next;
        } else {
                *elf_listp = rec->next;
        }
        if (rec->next != NULL) {
                rec->next->prev = rec->prev;
        }
        return (rec);
}

/*
 * nfsl_log_file_free - frees a record
 */
static void
nfsl_log_file_free(struct nfsl_log_file *elfrec)
{
        if (elfrec == NULL)
                return;
        if (elfrec->path != NULL)
                free(elfrec->path);
        if (elfrec->buf != NULL)
                free(elfrec->buf);
        free(elfrec);
}

/*
 * Exported Functions
 */

/*
 * nfslog_open_elf_file - open the output elf file and mallocs needed buffers
 * Returns a pointer to the nfsl_log_file on success, NULL on error.
 *
 * *error contains the last error encountered on this object, It can
 * be used to avoid reporting the same error endlessly, by comparing
 * the current error to the last error. It is reset to the current error
 * code on return.
 */
void *
nfslog_open_elf_file(char *elfpath, nfslog_buffer_header *bufhdr, int *error)
{
        struct nfsl_log_file *elfrec;
        struct stat stat_buf;
        int preverror = *error;

        if ((elfrec = malloc(sizeof (*elfrec))) == NULL) {
                *error = errno;
                if (*error != preverror) {
                        syslog(LOG_ERR, gettext("nfslog_open_elf_file: %s"),
                                strerror(*error));
                }
                return (NULL);
        }
        bzero(elfrec, sizeof (*elfrec));

        elfrec->buf = (char *)malloc(DFLT_BUFFERSIZE + DFLT_OVFSIZE);
        if (elfrec->buf == NULL) {
                *error = errno;
                if (*error != preverror) {
                        syslog(LOG_ERR, gettext("nfslog_open_elf_file: %s"),
                                strerror(*error));
                }
                nfsl_log_file_free(elfrec);
                return (NULL);
        }

        if ((elfrec->path = strdup(elfpath)) == NULL) {
                *error = errno;
                if (*error != preverror) {
                        syslog(LOG_ERR, gettext("nfslog_open_elf_file: %s"),
                                strerror(*error));
                }
                nfsl_log_file_free(elfrec);
                return (NULL);
        }

        if ((elfrec->fp = fopen(elfpath, "a")) == NULL) {
                *error = errno;
                if (*error != preverror) {
                        syslog(LOG_ERR, gettext("Cannot open '%s': %s"),
                                elfpath, strerror(*error));
                }
                nfsl_log_file_free(elfrec);
                return (NULL);
        }

        if (stat(elfpath, &stat_buf) == -1) {
                *error = errno;
                if (*error != preverror) {
                        syslog(LOG_ERR, gettext("Cannot stat '%s': %s"),
                                elfpath, strerror(*error));
                }
                (void) fclose(elfrec->fp);
                nfsl_log_file_free(elfrec);
                return (NULL);
        }

        nfsl_log_file_add(elfrec, &elf_file_list);

        if (stat_buf.st_size == 0) {
                /*
                 * Print header unto logfile
                 */
                nfsl_elf_buffer_header_print(elfrec, bufhdr);
        }

        if (hostname[0] == '\0') {
                (void) gethostname(hostname, MAXHOSTNAMELEN);
        }

        return (elfrec);
}

/*
 * nfslog_close_elf_file - close elffile and write out last buffer
 */
void
nfslog_close_elf_file(void **elfcookie)
{
        struct nfsl_log_file    *elfrec;

        if ((*elfcookie == NULL) || ((elfrec = nfsl_log_file_del(
            *elfcookie, &elf_file_list)) == NULL)) {
                *elfcookie = NULL;
                return;
        }
        if (elfrec->fp != NULL) {
                /* Write the last output buffer to disk */
                (void) nfsl_write_elfbuf(elfrec);
                (void) fclose(elfrec->fp);
        }
        nfsl_log_file_free(elfrec);
        *elfcookie = NULL;
}

/*
 * nfslog_process_elf_rec - processes the record in the buffer and outputs
 *      to the elf log.
 * Return 0 for success, errno else.
 */
int
nfslog_process_elf_rec(void *elfcookie, nfslog_request_record *logrec,
        char *path1, char *path2)
{
        struct nfsl_log_file    *elfrec;
        struct nfsl_proc_disp   *disp;
        char                    *progname;

        if ((elfrec = nfsl_log_file_find(elfcookie, elf_file_list)) == NULL) {
                return (EINVAL);
        }
        /* Make sure there is room */
        if (elfrec->bufoffset > DFLT_BUFFERSIZE) {
                if (nfsl_write_elfbuf(elfrec) < 0) {
                        return (errno);
                }
        }
        if ((disp = nfsl_find_elf_dispatch(logrec, &progname)) != NULL) {
                nfsl_elf_rpc_print(elfrec, logrec, disp, progname,
                        path1, path2);
        }
        return (0);
}