[comp.sources.unix] v16i084: Logfile monitor tool for Suns

rsalz@uunet.uu.net (Rich Salz) (11/16/88)

Submitted-by: Steven Grimm <koreth@ssyx.ucsc.edu>
Posting-number: Volume 16, Issue 84
Archive-name: lumberjack

[  I have no idea where the name came from...  --r$  ]

Enclosed is a program which monitors logfiles.  It will alert the user to
changes in one of the monitored files when in icon form (described in the
manual page).  It has been compiled and tested on SunOS 3.4, but should
work with more recent versions of SunOS as well.

#! /bin/sh
# This is a shell archive.  Remove anything before the "#! /bin/sh" line,
# then unpack it by saving it in a file and typing "sh file."
# Contents : Makefile ljack.c ljack.l new1.icon new2.icon none.icon
if `test ! -s Makefile`
then
echo "x - Makefile"
cat > Makefile << '@\Rogue\Monster\'
LIBS=-lsuntool -lsunwindow -lpixrect
CFLAGS=-pipe

ljack: ljack.c new1.icon new2.icon none.icon
	$(CC) $(CFLAGS) -o $@ ljack.c $(LIBS)

install: ljack
	install -s -c ljack /usr/local

clean:
	rm ljack
@\Rogue\Monster\
else
  echo "shar: Will not over write Makefile"
fi
if `test ! -s ljack.c`
then
echo "x - ljack.c"
cat > ljack.c << '@\Rogue\Monster\'
#include <stdio.h>
#include <values.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/time.h>
#include <suntool/sunview.h>
#include <suntool/frame.h>
#include <suntool/textsw.h>
#include <sunwindow/notify.h>

/*
 * SunView size definitions.  I shouldn't have to define these, but
 * there is no way to query SunView for them.
 */
#define LMARGIN		5	/* left frame margin */
#define RMARGIN		5	/* right frame margin */
#define BMARGIN		5	/* bottom frame margin */
#define SMARGIN		5	/* subwindow spacing */

Frame	frame;
int	files, updating = 0;
Textsw	text[16];		/* text subwindows */
Menu	menu[16];		/* menus for each subwindow */
char	*file[16];		/* filenames */
off_t	length[16];		/* file lengths */
char	alert[16];		/* flags: blink icon on update? */

/*
 * Construct the icons.
 * None.icon is displayed when there are no new files; otherwise,
 * new1.icon and new2.icon are toggled once a second.
 */
static short none_icon_i[] = {
#include "none.icon"
};
mpr_static(none_icon_p, 64, 64, 1, none_icon_i);

static short new1_icon_i[] = {
#include "new1.icon"
};
mpr_static(new1_icon_p, 64, 64, 1, new1_icon_i);

static short new2_icon_i[] = {
#include "new2.icon"
};
mpr_static(new2_icon_p, 64, 64, 1, new2_icon_i);

Icon ikes[3];
int  inum = 1;
int  newstuff;

main(argc, argv)
char **argv;
{
	ikes[0] = icon_create(ICON_IMAGE, &none_icon_p, 0);
	ikes[1] = icon_create(ICON_IMAGE, &new1_icon_p, 0);
	ikes[2] = icon_create(ICON_IMAGE, &new2_icon_p, 0);

	frame = window_create(NULL, FRAME,
		FRAME_LABEL,		"LUMBERJACK 1.0 by Steven Grimm (koreth@ssyx.ucsc.edu)",
		FRAME_ARGC_PTR_ARGV,	&argc, argv,
		FRAME_SUBWINDOWS_ADJUSTABLE, FALSE,
		FRAME_ICON,		ikes[0],
		WIN_ERROR_MSG,		"Couldn't open window",
		0);

	parse_args(argc, argv);
	create_subwindows();
	set_window_sizes();
	load_files();

	newstuff = 0;
	window_set(frame, FRAME_ICON, ikes[0], 0);

	set_notifier();

	window_main_loop(frame);
}

