[comp.sources.misc] monitor -- monitor a command using curses

vandys@lindy.stanford.edu (Andy Valencia) (07/21/87)

Rich,
	Well, you had to ask.  Here's a generalized monitoring program;
you run it with:
	monitor -i 10 command arg arg...
	It runs the command, then displays the first n (where n is the number
of lines on your terminal) lines of output on your screen, then cycles each
10 seconds (-i means interval--default is 30 seconds).  I've seen this hither
and yon for years, but I keep running into places which don't have it.
	Just compile with:
	cc -O -o monitor monitor.c -lcurses -ltermcap
	Works on my 4.2 system, will need changes for SysV.

/*
 * monitor.c--utility which cyclically takes the output of a command and
 *	displays it on the screen via curses(3x).
 */
#include <stdio.h>
#include <curses.h>
#include <signal.h>
#include <errno.h>
extern int errno;
static int numlines;

    /*
     * Set up curses
     */
static void
setup_screen(){
    static char bp[1024];
    extern char *getenv();

    if( getenv("TERM") == NULL ){
	fprintf(stderr,"Please set your TERM variable\n");
	exit(1);
    }
    if( tgetent(bp,getenv("TERM")) != 1 ){
	fprintf(stderr,"I can't use terminal type '%s'\n",getenv("TERM"));
	exit(1);
    }
    if( (numlines = tgetnum("li")) < 0 ){
	fprintf(stderr,"You seem to have an illegal number of lines\n");
	exit(1);
    }
    initscr();
    clear();
    refresh();
}

    /*
     * Run the command, take as many lines of output as will fit on the
     *	screen, paint them.
     */
static void
paint_screen(cmd)
    char *cmd;
{
    char tmpf[80], buf[256];
    register char *p;
    FILE *fp;
    register c;
    int kid, kidstat, x, y;

    strcpy(tmpf,"/tmp/monXXXXXX");
    mktemp(tmpf);
    if( (kid = fork()) == 0 ){

	    /*
	     * The subprocess's output will go to a file; close stdout and
	     *	stderr, then re-open them directed to a temp file.
	     */
	close(1); close(2);
	dup(creat(tmpf,0700));

	    /*
	     * Just to make sure, disable access to stdin
	     */
	close(0);

	    /*
	     * Call the program, return its status
	     */
	exit( system(cmd) );
    }

	/*
	 * Wait for our kid.  Kill him off if we get a signal from the user.
	 */
    if( wait(&kidstat) < 0 ){
	int hold_errno = errno;

	endwin();
	errno = hold_errno;
	perror(cmd[0]);
	kill(kid,SIGKILL);
	unlink(tmpf);
	exit( 1 );
    }

	/*
	 * Wait didn't return error, check if kid ran OK.
	 */
    if( kidstat ){
	endwin();
	fprintf(stderr,"Problem running command\n");
	unlink(tmpf);
	exit( 1 );
    }

	/*
	 * Suck in tmpf's contents, feed it to curses
	 */
    if( (fp = fopen(tmpf,"r")) == NULL ){
	endwin();
	fprintf(stderr,"Error reading file '%s'\n",tmpf);
	exit( 1 );
    }
    unlink(tmpf);
    for( x = 0; x < numlines; ++x ){
	p = buf;
	while( (c = getc(fp)) != EOF )
	    if( c == '\n' ) break;
	    else *p++ = c;
	if( c == EOF ) break;
	*p = '\0';
	buf[79] = '\0';
	move(x,0);
	clrtoeol();
	addstr(buf);
    }
    for( y = x; y < numlines; ++y ){
	move(y,0);
	clrtoeol();
    }
    move(numlines-1,79);
    fclose(fp);
    refresh();
}

    /*
     * Main routine: get arguments, run the thing
     */
main(argc,argv)
    int argc;
    char **argv;
{
    char cmd[4096];
    int interval, x;

	/*
	 * Give'm help if they need it
	 */
    if( argc < 2 ){
	fprintf(stderr,"Usage is: %s [-i <secs>] command [args...]\n",
	    argv[0]);
	exit( 1 );
    }

	/*
	 * If they specify '-i', take a user-specified interval between
	 *	displays, otherwise default to 30 seconds.
	 */
    if( strncmp(argv[1], "-i", 2) == 0 ){
	if( argv[1][2] ){
	    interval = atoi(argv[1]+2);
	    argv += 2;
	} else {
	    interval = atoi(argv[2]);
	    argv += 3;
	}
    } else {
	interval = 30;
	++argv;
    }

	/*
	 * Build a command line
	 */
    cmd[0] = '\0';
    for( x = 0; argv[x]; ++x ){
	strcat(cmd,argv[x]);
	strcat(cmd," ");
    }

	/*
	 * Prepare curses
	 */
    setup_screen();

	/*
	 * Forever: paint the screen & loop
	 */
    while( 1 ){
	paint_screen(cmd);
	sleep(interval);
    }
}

------- End of Forwarded ddress