.\"
.\" $Id: write_log.3,v 1.1 2000/07/27 16:59:03 alaffin Exp $
.\"
.\" Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.
.\"
.\" This program is free software; you can redistribute it and/or modify it
.\" under the terms of version 2 of the GNU General Public License as
.\" published by the Free Software Foundation.
.\"
.\" This program is distributed in the hope that it would be useful, but
.\" WITHOUT ANY WARRANTY; without even the implied warranty of
.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
.\"
.\" Further, this software is distributed without any warranty that it is
.\" free of the rightful claim of any third person regarding infringement
.\" or the like.  Any license provided herein, whether implied or
.\" otherwise, applies only to this software file.  Patent licenses, if
.\" any, provided herein do not apply to combinations of this program with
.\" other software, or any other product whatsoever.
.\"
.\" You should have received a copy of the GNU General Public License along
.\" with this program; if not, write the Free Software Foundation, Inc.,
.\" 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
.\"
.\" Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
.\" Mountain View, CA  94043, or:
.\"
.\" http://www.sgi.com
.\"
.\" For further information regarding this notice, see:
.\"
.\" http://oss.sgi.com/projects/GenInfo/NoticeExplan/
.\"
.TH WRITE_LOG 3 07/25/2000 "Linux Test Project"
.SH NAME
write_log \- a set of routines for logging writes to a history file
.SH SYNOPSIS
.nf
\fB#include "write_log.h"\fP

\fBint wlog_open(struct wlog_file *\fIwfile\fB, int \fItrunc\fB, int \fImode\fB);\fR
\fBint wlog_close(struct wlog_file *\fIwfile\fB);\fR
\fBint wlog_record_write(struct wlog_file *\fIwfile\fB, struct wlog_rec *\fIwrec\fB, long \fIoffset\fB);\fR
\fBint wlog_scan_backward(struct wlog_file *\fIwfile\fB, int \fInrecs\fB, int (*func)(struct wlog_rec *\fIrec\fB), long \fIdata\fB);\fR

\fBchar *Wlog_Error_String;\fR
.fi
.SH DESCRIPTION

The write_log package is a set of routines for creating a history file
of write operations done to a set of files.

It is assumed that the actual pattern written to the file will be
repeated occurrences of a string whose length is no greater than
WLOG_MAX_PATTERN.  See the pattern(3) man
page for routines to conveniently generate buffers of this kind.

wlog_open() initializes the history file contained in wfile->w_file, and
fills in the wfile structure.  If trunc is non-zero, and existing history
file by the same name will be truncated.  If no history file exists, it
will be created with the specified mode.

wlog_close() releases any resources associated with the given wfile.  Use
of wfile after this point is undefined until it is initialized again with
wlog_open().

wlog_record_write() is the main routine for putting a wlog_rec into the
history file.  The caller is responsible for supplying a fully initialized
wlog_rec structure.  If offset is < 0, the record will be appended to the
end of the history file.  If offset is >= 0, the record will be written
at the indicated offset.  This, along with the w_done field in the wlog_rec
structure, provide a mechanism for 'pre-logging' a write, doing the write
operation, and then overlaying the original record with the w_done flag
set to 1.  This is useful for async writes which may not complete in a
timely manner.  It is also useful for seeing which write operations were
pending at the time of a system crash - the ones whose w_done flag is 0 have
not yet been verified as complete.
The return value from wlog_record_write() is the offset
in the history file at which the record was written.

wlog_scan_backward() can be used to conveniently scan a write history file.
The routine scans the file in reverse order (ie. first record written is
scanned last).  For every record found, the user supplied function is called
with 2 parameters:  the read record, and an arbitrary word passed in by the
user.  This word may be interpreted however the user desires.  If nrecs is
greater than 0, up to nrecs will be scanned.  The user supplied function should
return 1 of the following:  WLOG_STOP_SCAN, or WLOG_CONTINUE_SCAN.
WLOG_STOP_SCAN provides a way for the user supplied function to prematurely
abort the scanning process.  WLOG_CONTINUE_SCAN instructs wlog_scan_backward()
to continue scanning the next record.

In order for the history file to be effective, some basic rules must
be followed by the programs using the history mechanism:

.in +.5i
.ll -.5i
The area of the data file being written must be locked from
before the write operation, until after the wlog_record_write()
is complete.  This is necessary to 'synchronize' simultaneous
writes to the same area of a file.  Note that the entire file
does not need to be locked, only the portion being written to.
If the calling program can guarantee that there will never be more
than 1 process writing to the same area of a file at the same time,
locking is not necessary.  (Note:  UNICOS Shared File Systems do not support
record locking.  The whole file is silently locked.)

Pathnames in the history file (w_path field) should be full
pathnames if possible.  This allows validation tools to be
able to find the test files without having to take working
directory considerations into account.
.ll +.5i
.in -.5i

.nf
\fC
.ta .25i +.5i +1i
/*
 * write log file data type.  wlog_open() initializes this structure
 * which is then passed around to the various wlog_xxx routines.
 */

struct wlog_file {
	int	w_afd;	/* append fd */
	int	w_rfd;	/* random-access fd */
	char	w_file[1024];	/* name of the write_log */
};
\fR
.DT
.fi

.nf
\fC
.ta .25i +.5i +2.0i
/*
 * User view of a history file record.  Note that this is not
 * necessarily how the data is formatted on disk (significant
 * compression occurs), so don't expect to od(1) the history file and
 * see things formatted this way.  See the file write_log.h for comments
 * on how the data is actually written to disk.
 */

struct wlog_rec {
	int	w_pid;	/* pid doing the write */
	int	w_offset;	/* file offset */
	int	w_nbytes;	/* # bytes written */
	int	w_oflags;	/* low-order open() flags */
	int	w_done;	/* 1 if io confirmed done */
	int	w_async;	/* 1 if async write (writea) */

	char	w_host[WLOG_MAX_HOST+1];	/* host doing write */
	int	w_hostlen;	/* host name length */
	char	w_path[WLOG_MAX_PATH+1];	/* file written to */
	int	w_pathlen;	/* file name length */
	char	w_pattern[WLOG_MAX_PATTERN+1];	/* pattern written */
	int	w_patternlen;	/* pattern length */
};
\fR
.DT
.fi

Note:  The history files can become very large very quickly if a lot of
processes are logging writes.  This is especially apt to happen if long
pathnames or patterns are used.  This is because the w_host, w_path, and
w_pattern fields are variable length fields when stored on disk.  Thus, use
short pathnames and patterns to minimize the size of the history file.  If
any of the w_path, w_pattern, or w_host fields are not important to you, set
the respective length field to 0 in the wlog_rec structure.

.SH EXAMPLES
This is a simple example of how to initialize a history file, and
record a write to it.

.nf
#include "write_log.h"

main()
{
	struct wlog_rec wrec;
	struct wlog_file wfile;

	...
	strcpy(wfile.w_file, hisfile);
	if (wlog_open(&wfile, 1, 0666) < 0) {
		fprintf("wlog_open failed\n");
		exit(2);
	}

	...

	wrec.w_pid = getpid();
	wrec.w_offset = write_offset;
	wrec.w_nbytes = nbytes;
	wrec.w_oflags = open_flags;
	wrec.w_done = 0;
	wrec.w_async = 0;
	wrec.w_host = 0;             /* don't care about host */

	wrec.w_pathlen = sprintf(wrec.w_path, "%s", path);
	wrec.w_patternlen = sprintf(wrec.w_pattern, "%s", pattern);

	pattern_fill(buf, nbytes, pattern, strlen(pattern), 0);

	... lock fd here ...

	log_offset = wlog_record_write(&wfile, &wrec, -1);
	write(fd, buf, nbytes);
	wrec.w_done = 1;
	wlog_record_write(&wfile, &wrec, log_offset);

	... unlock fd here ...

	...

	/*
	 * Scan the logfile printing records for the file in 'path'.
	 */

	wlog_scan_backward(&wfile, 0, print_log_record, (long)path);
}

int
print_log_record(record, data)
struct wlog_rec	*record;
long		data;
{
	char	*path;

	path = (char *)data;
	if (strcmp(record->w_path, path) == 0) {
		printf("write() of %d bytes to %s at offset %d by pid %d\n",
			record->w_nbytes, record->path, record->w_offset, record->w_pid);
	}

	return WLOG_CONTINUE_SCAN;
}

.fi
.SH "SEE ALSO"
pattern(3).
.SH DIAGNOSTICS
All routines return a value < 0 on failure, and >= 0 on success.  Error
messages can be accessed through Wlog_Error_String.
.SH BUGS
None known.