/*
 * Create all the necessary subwindows.
 */
create_subwindows()
{
	int i, domenu();

	for (i=0; i<files; i++)
	{
		menu[i] = menu_create(MENU_TITLE_ITEM, file[i],
				MENU_ACTION_ITEM, "Close", domenu,
				0);
		text[i] = window_create(frame, TEXTSW,
			WIN_ERROR_MSG,		"Couldn't create subwindow",
			WIN_IGNORE_PICK_EVENT,	WIN_RESIZE,
			TEXTSW_DISABLE_CD,	TRUE,
			TEXTSW_DISABLE_LOAD,	TRUE,
			TEXTSW_INSERT_MAKES_VISIBLE, TEXTSW_ALWAYS,
			TEXTSW_IGNORE_LIMIT,	TEXTSW_INFINITY,
			TEXTSW_MENU,		menu[i],
			0);
	}
}

/*
 * Set all the window sizes.  This gets called whenever the base
 * frame gets resized, and once at initialization.
 *
 * For now, all the subwindows are assumed to be of equal size.
 */
set_window_sizes()
{
	int i, fr_height, fr_width, ts_height, ts_width;

	fr_height = (int)window_get(frame, WIN_HEIGHT) -
		(int)window_get(frame, WIN_TOP_MARGIN);
	fr_width = (int)window_get(frame, WIN_WIDTH) -
		LMARGIN - RMARGIN;
	ts_height = fr_height/files - SMARGIN;
	ts_width = fr_width;

	for (i=0; i<files; i++)
	{
		window_set(text[i],
			WIN_HEIGHT, ts_height,
			WIN_WIDTH, ts_width,
			0);
		if (i)
			window_set(text[i], WIN_BELOW, text[i-1], 0);
	}
}

/*
 * Parse command line arguments.
 * For now, this is just a list of filenames.
 */
parse_args(c, v)
char **v;
{
	int i, nflag = 0;

	files = 0;
	for (i=1; i<c; i++)
	{
		if (! strcmp(v[i], "-n"))
			nflag = 1;
		else if (access(v[i], R_OK) == 0)
		{
			file[files] = v[i];
			length[files] = MAXINT;
			alert[files] = !nflag;
			nflag = 0;
			if (++files == 16)
				break;
		}
		else
			perror(v[i]);
	}
	if (! files)
		exit(1);
}

/*
 * Load all the files into their windows, and place the cursors at the
 * infinity position.  This gets called once every second.
 */
load_files()
{
	int i;
	struct stat st;

	for (i=0; i<files; i++)
	{
		if (stat(file[i], &st) < 0 || access(file[i], R_OK))
		{
			kill_file(i);
			continue;
		}
		if (st.st_size == length[i])
			continue;
		if (st.st_size < length[i])	/* file has gotten smaller */
			window_set(text[i], TEXTSW_FILE, file[i], 0);
		else if (st.st_size > length[i])
		{
			char *buf;
			int  fd, bsiz;

			buf = (char *)malloc(bsiz = (st.st_size - length[i]));
			fd = open(file[i], O_RDONLY);
			if (fd < 0)
			{
filerror:
				free(buf);
				kill_file(i);
			}
			if (lseek(fd, length[i], L_SET) < 0)
				goto filerror;
			if (read(fd, buf, bsiz) < 0)
				goto filerror;

			window_set(text[i],
				TEXTSW_INSERTION_POINT, TEXTSW_INFINITY,
				0);
			textsw_insert(text[i], buf, bsiz);
			textsw_possibly_normalize(text[i], (Textsw_index)length[i]);
			free(buf);
		}
		length[i] = st.st_size;
		if (alert[i])
			newstuff++;
	}
	if (newstuff)
		if ((int)window_get(frame, FRAME_CLOSED))
		{
			inum ^= 3;
			window_set(frame, FRAME_ICON, ikes[inum], 0);
		}
		else
		{
			newstuff = 0;
			window_set(frame, FRAME_ICON, ikes[0], 0);
		}
}

