#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/utsname.h>
#include <sys/mman.h>
#include <strings.h>
#include <errno.h>
#include <syslog.h>
#include <time.h>
#include <limits.h>
#include <libintl.h>
#include <values.h>
#include <search.h>
#include <pwd.h>
#include <netdb.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 "nfslogd.h"
#define MAX_LRS_READ_AHEAD 2048
#define MAX_RECS_TO_DELAY 32768
static int nfslog_init_buf(char *, struct nfslog_buf *, int *);
static void nfslog_free_buf(struct nfslog_buf *, int);
static struct nfslog_lr *nfslog_read_buffer(struct nfslog_buf *);
static void free_lrp(struct nfslog_lr *);
static struct nfslog_lr *remove_lrp_from_lb(struct nfslog_buf *,
struct nfslog_lr *);
static void insert_lrp_to_lb(struct nfslog_buf *,
struct nfslog_lr *);
static void nfslog_rewrite_bufheader(struct nfslog_buf *);
struct nfslog_buf *
nfslog_open_buf(char *bufpath, int *error)
{
struct nfslog_buf *lbp = NULL;
if (bufpath == NULL) {
*error = EINVAL;
return (NULL);
}
if ((lbp = malloc(sizeof (struct nfslog_buf))) == NULL) {
*error = ENOMEM;
return (NULL);
}
bzero(lbp, sizeof (struct nfslog_buf));
if (nfslog_init_buf(bufpath, lbp, error)) {
free(lbp);
return (NULL);
}
return (lbp);
}
void
nfslog_close_buf(struct nfslog_buf *lbp, int close_quick)
{
nfslog_free_buf(lbp, close_quick);
free(lbp);
}
static int
nfslog_init_buf(char *bufpath, struct nfslog_buf *lbp, int *error)
{
struct stat sb;
int preverror = *error;
lbp->next = lbp;
lbp->prev = lbp;
lbp->mmap_addr = (intptr_t)MAP_FAILED;
lbp->last_rec_id = MAXINT - 1;
lbp->bh.bh_length = 0;
lbp->bh_lrp = NULL;
lbp->num_lrps = 0;
lbp->lrps = NULL;
lbp->last_record_offset = 0;
lbp->prp = NULL;
lbp->num_pr_queued = 0;
lbp->bufpath = strdup(bufpath);
if (lbp->bufpath == NULL) {
*error = ENOMEM;
if (preverror != *error) {
syslog(LOG_ERR, gettext("Cannot strdup '%s': %s"),
bufpath, strerror(*error));
}
nfslog_free_buf(lbp, FALSE);
return (*error);
}
if ((lbp->fd = open(bufpath, O_RDWR)) < 0) {
*error = errno;
if (preverror != *error) {
syslog(LOG_ERR, gettext("Cannot open '%s': %s"),
bufpath, strerror(*error));
}
nfslog_free_buf(lbp, FALSE);
return (*error);
}
lbp->fl.l_type = F_WRLCK;
lbp->fl.l_whence = SEEK_SET;
lbp->fl.l_start = (offset_t)0;
lbp->fl.l_len = 0;
lbp->fl.l_sysid = 0;
lbp->fl.l_pid = 0;
if (fcntl(lbp->fd, F_SETLKW, &lbp->fl) == -1) {
*error = errno;
if (preverror != *error) {
syslog(LOG_ERR, gettext("Cannot lock (%s): %s"),
bufpath, strerror(*error));
}
nfslog_free_buf(lbp, FALSE);
return (*error);
}
if (fstat(lbp->fd, &sb)) {
*error = errno;
if (preverror != *error) {
syslog(LOG_ERR, gettext("Cannot stat (%s): %s"),
bufpath, strerror(*error));
}
nfslog_free_buf(lbp, FALSE);
return (*error);
}
lbp->filesize = sb.st_size;
lbp->mmap_addr = (intptr_t)mmap(0, lbp->filesize, PROT_READ|PROT_WRITE,
MAP_SHARED|MAP_NORESERVE, lbp->fd, 0);
if (lbp->mmap_addr == (intptr_t)MAP_FAILED) {
lbp->next_rec = 0;
} else {
lbp->next_rec = lbp->mmap_addr;
}
if ((lbp->bh_lrp = nfslog_read_buffer(lbp)) == NULL) {
*error = EIO;
if (preverror != *error) {
syslog(LOG_ERR, gettext(
"error in reading file '%s': %s"),
bufpath, strerror(EIO));
}
nfslog_free_buf(lbp, FALSE);
return (*error);
}
if (!xdr_nfslog_buffer_header(&lbp->bh_lrp->xdrs, &lbp->bh)) {
*error = EIO;
if (preverror != *error) {
syslog(LOG_ERR, gettext(
"error in reading file '%s': %s"),
bufpath, strerror(*error));
}
nfslog_free_buf(lbp, FALSE);
return (*error);
}
lbp->next_rec += lbp->bh.bh_offset;
if (lbp->mmap_addr == (intptr_t)MAP_FAILED && lbp->next_rec != 0) {
(void) lseek(lbp->fd, lbp->next_rec, SEEK_SET);
lbp->last_record_offset = lbp->next_rec;
} else {
lbp->last_record_offset = lbp->next_rec - lbp->mmap_addr;
}
return (*error = 0);
}
static void
nfslog_free_buf(struct nfslog_buf *lbp, int close_quick)
{
XDR xdrs;
int error;
caddr_t buffer;
struct nfslog_lr *lrp, *lrp_next;
struct processed_records *prp, *tprp;
if (lbp->prp) {
if (lbp->last_record_offset == lbp->prp->start_offset) {
lbp->last_record_offset =
lbp->prp->start_offset + lbp->prp->len;
nfslog_rewrite_bufheader(lbp);
}
if (close_quick)
return;
prp = lbp->prp;
do {
tprp = prp->next;
free(prp);
prp = tprp;
} while (lbp->prp != prp);
}
if (close_quick)
return;
if (lbp->lrps != NULL) {
lrp = lbp->lrps;
do {
lrp_next = lrp->next;
nfslog_free_logrecord(lrp, FALSE);
lrp = lrp_next;
} while (lrp != lbp->lrps);
lbp->lrps = NULL;
}
if (lbp->bh.bh_length != 0) {
buffer = (lbp->bh_lrp->buffer != NULL ?
lbp->bh_lrp->buffer : (caddr_t)lbp->mmap_addr);
xdrmem_create(&xdrs, buffer, lbp->bh_lrp->recsize, XDR_FREE);
(void) xdr_nfslog_buffer_header(&xdrs, &lbp->bh);
lbp->bh.bh_length = 0;
}
if (lbp->bh_lrp != NULL) {
free_lrp(lbp->bh_lrp);
lbp->bh_lrp = NULL;
}
if (lbp->mmap_addr != (intptr_t)MAP_FAILED) {
if (munmap((void *)lbp->mmap_addr, lbp->filesize)) {
error = errno;
syslog(LOG_ERR, gettext("munmap failed: %s: %s"),
(lbp->bufpath != NULL ? lbp->bufpath : ""),
strerror(error));
}
lbp->mmap_addr = (intptr_t)MAP_FAILED;
}
if (lbp->fd >= 0) {
lbp->fl.l_type = F_UNLCK;
if (fcntl(lbp->fd, F_SETLK, &lbp->fl) == -1) {
error = errno;
syslog(LOG_ERR,
gettext("Cannot unlock file %s: %s"),
(lbp->bufpath != NULL ? lbp->bufpath : ""),
strerror(error));
}
(void) close(lbp->fd);
lbp->fd = -1;
}
if (lbp->bufpath != NULL)
free(lbp->bufpath);
}
static struct nfslog_lr *
nfslog_read_buffer(struct nfslog_buf *lbp)
{
XDR xdrs;
unsigned int record_size;
struct nfslog_lr *lrp;
char *sizebuf, tbuf[16];
caddr_t buffer;
offset_t next_rec;
lrp = (struct nfslog_lr *)malloc(sizeof (*lrp));
bzero(lrp, sizeof (*lrp));
if (lbp->mmap_addr == (intptr_t)MAP_FAILED) {
if (read(lbp->fd, tbuf, BYTES_PER_XDR_UNIT) <= 0) {
free_lrp(lrp);
return (NULL);
}
sizebuf = tbuf;
} else {
if (lbp->filesize <= lbp->next_rec - lbp->mmap_addr) {
free_lrp(lrp);
return (NULL);
}
sizebuf = (char *)(uintptr_t)lbp->next_rec;
}
xdrmem_create(&xdrs, sizebuf, sizeof (unsigned int), XDR_DECODE);
if (!xdr_u_int(&xdrs, &record_size)) {
free_lrp(lrp);
return (NULL);
}
lrp->recsize = record_size;
next_rec = lbp->next_rec + lrp->recsize;
if (lbp->mmap_addr == (intptr_t)MAP_FAILED) {
if (lbp->filesize < next_rec) {
syslog(LOG_ERR, gettext(
"Last partial record in work buffer %s "
"discarded\n"), lbp->bufpath);
free_lrp(lrp);
return (NULL);
}
if ((lrp->buffer = malloc(lrp->recsize)) == NULL) {
free_lrp(lrp);
return (NULL);
}
bcopy(sizebuf, lrp->buffer, BYTES_PER_XDR_UNIT);
if (read(lbp->fd, &lrp->buffer[BYTES_PER_XDR_UNIT],
lrp->recsize - BYTES_PER_XDR_UNIT) <= 0) {
free_lrp(lrp);
return (NULL);
}
} else if (lbp->filesize < next_rec - lbp->mmap_addr) {
syslog(LOG_ERR, gettext(
"Last partial record in work buffer %s "
"discarded\n"), lbp->bufpath);
free_lrp(lrp);
return (NULL);
}
lrp->next = lrp->prev = lrp;
if (lbp->mmap_addr == (intptr_t)MAP_FAILED)
lrp->f_offset = lbp->next_rec;
else
lrp->f_offset = lbp->next_rec - lbp->mmap_addr;
lrp->record = lbp->next_rec;
lrp->xdrargs = lrp->xdrres = NULL;
lrp->lbp = lbp;
buffer = (lrp->buffer != NULL ? lrp->buffer : (caddr_t)lrp->record);
xdrmem_create(&lrp->xdrs, buffer, lrp->recsize, XDR_DECODE);
lbp->next_rec = next_rec;
return (lrp);
}
static struct nfslog_lr *
remove_lrp_from_lb(struct nfslog_buf *lbp, struct nfslog_lr *lrp)
{
if (lbp->lrps == lrp) {
if (lbp->lrps == lbp->lrps->next) {
lbp->lrps = NULL;
} else {
lbp->lrps = lrp->next;
remque(lrp);
}
} else {
remque(lrp);
}
lbp->num_lrps--;
return (lrp);
}
static void
insert_lrp_to_lb(struct nfslog_buf *lbp, struct nfslog_lr *lrp)
{
int ins_rec_id = lrp->log_record.re_header.rh_rec_id;
struct nfslog_lr *curlrp;
if (lbp->lrps == NULL) {
lbp->lrps = lrp;
} else {
if (ins_rec_id < lbp->lrps->log_record.re_header.rh_rec_id) {
lrp->next = lbp->lrps;
lrp->prev = lbp->lrps->prev;
lbp->lrps->prev->next = lrp;
lbp->lrps->prev = lrp;
lbp->lrps = lrp;
} else {
curlrp = lbp->lrps;
do {
if (ins_rec_id <
curlrp->next->log_record.re_header.rh_rec_id)
break;
curlrp = curlrp->next;
} while (curlrp != lbp->lrps);
if (curlrp == lbp->lrps)
insque(lrp, lbp->lrps->prev);
else
insque(lrp, curlrp);
}
}
lbp->num_lrps++;
}
static void
nfslog_rewrite_bufheader(struct nfslog_buf *lbp)
{
XDR xdrs;
nfslog_buffer_header bh;
#define XBUFSIZE 128
char buffer[XBUFSIZE];
unsigned int wsize;
if (lbp->bh.bh_flags & NFSLOG_BH_OFFSET_OVERFLOW) {
return;
}
bh = lbp->bh;
if ((bh.bh_version > 1) ||
(lbp->last_record_offset - bh.bh_length < UINT32_MAX)) {
bh.bh_offset = lbp->last_record_offset - bh.bh_length;
} else {
bh.bh_flags |= NFSLOG_BH_OFFSET_OVERFLOW;
lbp->bh.bh_flags = bh.bh_flags;
syslog(LOG_ERR, gettext(
"nfslog_rewrite_bufheader: %s: offset does not fit "
"in a 32 bit field\n"), lbp->bufpath);
}
xdrmem_create(&xdrs, buffer, XBUFSIZE, XDR_ENCODE);
if (!xdr_nfslog_buffer_header(&xdrs, &bh)) {
syslog(LOG_ERR, gettext(
"error in re-writing buffer file %s header\n"),
lbp->bufpath);
return;
}
wsize = xdr_getpos(&xdrs);
if (lbp->mmap_addr == (intptr_t)MAP_FAILED) {
(void) lseek(lbp->fd, 0, SEEK_SET);
(void) write(lbp->fd, buffer, wsize);
(void) lseek(lbp->fd, lbp->next_rec, SEEK_SET);
(void) fsync(lbp->fd);
} else {
bcopy(buffer, (void *)lbp->mmap_addr, wsize);
(void) msync((void *)lbp->mmap_addr, wsize, MS_SYNC);
}
}
static void
nfslog_ins_last_rec_processed(struct nfslog_lr *lrp)
{
struct processed_records *prp, *tp;
prp = malloc(sizeof (*prp));
prp->next = prp->prev = prp;
prp->start_offset = lrp->f_offset;
prp->len = lrp->recsize;
prp->num_recs = 1;
lrp->lbp->num_pr_queued++;
if (lrp->lbp->prp == NULL) {
lrp->lbp->prp = prp;
} else {
tp = lrp->lbp->prp;
do {
if (prp->start_offset < tp->start_offset)
break;
tp = tp->next;
} while (tp != lrp->lbp->prp);
insque(prp, tp->prev);
if (tp == lrp->lbp->prp && prp->start_offset < tp->start_offset)
lrp->lbp->prp = prp;
if (prp->start_offset ==
(prp->prev->start_offset + prp->prev->len)) {
tp = prp->prev;
remque(prp);
tp->len += prp->len;
tp->num_recs += prp->num_recs;
free(prp);
prp = tp;
}
if (prp->next->start_offset ==
(prp->start_offset + prp->len)) {
prp->len += prp->next->len;
prp->num_recs += prp->next->num_recs;
tp = prp->next;
remque(tp);
free(tp);
}
}
if (lrp->lbp->num_pr_queued > MAX_RECS_TO_DELAY) {
prp = lrp->lbp->prp;
if (lrp->lbp->last_record_offset ==
prp->start_offset) {
lrp->lbp->last_record_offset =
prp->start_offset + prp->len;
nfslog_rewrite_bufheader(lrp->lbp);
tp = prp->next;
if (tp != prp)
remque(prp);
else
tp = NULL;
lrp->lbp->prp = tp;
lrp->lbp->num_pr_queued -= prp->num_recs;
free(prp);
}
}
}
struct nfslog_lr *
nfslog_get_logrecord(struct nfslog_buf *lbp)
{
unsigned int next_rec_id = lbp->last_rec_id + 1;
struct nfslog_lr *lrp = NULL;
if (lbp->lrps != NULL) {
if (lbp->lrps->log_record.re_header.rh_rec_id == next_rec_id) {
lrp = remove_lrp_from_lb(lbp, lbp->lrps);
lbp->last_rec_id = lrp->log_record.re_header.rh_rec_id;
} else {
if (next_rec_id <
lbp->lrps->log_record.re_header.rh_rec_id &&
((lbp->lrps->log_record.re_header.rh_rec_id -
next_rec_id) > (MAXINT / 2))) {
lrp = remove_lrp_from_lb(lbp, lbp->lrps);
lbp->last_rec_id =
lrp->log_record.re_header.rh_rec_id;
}
}
}
while (lrp == NULL) {
if (lbp->num_lrps >= MAX_LRS_READ_AHEAD) {
lrp = remove_lrp_from_lb(lbp, lbp->lrps);
lbp->last_rec_id = lrp->log_record.re_header.rh_rec_id;
break;
}
if ((lrp = nfslog_read_buffer(lbp)) == NULL) {
if (lbp->lrps != NULL) {
lrp = remove_lrp_from_lb(lbp, lbp->lrps);
lbp->last_rec_id =
lrp->log_record.re_header.rh_rec_id;
} else {
return (NULL);
}
} else {
if (!xdr_nfslog_request_record(&lrp->xdrs,
&lrp->log_record)) {
nfslog_free_logrecord(lrp, FALSE);
return (NULL);
}
if (lrp->log_record.re_header.rh_rec_id <=
next_rec_id) {
lbp->last_rec_id =
lrp->log_record.re_header.rh_rec_id;
} else {
insert_lrp_to_lb(lbp, lrp);
lrp = NULL;
}
}
}
return (lrp);
}
void
nfslog_free_logrecord(struct nfslog_lr *lrp, bool_t processing_complete)
{
caddr_t buffer;
nfslog_request_record *reqrec;
if (processing_complete) {
nfslog_ins_last_rec_processed(lrp);
}
reqrec = &lrp->log_record;
buffer = (lrp->buffer != NULL ? lrp->buffer : (caddr_t)lrp->record);
xdrmem_create(&lrp->xdrs, buffer, lrp->recsize, XDR_FREE);
(void) xdr_nfslog_request_record(&lrp->xdrs, reqrec);
if (lrp->xdrargs != NULL && reqrec->re_rpc_arg)
(*lrp->xdrargs)(&lrp->xdrs, reqrec->re_rpc_arg);
if (reqrec->re_rpc_arg)
free(reqrec->re_rpc_arg);
if (lrp->xdrres != NULL && reqrec->re_rpc_res)
(*lrp->xdrres)(&lrp->xdrs, reqrec->re_rpc_res);
if (reqrec->re_rpc_res)
free(reqrec->re_rpc_res);
free_lrp(lrp);
}
static void
free_lrp(struct nfslog_lr *lrp)
{
if (lrp->buffer != NULL)
free(lrp->buffer);
free(lrp);
}
void
nfslog_opaque_print_buf(void *buf, int len, char *outbuf, int *outbufoffsetp,
int maxoffset)
{
int i, j;
uint_t *ip;
uchar_t *u_buf = (uchar_t *)buf;
int outbufoffset = *outbufoffsetp;
outbufoffset += sprintf(&outbuf[outbufoffset], " \"");
if (len <= sizeof (int)) {
for (j = 0; (j < len) && (outbufoffset < maxoffset);
j++, u_buf++)
outbufoffset += sprintf(&outbuf[outbufoffset],
"%02x", *u_buf);
return;
}
j = (int)((uintptr_t)buf % sizeof (int));
i = 0;
if (j > 0) {
i = sizeof (int) - j;
for (; (j < sizeof (int)) && (outbufoffset < maxoffset);
j++, u_buf++)
outbufoffset += sprintf(&outbuf[outbufoffset],
"%02x", *u_buf);
}
ip = (uint_t *)u_buf;
for (; ((i + sizeof (int)) <= len) && (outbufoffset < maxoffset);
i += sizeof (int), ip++) {
outbufoffset += sprintf(&outbuf[outbufoffset], " %08x", *ip);
}
if (i < len) {
u_buf = (uchar_t *)ip;
if (i > j)
outbufoffset += sprintf(&outbuf[outbufoffset], " ");
for (; (i < len) && (outbufoffset < maxoffset); i++, u_buf++) {
outbufoffset += sprintf(&outbuf[outbufoffset],
"%02x", *u_buf);
}
}
if (outbufoffset < maxoffset)
outbufoffset += sprintf(&outbuf[outbufoffset], "\"");
*outbufoffsetp = outbufoffset;
}