2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A/*
2N/A * Event Log Service RPC (LOGR) interface definition.
2N/A */
2N/A#include <sys/utsname.h>
2N/A#include <unistd.h>
2N/A#include <strings.h>
2N/A#include <smbsrv/libsmb.h>
2N/A#include <smb/nmpipes.h>
2N/A#include <smbsrv/libntsvcs.h>
2N/A#include <smbsrv/ndl/eventlog.ndl>
2N/A
2N/A
2N/A#define LOGR_FWD +1
2N/A#define LOGR_REW -1
2N/A#define LOGR_RECORD_SIGNATURE 0x654C664C
2N/A
2N/A#define LOGR_PRI(p) ((p) & LOG_PRIMASK)
2N/A#define LOGR_WNSTRLEN(S) ((strlen((S)) + 1) * sizeof (smb_wchar_t))
2N/A
2N/A#define LOGR_MSG_DWORD_OFFSET 12
2N/A#define LOGR_MSG_WORD_OFFSET 4
2N/A
2N/A/*
2N/A * READ flags for EventLogRead
2N/A *
2N/A * EVENTLOG_SEEK_READ
2N/A * The read operation proceeds from the record specified by the
2N/A * dwRecordOffset parameter. This flag cannot be used with
2N/A * EVENTLOG_SEQUENTIAL_READ.
2N/A *
2N/A * EVENTLOG_SEQUENTIAL_READ
2N/A * The read operation proceeds sequentially from the last call to the
2N/A * ReadEventLog function using this handle. This flag cannot be used
2N/A * with EVENTLOG_SEEK_READ.
2N/A *
2N/A * If the buffer is large enough, more than one record can be read at
2N/A * the specified seek position; you must specify one of the following
2N/A * flags to indicate the direction for successive read operations.
2N/A *
2N/A * EVENTLOG_FORWARDS_READ
2N/A * The log is read in chronological order. This flag cannot be used
2N/A * with EVENTLOG_BACKWARDS_READ.
2N/A *
2N/A * EVENTLOG_BACKWARDS_READ
2N/A * The log is read in reverse chronological order. This flag cannot be
2N/A * used with EVENTLOG_FORWARDS_READ.
2N/A */
2N/A#define EVENTLOG_SEQUENTIAL_READ 0x0001
2N/A#define EVENTLOG_SEEK_READ 0x0002
2N/A#define EVENTLOG_FORWARDS_READ 0x0004
2N/A#define EVENTLOG_BACKWARDS_READ 0x0008
2N/A
2N/A/*
2N/A * The types of events that can be logged.
2N/A */
2N/A#define EVENTLOG_SUCCESS 0x0000
2N/A#define EVENTLOG_ERROR_TYPE 0x0001
2N/A#define EVENTLOG_WARNING_TYPE 0x0002
2N/A#define EVENTLOG_INFORMATION_TYPE 0x0004
2N/A#define EVENTLOG_AUDIT_SUCCESS 0x0008
2N/A#define EVENTLOG_AUDIT_FAILURE 0x0010
2N/A
2N/A/*
2N/A * Event Identifiers
2N/A *
2N/A * Event identifiers uniquely identify a particular event. Each event
2N/A * source can define its own numbered events and the description strings
2N/A * to which they are mapped. Event viewers can present these strings to
2N/A * the user. They should help the user understand what went wrong and
2N/A * suggest what actions to take. Direct the description at users solving
2N/A * their own problems, not at administrators or support technicians.
2N/A * Make the description clear and concise and avoid culture-specific
2N/A * phrases.
2N/A *
2N/A * The following diagram illustrates the format of an event identifier.
2N/A *
2N/A * 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
2N/A * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
2N/A * +---+-+-+-----------------------+-------------------------------+
2N/A * |Sev|C|R| Facility | Code |
2N/A * +---+-+-+-----------------------+-------------------------------+
2N/A *
2N/A * Sev
2N/A * Indicates the severity. This is one of the following values:
2N/A * 00 - Success
2N/A * 01 - Informational
2N/A * 10 - Warning
2N/A * 11 - Error
2N/A *
2N/A * C
2N/A * Indicates a customer code (1) or a system code (0).
2N/A * R
2N/A * Reserved bit.
2N/A * Facility
2N/A * Facility code.
2N/A * Code
2N/A * Status code for the facility.
2N/A */
2N/A#define EVENTID_SEVERITY_SUCCESS 0x00000000
2N/A#define EVENTID_SEVERITY_INFO 0x40000000
2N/A#define EVENTID_SEVERITY_WARNING 0x80000000
2N/A#define EVENTID_SEVERITY_ERROR 0xC0000000
2N/A
2N/A#define EVENTID_SYSTEM_CODE 0x00000000
2N/A#define EVENTID_CUSTOMER_CODE 0x20000000
2N/A
2N/Astatic int logr_s_EventLogClose(void *, ndr_xa_t *);
2N/Astatic int logr_s_EventLogQueryCount(void *, ndr_xa_t *);
2N/Astatic int logr_s_EventLogGetOldestRec(void *, ndr_xa_t *);
2N/Astatic int logr_s_EventLogOpen(void *, ndr_xa_t *);
2N/Astatic int logr_s_EventLogRead(void *, ndr_xa_t *);
2N/A
2N/Astatic ndr_stub_table_t logr_stub_table[] = {
2N/A { logr_s_EventLogClose, LOGR_OPNUM_EventLogClose },
2N/A { logr_s_EventLogQueryCount, LOGR_OPNUM_EventLogQueryCount },
2N/A { logr_s_EventLogGetOldestRec, LOGR_OPNUM_EventLogGetOldestRec },
2N/A { logr_s_EventLogOpen, LOGR_OPNUM_EventLogOpen },
2N/A { logr_s_EventLogRead, LOGR_OPNUM_EventLogRead },
2N/A {0}
2N/A};
2N/A
2N/Astatic ndr_service_t logr_service = {
2N/A "LOGR", /* name */
2N/A "Event Log Service", /* desc */
2N/A "\\eventlog", /* endpoint */
2N/A PIPE_NTSVCS, /* sec_addr_port */
2N/A "82273fdc-e32a-18c3-3f78-827929dc23ea", 0, /* abstract */
2N/A NDR_TRANSFER_SYNTAX_UUID, 2, /* transfer */
2N/A 0, /* no bind_instance_size */
2N/A 0, /* no bind_req() */
2N/A 0, /* no unbind_and_close() */
2N/A 0, /* use generic_call_stub() */
2N/A &TYPEINFO(logr_interface), /* interface ti */
2N/A logr_stub_table /* stub_table */
2N/A};
2N/A
2N/A/*
2N/A * logr_initialize
2N/A *
2N/A * This function registers the LOGR RPC interface with the RPC runtime
2N/A * library. It must be called in order to use either the client side
2N/A * or the server side functions.
2N/A */
2N/Avoid
2N/Alogr_initialize(void)
2N/A{
2N/A (void) ndr_svc_register(&logr_service);
2N/A logr_init();
2N/A}
2N/A
2N/Avoid
2N/Alogr_finalize(void)
2N/A{
2N/A logr_fini();
2N/A}
2N/A
2N/A/*
2N/A * logr_hdlookup
2N/A *
2N/A * Handle lookup wrapper to validate the local service and/or manager context.
2N/A */
2N/Astatic ndr_handle_t *
2N/Alogr_hdlookup(ndr_xa_t *mxa, ndr_hdid_t *id)
2N/A{
2N/A ndr_handle_t *hd;
2N/A logr_context_t *ctx;
2N/A
2N/A if ((hd = ndr_hdlookup(mxa, id)) == NULL)
2N/A return (NULL);
2N/A
2N/A if ((ctx = (logr_context_t *)hd->nh_data) == NULL)
2N/A return (NULL);
2N/A
2N/A if (ctx->lc_source_name == NULL)
2N/A return (NULL);
2N/A
2N/A return (hd);
2N/A}
2N/A
2N/A/*
2N/A * logr_context_data_free
2N/A *
2N/A * Callback to free the context data associated with local service
2N/A * and/or manager context.
2N/A */
2N/Astatic void
2N/Alogr_context_data_free(void *ctxp)
2N/A{
2N/A logr_context_t *ctx = (logr_context_t *)ctxp;
2N/A
2N/A if (ctx == NULL)
2N/A return;
2N/A
2N/A free(ctx->lc_source_name);
2N/A free(ctx->lc_cached_read_data->rd_log);
2N/A free(ctx->lc_cached_read_data);
2N/A free(ctx);
2N/A ctx = NULL;
2N/A}
2N/A
2N/A/*
2N/A * logr_hdalloc
2N/A *
2N/A * Handle allocation wrapper to setup the local manager context.
2N/A */
2N/Astatic ndr_hdid_t *
2N/Alogr_hdalloc(ndr_xa_t *mxa, char *logname)
2N/A{
2N/A logr_context_t *ctx;
2N/A
2N/A if ((ctx = malloc(sizeof (logr_context_t))) == NULL)
2N/A return (NULL);
2N/A bzero(ctx, sizeof (logr_context_t));
2N/A
2N/A ctx->lc_source_name = strdup(logname);
2N/A if (ctx->lc_source_name == NULL) {
2N/A free(ctx);
2N/A return (NULL);
2N/A }
2N/A
2N/A if (logr_get_snapshot(ctx) != 0) {
2N/A free(ctx->lc_source_name);
2N/A free(ctx);
2N/A return (NULL);
2N/A }
2N/A
2N/A return (ndr_hdalloc(mxa, ctx));
2N/A}
2N/A
2N/A/*
2N/A * logr_s_EventLogClose
2N/A *
2N/A * This is a request to close the LOGR interface specified by handle.
2N/A * Free the handle and associated resources, and zero out the result
2N/A * handle for the client.
2N/A */
2N/Astatic int
2N/Alogr_s_EventLogClose(void *arg, ndr_xa_t *mxa)
2N/A{
2N/A struct logr_EventLogClose *param = arg;
2N/A ndr_hdid_t *id = (ndr_hdid_t *)&param->handle;
2N/A ndr_handle_t *hd;
2N/A
2N/A if ((hd = ndr_hdlookup(mxa, id)) == NULL) {
2N/A bzero(&param->result_handle, sizeof (logr_handle_t));
2N/A param->status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE);
2N/A return (NDR_DRC_OK);
2N/A }
2N/A logr_context_data_free(hd->nh_data);
2N/A ndr_hdfree(mxa, id);
2N/A
2N/A bzero(&param->result_handle, sizeof (logr_handle_t));
2N/A param->status = NT_STATUS_SUCCESS;
2N/A
2N/A return (NDR_DRC_OK);
2N/A}
2N/A
2N/A/*
2N/A * logr_s_EventLogOpen
2N/A *
2N/A * Open the event log. Not supported yet.
2N/A */
2N/A/*ARGSUSED*/
2N/Astatic int
2N/Alogr_s_EventLogOpen(void *arg, ndr_xa_t *mxa)
2N/A{
2N/A struct logr_EventLogOpen *param = arg;
2N/A ndr_hdid_t *id = NULL;
2N/A ndr_handle_t *hd;
2N/A char *log_name = NULL;
2N/A
2N/A if (!ndr_is_admin(mxa)) {
2N/A bzero(&param->handle, sizeof (logr_handle_t));
2N/A param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);
2N/A return (NDR_DRC_OK);
2N/A }
2N/A
2N/A if (param->log_name.length != 0)
2N/A log_name = (char *)param->log_name.str;
2N/A
2N/A if (!logr_is_supported(log_name)) {
2N/A bzero(&param->handle, sizeof (logr_handle_t));
2N/A param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);
2N/A return (NDR_DRC_OK);
2N/A }
2N/A
2N/A id = logr_hdalloc(mxa, log_name);
2N/A if (id && ((hd = logr_hdlookup(mxa, id)) != NULL)) {
2N/A hd->nh_data_free = logr_context_data_free;
2N/A bcopy(id, &param->handle, sizeof (logr_handle_t));
2N/A param->status = NT_STATUS_SUCCESS;
2N/A } else {
2N/A bzero(&param->handle, sizeof (logr_handle_t));
2N/A param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);
2N/A }
2N/A
2N/A return (NDR_DRC_OK);
2N/A}
2N/A
2N/A/*
2N/A * logr_s_EventLogQueryCount
2N/A *
2N/A * take a snapshot from system log, assign it to the given handle.
2N/A * return number of log entries in the snapshot as result of RPC
2N/A * call.
2N/A */
2N/Astatic int
2N/Alogr_s_EventLogQueryCount(void *arg, ndr_xa_t *mxa)
2N/A{
2N/A struct logr_EventLogQueryCount *param = arg;
2N/A ndr_hdid_t *id = (ndr_hdid_t *)&param->handle;
2N/A ndr_handle_t *hd;
2N/A logr_context_t *ctx;
2N/A logr_read_data_t *data;
2N/A
2N/A if ((hd = logr_hdlookup(mxa, id)) == NULL) {
2N/A param->status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE);
2N/A return (NDR_DRC_OK);
2N/A }
2N/A
2N/A ctx = (logr_context_t *)hd->nh_data;
2N/A data = ctx->lc_cached_read_data;
2N/A
2N/A param->rec_num = data->rd_tot_recnum;
2N/A param->status = NT_STATUS_SUCCESS;
2N/A return (NDR_DRC_OK);
2N/A}
2N/A
2N/A/*
2N/A * logr_s_EventLogGetOldestRec
2N/A *
2N/A * Return oldest record number in the snapshot as result of RPC call.
2N/A */
2N/Astatic int
2N/Alogr_s_EventLogGetOldestRec(void *arg, ndr_xa_t *mxa)
2N/A{
2N/A struct logr_EventLogGetOldestRec *param = arg;
2N/A ndr_hdid_t *id = (ndr_hdid_t *)&param->handle;
2N/A ndr_handle_t *hd;
2N/A logr_context_t *ctx;
2N/A logr_read_data_t *data;
2N/A
2N/A if ((hd = logr_hdlookup(mxa, id)) == NULL) {
2N/A param->status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE);
2N/A return (NDR_DRC_OK);
2N/A }
2N/A
2N/A ctx = (logr_context_t *)hd->nh_data;
2N/A data = ctx->lc_cached_read_data;
2N/A
2N/A param->oldest_rec = data->rd_log->li_idx - data->rd_tot_recnum + 1;
2N/A
2N/A param->status = NT_STATUS_SUCCESS;
2N/A return (NDR_DRC_OK);
2N/A}
2N/A
2N/A/*
2N/A * logr_set_event_typeid
2N/A *
2N/A * Map the local system log priority to the event type and event ID
2N/A * for Windows events.
2N/A */
2N/Avoid
2N/Alogr_set_event_typeid(int le_pri, WORD *etype, DWORD *eid)
2N/A{
2N/A switch (LOGR_PRI(le_pri)) {
2N/A case LOG_EMERG:
2N/A case LOG_ALERT:
2N/A case LOG_CRIT:
2N/A case LOG_ERR:
2N/A *eid = EVENTID_SEVERITY_ERROR;
2N/A *etype = EVENTLOG_ERROR_TYPE;
2N/A break;
2N/A case LOG_WARNING:
2N/A *eid = EVENTID_SEVERITY_WARNING;
2N/A *etype = EVENTLOG_WARNING_TYPE;
2N/A break;
2N/A case LOG_NOTICE:
2N/A case LOG_INFO:
2N/A case LOG_DEBUG:
2N/A *eid = EVENTID_SEVERITY_INFO;
2N/A *etype = EVENTLOG_INFORMATION_TYPE;
2N/A break;
2N/A default:
2N/A *eid = EVENTID_SEVERITY_SUCCESS;
2N/A *etype = EVENTLOG_SUCCESS;
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * logr_get_entry
2N/A *
2N/A * Gets a log entry.
2N/A */
2N/Astatic logr_entry_t *
2N/Alogr_get_entry(logr_info_t *linfo, int entno)
2N/A{
2N/A return (&linfo->li_entry[entno]);
2N/A}
2N/A
2N/A/*
2N/A * logr_set_logrecord
2N/A *
2N/A * Fill a Windows event record based on a local system log record.
2N/A */
2N/Astatic void
2N/Alogr_set_logrecord(char *src_name, logr_entry_t *le,
2N/A DWORD recno, logr_record_t *rec)
2N/A{
2N/A int srcname_len = 0, hostname_len = 0, len;
2N/A uint32_t str_offs, sh_len, r_length, r_resv;
2N/A uint16_t r_strnum;
2N/A smb_wchar_t wcs_hostname[MAXHOSTNAMELEN];
2N/A smb_wchar_t wcs_srcname[SYS_NMLN * 2];
2N/A
2N/A (void) ndr_mbstowcs(NULL, wcs_srcname, src_name,
2N/A strlen(src_name) + 1);
2N/A srcname_len = LOGR_WNSTRLEN(src_name);
2N/A
2N/A /* Because, Solaris allows remote logging, need to get hostname here */
2N/A (void) ndr_mbstowcs(NULL, wcs_hostname, le->le_hostname,
2N/A strlen(le->le_hostname) + 1);
2N/A hostname_len = LOGR_WNSTRLEN(le->le_hostname);
2N/A
2N/A sh_len = srcname_len + hostname_len;
2N/A str_offs = LOGR_MSG_DWORD_OFFSET * sizeof (DWORD) +
2N/A LOGR_MSG_WORD_OFFSET * sizeof (WORD) + sh_len;
2N/A
2N/A r_length = sizeof (logr_record_t);
2N/A r_resv = LOGR_RECORD_SIGNATURE;
2N/A r_strnum = 1;
2N/A
2N/A rec->Length1 = LE_IN32(&r_length);
2N/A rec->Reserved = LE_IN32(&r_resv);
2N/A rec->RecordNumber = LE_IN32(&recno);
2N/A rec->TimeGenerated = LE_IN32(&le->le_timestamp.tv_sec);
2N/A rec->TimeWritten = LE_IN32(&le->le_timestamp.tv_sec);
2N/A logr_set_event_typeid(le->le_pri, &rec->EventType, &rec->EventID);
2N/A rec->EventID = LE_IN32(&rec->EventID);
2N/A rec->EventType = LE_IN16(&rec->EventType);
2N/A rec->NumStrings = LE_IN16(&r_strnum);
2N/A rec->EventCategory = 0;
2N/A rec->ReservedFlags = 0;
2N/A rec->ClosingRecordNumber = 0;
2N/A rec->StringOffset = LE_IN32(&str_offs);
2N/A rec->UserSidLength = 0;
2N/A rec->UserSidOffset = 0;
2N/A rec->DataLength = 0;
2N/A rec->DataOffset = 0;
2N/A
2N/A bzero(rec->info, LOGR_MAXENTRYLEN);
2N/A (void) memcpy(rec->info, wcs_srcname, srcname_len);
2N/A (void) memcpy(rec->info + srcname_len, wcs_hostname, hostname_len);
2N/A
2N/A len = strlen(le->le_msg) + 1;
2N/A if (len > 0)
2N/A /*LINTED E_BAD_PTR_CAST_ALIGN*/
2N/A (void) ndr_mbstowcs(NULL, (smb_wchar_t *)(rec->info + sh_len),
2N/A le->le_msg, len);
2N/A
2N/A rec->Length2 = LE_IN32(&r_length);
2N/A}
2N/A
2N/A/*
2N/A * logr_s_EventLogRead
2N/A *
2N/A * Reads a whole number of entries from system log. The function can
2N/A * read log entries in chronological or reverse chronological order.
2N/A */
2N/Astatic int
2N/Alogr_s_EventLogRead(void *arg, ndr_xa_t *mxa)
2N/A{
2N/A struct logr_EventLogRead *param = arg;
2N/A ndr_hdid_t *id = (ndr_hdid_t *)&param->handle;
2N/A ndr_handle_t *hd;
2N/A logr_read_data_t *rdata;
2N/A logr_entry_t *le;
2N/A DWORD ent_no, ent_num, ent_remain;
2N/A logr_record_t *rec;
2N/A BYTE *buf;
2N/A int dir, ent_per_req, iter;
2N/A logr_context_t *ctx;
2N/A
2N/A if ((hd = logr_hdlookup(mxa, id)) == NULL) {
2N/A param->status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE);
2N/A return (NDR_DRC_OK);
2N/A }
2N/A
2N/A ctx = (logr_context_t *)hd->nh_data;
2N/A rdata = ctx->lc_cached_read_data;
2N/A if (rdata == NULL) {
2N/A param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY);
2N/A return (NDR_DRC_OK);
2N/A }
2N/A
2N/A dir = (param->read_flags & EVENTLOG_FORWARDS_READ) ?
2N/A LOGR_FWD : LOGR_REW;
2N/A
2N/A if (param->read_flags & EVENTLOG_SEEK_READ)
2N/A rdata->rd_last_sentrec = param->rec_offset;
2N/A else if (rdata->rd_first_read)
2N/A /*
2N/A * set last record number which is read for
2N/A * the first iteration of sequential read.
2N/A */
2N/A rdata->rd_last_sentrec = (dir == LOGR_FWD)
2N/A ? (rdata->rd_log->li_idx - rdata->rd_tot_recnum)
2N/A : rdata->rd_log->li_idx;
2N/A
2N/A ent_remain = (dir == LOGR_FWD)
2N/A ? (rdata->rd_tot_recnum - rdata->rd_last_sentrec)
2N/A : rdata->rd_last_sentrec;
2N/A
2N/A /*
2N/A * function should return as many whole log entries as
2N/A * will fit in the buffer; it should not return partial
2N/A * entries, even if there is room in the buffer.
2N/A */
2N/A ent_per_req = param->nbytes_to_read / sizeof (logr_record_t);
2N/A if (ent_remain > ent_per_req)
2N/A ent_remain = ent_per_req;
2N/A
2N/A if (ent_remain == 0) {
2N/A /*
2N/A * Send this error to Windows client so that it
2N/A * can figure out that there is no more record
2N/A * to read.
2N/A */
2N/A param->buf = NDR_STRDUP(mxa, "");
2N/A param->sent_size = 0;
2N/A param->status = NT_SC_ERROR(NT_STATUS_END_OF_FILE);
2N/A return (NDR_DRC_OK);
2N/A }
2N/A
2N/A param->buf = NDR_MALLOC(mxa, param->nbytes_to_read);
2N/A buf = (BYTE *)param->buf;
2N/A
2N/A for (ent_num = 0, ent_no = rdata->rd_last_sentrec;
2N/A ent_num < ent_remain; ent_num++, ent_no += dir) {
2N/A
2N/A iter = ent_no & LOGR_NMSGMASK;
2N/A if (dir == LOGR_REW)
2N/A iter = (ent_no - 1) & LOGR_NMSGMASK;
2N/A
2N/A le = logr_get_entry(rdata->rd_log, iter);
2N/A
2N/A /*LINTED E_BAD_PTR_CAST_ALIGN*/
2N/A rec = (logr_record_t *)buf;
2N/A logr_set_logrecord(ctx->lc_source_name, le, ent_no, rec);
2N/A buf += sizeof (logr_record_t);
2N/A }
2N/A
2N/A rdata->rd_last_sentrec = ent_no;
2N/A rdata->rd_first_read = 0;
2N/A
2N/A param->sent_size = sizeof (logr_record_t) * ent_remain;
2N/A param->status = NT_STATUS_SUCCESS;
2N/A
2N/A return (NDR_DRC_OK);
2N/A}