/*
 * Handle events.
 */
Notify_value
event(window, event, arg, type)
Window window;
Event *event;
Notify_arg arg;
Notify_event_type type;
{
	Notify_value value;

	value = notify_next_event_func(window, event, arg, type);
	if (event_id(event) == WIN_RESIZE)
		set_window_sizes();
	return(value);
}

/*
 * Handle a menu selection.
 */
domenu(m, mi)
Menu m;
Menu_item mi;
{
	int win;

	for (win = 0; win < files; win++)
		if (menu[win] == m)
			break;
	if (win == files)
		return;
	if ((int)menu_get(m, MENU_SELECTED) == 2)
		kill_file(win);
}

/*
 * Kill a file, either because we got an error reading it or because
 * the user said so.
 */
kill_file(num)
int num;
{
	int i;

	menu_destroy(menu[num]);
	window_destroy(text[num]);
	if (num != files-1)
	{
		for (i = num+1; i < files; i++)
		{
			menu[i-1] = menu[i];
			text[i-1] = text[i];
			file[i-1] = file[i];
			length[i-1] = length[i];
			alert[i-1] = alert[i];
		}
	}
	if (num == 0)
		window_set(text[0], WIN_Y, 0, 0);
	if (--files == 0)
		exit(0);
	set_window_sizes();
}

/*
 * Set up a timer.  It checks all the files once a second.
 * Also, tell the notifier what to do about resizes and so forth.
 */
set_notifier()
{
	static struct itimerval timer; /* this is our handle */

	bzero(&timer, sizeof(timer));
	timer.it_interval.tv_sec = 1;
	timer.it_value.tv_sec = 1;
	notify_set_itimer_func(&timer, load_files, ITIMER_REAL, &timer, NULL);
	notify_interpose_event_func(frame, event, NOTIFY_SAFE);
}
@\Rogue\Monster\
else
  echo "shar: Will not over write ljack.c"
fi
if `test ! -s ljack.l`
then
echo "x - ljack.l"
cat > ljack.l << '@\Rogue\Monster\'
.TH LJACK LOCAL "2 September 1988"
.SH NAME
ljack \- monitor system logs
.SH SYNOPSIS
.B ljack
[ -n ] file1 [[ -n ] file2 ... ]
.SH DESCRIPTION
.I Ljack
(short for "lumberjack") runs under the SunView windowing environment.  It
monitors one or more logfiles, optionally alerting the user when an update
takes place.  The
.BR file s
specified on the command line should be readable by the user; if a file is
not readable, no subwindow will be created for it.
.PP
.IR Ljack 's
display is organized into a number of equal-sized subwindows, one for each
.B file.
The subwindows are standard text-editing windows (see
.IR textedit (1))
that can be scrolled and searched as usual.  New information is added to the
appropriate subwindow as it appears in a
.B file,
and the display scrolls to show the new data if necessary.
.PP
If
.I ljack
is in icon form when a
.B file
is updated, an animated axe chops at the log in the icon to inform the user
that something new has arrived.  The axe disappears when the icon is opened
and closed again.
.PP
Up to sixteen
.BR file s
can be viewed; in some versions of SunOS (3.x and lower) there are not enough
file descriptors to view more than six files.
.SH OPTION
.IP \fB\-n\fR
If a
.B file
is preceded by a \-n argument, the axe will not appear when the
.B file
is updated.
.SH AUTHOR
Steven Grimm, koreth@ssyx.ucsc.edu
.SH "SEE ALSO"
textedit(1), suntools(1)
@\Rogue\Monster\
else
  echo "shar: Will not over write ljack.l"
