NAME
write_log - a set of routines for logging writes to a history file
SYNOPSIS
#include "write_log.h"
int wlog_open(struct wlog_file *wfile, int trunc, int mode);
int wlog_close(struct wlog_file *wfile);
int wlog_record_write(struct wlog_file *wfile, struct wlog_rec *wrec, long offset);
int wlog_scan_backward(struct wlog_file *wfile, int nrecs, int (*func)(struct wlog_rec *rec), long data);
char *Wlog_Error_String;
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:
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.
/*
* 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 */
};
/*
* 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 */
};
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.
EXAMPLES
This is a simple example of how to initialize a history file, and
record a write to it.
#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 failed0);
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 %d0,
record->w_nbytes, record->path, record->w_offset, record->w_pid);
}
return WLOG_CONTINUE_SCAN;
}
SEE ALSO
pattern(3).
DIAGNOSTICS
All routines return a value < 0 on failure, and >= 0 on success. Error
messages can be accessed through Wlog_Error_String.
BUGS
None known.