fi
if `test ! -s new1.icon`
then
echo "x - new1.icon"
cat > new1.icon << '@\Rogue\Monster\'
/* Format_version=1, Width=64, Height=64, Depth=1, Valid_bits_per_item=16
 */
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x01FF,0x0000,0x0000,0x0000,0x060F,0xFC00,0x0000,0x0000,
	0x09F3,0xFFF8,0x0000,0x0000,0x1209,0xFFFF,0xE000,0x0000,
	0x24E4,0xF7FF,0xFFC0,0x0000,0x2B1A,0xFFFF,0xFFF8,0x0000,
	0x5209,0x7FF7,0xFFFC,0x0000,0x5445,0x77FF,0xFFBE,0x0000,
	0x54A5,0x7FFF,0xFFFE,0x0000,0x5445,0x7FFF,0xFBFE,0x0000,
	0x5209,0x7FFF,0xDFFE,0x0000,0x2B1A,0xFDFB,0x9FFE,0x0000,
	0x24E4,0xFFFF,0x1EFC,0x0000,0x1209,0xFFFE,0x1FF8,0x0000,
	0x09F3,0xBFBC,0x0FF0,0x0000,0x060F,0xFFF8,0x0C00,0x0000,
	0x01FF,0xE010,0x0400,0x0000,0x0000,0x001E,0x0300,0x0000,
	0x0000,0x0001,0xC180,0x0000,0x0000,0x0000,0xB0C0,0x0000,
	0x0000,0x0000,0x98C0,0x0000,0x0000,0x0001,0x1D00,0x0000,
	0x0000,0x0002,0x2600,0x0000,0x0000,0x0004,0x2000,0x0000,
	0x0000,0x0004,0x4000,0x0000,0x0000,0x0008,0x8000,0x0000,
	0x0000,0x0011,0x0000,0x0000,0x0000,0x0021,0x0000,0x0000,
	0x0000,0x0022,0x0000,0x0000,0x0000,0x0044,0x0000,0x0000,
	0x0000,0x0088,0x0000,0x0000,0x0000,0x0090,0x0000,0x0000,
	0x0000,0x0110,0x0000,0x0000,0x0000,0x0220,0x0000,0x0000,
	0x0000,0x0440,0x0000,0x0000,0x0000,0x0480,0x0000,0x0000,
	0x0000,0x0880,0x0000,0x0000,0x0000,0x1100,0x0000,0x0000,
	0x0000,0x2200,0x0000,0x0000,0x0000,0x2400,0x0000,0x0000,
	0x0000,0x4400,0x0000,0x0000,0x0000,0x2800,0x0000,0x0000,
	0x0000,0x1000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x1000,0x0400,0x0040,0x0040,0x1049,0xA70C,0x5003,0x0E48,
	0x1049,0x5492,0x68C0,0x9050,0x1049,0x549C,0x4043,0x9060,
	0x1059,0x5490,0x4044,0x9050,0x1E29,0x570E,0x4042,0x8E48,
	0x0000,0x0000,0x0180,0x0000,0x0000,0x0000,0x0000,0x0000
@\Rogue\Monster\
else
  echo "shar: Will not over write new1.icon"
fi
if `test ! -s new2.icon`
then
echo "x - new2.icon"
cat > new2.icon << '@\Rogue\Monster\'
/* Format_version=1, Width=64, Height=64, Depth=1, Valid_bits_per_item=16
 */
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x01FF,0x0000,0x0000,0x0000,0x060F,0xFC00,0x0000,0x0000,
	0x09F3,0xFFF8,0x0000,0x0000,0x1209,0xFFFF,0xE000,0x0000,
	0x24E4,0xF7FF,0xFFC0,0x0000,0x2B1A,0xFFFF,0xFFF8,0x0000,
	0x5209,0x7FF7,0xFFFC,0x0000,0x5445,0x77FF,0xFFBE,0x0000,
	0x54A5,0x7FFF,0xFFFE,0x0000,0x5445,0x7FFF,0xFBFE,0x0000,
	0x5209,0x7FFF,0xFFFE,0x0000,0x2B1A,0xFDFB,0xFFFE,0x0000,
	0x24E4,0xFFFF,0xFEFC,0x0000,0x1209,0xFFFF,0xFFF8,0x0000,
	0x09F3,0xBFBF,0xFFF0,0x0000,0x060F,0xFFFF,0x0000,0x0000,
	0x01FF,0xE000,0x0000,0x0000,0x0000,0x0000,0x0038,0x0000,
	0x0000,0x0000,0x00C8,0x0000,0x0000,0x0000,0x0310,0x0000,
	0x0000,0x0000,0x0C10,0x0000,0x0000,0x0000,0x3010,0x0000,
	0x0000,0x0000,0x3810,0x0000,0x0000,0x0000,0x0610,0x0000,
	0x0000,0x0000,0x0118,0x0000,0x0000,0x0000,0x0108,0x0000,
	0x0000,0x0000,0x0684,0x0000,0x0000,0x0000,0x0884,0x0000,
	0x0000,0x0000,0x3066,0x0000,0x0000,0x0000,0x4198,0x0000,
	0x0000,0x0001,0x8210,0x0000,0x0000,0x0002,0x0C00,0x0000,
	0x0000,0x000C,0x3000,0x0000,0x0000,0x0010,0x4000,0x0000,
	0x0000,0x0061,0x8000,0x0000,0x0000,0x0082,0x0000,0x0000,
	0x0000,0x030C,0x0000,0x0000,0x0000,0x0410,0x0000,0x0000,
	0x0000,0x1860,0x0000,0x0000,0x0000,0x2180,0x0000,0x0000,
	0x0000,0x2200,0x0000,0x0000,0x0000,0x1C00,0x0000,0x0000,
	0x0000,0x1000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x1000,0x0400,0x0040,0x0040,0x1049,0xA70C,0x5003,0x0E48,
	0x1049,0x5492,0x68C0,0x9050,0x1049,0x549C,0x4043,0x9060,
	0x1059,0x5490,0x4044,0x9050,0x1E29,0x570E,0x4042,0x8E48,
	0x0000,0x0000,0x0180,0x0000,0x0000,0x0000,0x0000,0x0000
@\Rogue\Monster\
else
  echo "shar: Will not over write new2.icon"
fi
if `test ! -s none.icon`
then
echo "x - none.icon"
cat > none.icon << '@\Rogue\Monster\'
/* Format_version=1, Width=64, Height=64, Depth=1, Valid_bits_per_item=16
 */
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x01FF,0x0000,0x0000,0x0000,0x060F,0xFC00,0x0000,0x0000,
	0x09F3,0xFFF8,0x0000,0x0000,0x1209,0xFFFF,0xE000,0x0000,
	0x24E4,0xF7FF,0xFFC0,0x0000,0x2B1A,0xFFFF,0xFFF8,0x0000,
	0x5209,0x7FF7,0xFFFC,0x0000,0x5445,0x77FF,0xFFBE,0x0000,
	0x54A5,0x7FFF,0xFFFE,0x0000,0x5445,0x7FFF,0xFBFE,0x0000,
	0x5209,0x7FFF,0xFFFE,0x0000,0x2B1A,0xFDFB,0xFFFE,0x0000,
	0x24E4,0xFFFF,0xFEFC,0x0000,0x1209,0xFFFF,0xFFF8,0x0000,
	0x09F3,0xBFBF,0xFFF0,0x0000,0x060F,0xFFFF,0x0000,0x0000,
	0x01FF,0xE000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x1000,0x0400,0x0040,0x0040,0x1049,0xA70C,0x5003,0x0E48,
	0x1049,0x5492,0x68C0,0x9050,0x1049,0x549C,0x4043,0x9060,
	0x1059,0x5490,0x4044,0x9050,0x1E29,0x570E,0x4042,0x8E48,
	0x0000,0x0000,0x0180,0x0000,0x0000,0x0000,0x0000,0x0000
@\Rogue\Monster\
else
  echo "shar: Will not over write none.icon"
fi
# to concatenate archives, remove anything after this line
exit 0

-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.