[comp.sources.x] v05i009: New xnetload - version 1.5, Part01/01

argv@island.uu.net (Dan Heller) (10/10/89)

Submitted-by: "J. Michael Berkley" <jmberkley@watnext.waterloo.edu>
Posting-number: Volume 5, Issue 9
Archive-name: xnetload/part01

[ this is not a patch; this is a new version --argv ]

The following shar file is the newest xnetload - a multiple xload
using rwhod stats.
------------------------------------------------------------------------
# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by watnext!jmberkley on Wed Oct  4 10:09:27 EDT 1989
# Contents:  xnetload/README xnetload/Imakefile xnetload/xnetload.c
#	xnetload/getload.c xnetload/xnetload.1 xnetload/Makefile
#	xnetload/AUTHOR xnetload/patchlevel.h
 
if test ! -d xnetload ; then
    mkdir xnetload
fi

echo x - xnetload/README
sed 's/^@//' > "xnetload/README" <<'@//E*O*F xnetload/README//'
README for xnetload
Mike Berkley, September 1989

xnetload gives the system load for a list of remote machines by using
rwhod statistics.  The load for the local machine is also displayed,
but is calculated using the standard Load widget routines, (this means
that xnetload needs to be setgid kmem.)

There's an option to turn off the local load, -nolocal, which would
allow you to run with xnetload non-setgid.  Alternatively, you can
compile xnetload with -DNOLOCAL (see the Imakefile and Makefile).

xnetload was developed on a uVax running Ultrix.  It also compiles and
runs on Sun (both SunOS 3.5 and 4), MIPS and Sequent.  Of course,
there are some little glitches: the Makefile needs to be altered and
you may need to find memcpy() somewhere.

xnetload was written mostly as a tool to learning how to use the
toolkit.  I was amazed at what the toolkit can do in not very many
lines of code.

Since this was an experiment/learning tool, I'm sure that I did not
follow all of the correct X guidelines, but I tried.

The getload() routine is just my own kludge to read rwhod stats.  It
works, but it is replaceable.  Ideally, I would like to use some kind
of system call like Sun 4's RPC versions of rwho and rup command, then
there would not be any dependency on rwhod.  If you are living on a
system that does not use rwhod, or you would like a more efficient way
of determining system load, then you can replace getload() without
breaking the rest of the program.

** Note - if you define "*Load*update", but not  **
** "xnetload*remote*update", then xnetload will  **
** set both the remote and local update times to **
** the "*Load*update" value.  If this time is    **
** much less than the rwhod update period, then  **
** the remote load displays will look more like  **
** bar graphs.                                   **

Send comments, bug reports, etc. to jmberkley@watnext.waterloo.edu.

 Mike Berkley, University of Waterloo
 PAMI Lab
 jmberkley@watnext.waterloo.edu
 {utai,uunet}!watmath!watnext!jmberkley
@//E*O*F xnetload/README//
chmod u=rw,g=r,o=r xnetload/README
 
echo x - xnetload/Imakefile
sed 's/^@//' > "xnetload/Imakefile" <<'@//E*O*F xnetload/Imakefile//'
#
# Imakefile for xnetload
#
# Written by: Mike Berkley, jmberkley@watnext.waterloo.edu, 1989
#
# Permission to use, copy, modify and distribute (without charge) this
# software and its documentation is granted, provided that this comment
# is retained.

#
# Since xnetload finds the local machine's load using the standard Load
# widget stuff, it needs to be able to read kmem, i.e.  setgid kmem.  If
# you do not want this, then define NOLOCAL
#        DEFINES = -O -DNOLOCAL

       INCLUDES = -I$(TOP) -I$(TOP)/X11

# Dynix doesn't have -L option on load, but I have left changing the
# XLIB specification up to individual sites.  Dynix and our Sun 3.5
# don't come with a memcpy, so you'll have to add in whichever library
# has memcpy.
/* LOCAL_LIBRARIES = $(XAWLIB) $(XMULIB) $(XTOOLLIB) $(XLIB) */
LOCAL_LIBRARIES = -L/usr/software/X11/lib -lXaw -lXmu -lXt -lX11

           SRCS = xnetload.c getload.o
           OBJS = xnetload.o getload.o

   INSTALLFLAGS = $(INSTKMEMFLAGS)

all: xnetload

SingleProgramTarget(xnetload,$(OBJS),,$(LOCAL_LIBRARIES))
@//E*O*F xnetload/Imakefile//
chmod u=rw,g=r,o=r xnetload/Imakefile
 
echo x - xnetload/xnetload.c
sed 's/^@//' > "xnetload/xnetload.c" <<'@//E*O*F xnetload/xnetload.c//'
/* 
 * xnetload.c: multiple xload, using rwho statistics
 * $Date: 89/10/04 10:04:41 $ $Revision: 1.5 $
 *
 * Copyright 1989 University of Waterloo
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of UW not be used in advertising
 * or publicity pertaining to distribution of the software without specific,
 * written prior permission.  UW makes no representations about the
 * suitability of this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 *
 * UW DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL UW
 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * Author:  Mike Berkley, jmberkley@watnext.waterloo.edu, 1989
 *
 */

/*
 * xnetload uses the Load widget load proc to get the local
 * machine load.  This means that xnetload needs to be setgid
 * kmem.  If you do not want this, then define NOLOCAL when compiling.
 */

#include <stdio.h>
#include <netdb.h>
#include <ctype.h>
#include <X11/Xos.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>
#include <X11/Form.h>
#include <X11/Load.h>
#include "patchlevel.h"

void GetStatus();
extern int getload();         /* gets rwho load */
extern char *malloc();
extern char *strtok();
static Widget makestatuswidget(), makeremotestatus(), makesubform();
static void modifyloadcall();
static void SetRemUpdate();

#define DOWNMSG      " DOWN"
#define DEFREMUPDATE (60)	/* remote update defaults to 60 seconds */
#define BIGNUM (99999)		/* ~infinite number of load widgets */

/* arguments to Form widget */
static Arg Formargs[] = {
    { XtNfromVert, (XtArgVal) NULL },       /* Chain from .value wid */
    { XtNdefaultDistance, (XtArgVal) 1 },   /* default spacing */
    { XtNresizable, (XtArgVal) TRUE },      /* allow resize */
};

/* Arguments to load windows */
static Arg Statusargs[] = {
    { XtNfromHoriz, (XtArgVal) NULL },      /* Chain from .value wid */
    { XtNresizable, (XtArgVal) TRUE },      /* allow resize */
    { XtNborderWidth, (XtArgVal) 0 },       /* 0 border for load wid  */
    { XtNlabel, (XtArgVal) NULL },          /* Load title */
};

/* Application specific resources - turn off local load display */
static String Configuration;
static Boolean Localload;
static XtResource app_resources[] = {
    { "localload", "LocalLoad", XtRBoolean, sizeof(Boolean),
	(Cardinal) &Localload, XtRString,
/* if NOLOCAL is defined, then no local is displayed, by default */
#ifdef NOLOCAL
"off"
#else
"on"
#endif
},
  /* configuration of loads - horizontal, vertical or mXn/mxn */
  { "configuration", "Configuration", XtRString, sizeof(String),
      (Cardinal) &Configuration, XtRString, "horizontal" },
};

/* command line options for local load display and configuration */
/* If compiled NOLOCAL, then off is default, but on still allowed */
static XrmOptionDescRec app_options[] = {
    { "-local", "localload", XrmoptionNoArg, "on" },
    { "-nolocal", "localload", XrmoptionNoArg, "off" },
    { "-configuration", "configuration", XrmoptionSepArg, "vertical"},
};

main(argc,argv)
int argc;
char *argv[];
{
    int currarg;             /* arg counter to walk through hosts */
    int maincount,subcount,count;
    String maindirection,subdirection;
    static Widget toplevel, mainform, subform, statuswin, head;

    /* The toolkit tries to open your display if you use -help */
    /* so let's do it the other way */
    if((argc > 1) && !strncmp(argv[1],"-help",5)) {
        usage();
        exit(0);
    }

    /* Initialize the top level */
    toplevel = XtInitialize(*argv[0], "XNetload",
                            app_options, XtNumber(app_options), &argc, argv);

    /* Set remote load update to default to DEFREMUPDATE seconds */
    SetRemUpdate(toplevel);

    /* Get local vs nolocal, configuration */
    XtGetApplicationResources( toplevel, (caddr_t)NULL,
			      app_resources, XtNumber(app_resources),
			      NULL, (Cardinal) 0);
      
    /* Parse configuration */
    if(strncmp(Configuration,"horiz",5) == 0) {
	subcount = BIGNUM;	/* ~ inf num of widgets horizontally */
	maincount = 1;
    }
    else if(strncmp(Configuration,"vert",4) == 0) {
	maincount = BIGNUM;	/* ~ inf num of widgets horizontally */
	subcount = 1;
    }
    else {
	/* format is 4x5 for 4 columns, 5 rows */
	strtok(Configuration,"xX");
	if(Configuration[0] == '*')
	  subcount = BIGNUM;
	else if(! isdigit(Configuration[0])) {
	    usage();
	    exit(-1);
	}
	else if((subcount = atoi(Configuration)) == 0)
	  subcount = 1;
	
	Configuration = strtok((char *)NULL,"xX");
	if(Configuration[0] == '*')
	  maincount = BIGNUM;
	else if(! isdigit(Configuration[0])) {
	    usage();
	    exit(-1);
	}
	else if((maincount = atoi(Configuration)) == 0)
	  maincount = 1;
    }

    if(subcount >= maincount) {
	maindirection = XtNfromVert;
	subdirection = XtNfromHoriz;
    }
    /* need to swap so that bigger count is the sub count */
    /* This lowers the number of form widgets */
    else {
	maindirection = XtNfromHoriz;
	subdirection = XtNfromVert;
	/* swap counts */
	count = subcount;subcount = maincount;maincount = count;
    }
    /* adjust subcount so that we don't always get one row if we use Nx* */
    if(subcount == BIGNUM) {
	subcount = argc/maincount;
	if(maincount * subcount != argc)
	  subcount++;
    }

    /* Create a Form widget to contain all of the Loads */
    mainform =
      XtCreateManagedWidget("outerform",
                            formWidgetClass,
                            toplevel,
                            Formargs,
                            XtNumber(Formargs));

    /* Make the first sub-form widget, to contain one row/col */
    subform = makesubform(mainform,NULL,maindirection);

    /*
     * load widget reads kmem for local load.  Define NOLOCAL if you
     * don't want this.
     */
    count = 0;
    if(Localload) {
        /* Get and set label name */
        String labelname = malloc(BUFSIZ*sizeof(char));
        gethostname(labelname,BUFSIZ);
	strtok(labelname,".");

        /* Create the local load widget */
        statuswin =
	  makestatuswidget(labelname,subform,(Widget)NULL,
			   "local",subdirection);
	count++;		/* So local added into subform count */
    }

    /* Remote Loads */
    /* currarg initialized above */
    for(currarg = 1;(currarg < argc)&&(maincount > 0);maincount--) {
	for(;(currarg < argc)&&(count < subcount);count++,currarg++) {
	    if(count > 0) {
		/* Chain to first load */
		statuswin = makeremotestatus(argv[currarg],
					     subform,
					     statuswin,
					     subdirection);
	    }
	    else {
		/* chain to left/top edge of subform */
		statuswin = makeremotestatus(argv[currarg],
					     subform,
					     NULL,
					     subdirection);
	    }
	}
	count = 0;
	/* make another subform if there are more machines */
	if((currarg < argc) && (maincount > 1))
	  subform = makesubform(mainform,subform,maindirection);
    }

    /* if there are no loads to display, then should stop   */
    /* Note, statuswin is static, guaranteed to start at 0, */
    /* so it's non-zero only if a load widget was created   */
    if(!statuswin) {
        fprintf(stderr,"xnetload: No machines to display\n");
        usage();
        exit(0);
    }

    /* draw the widgets, and look for new events */
    XtRealizeWidget(toplevel);
    XtMainLoop();
}

/*
 * Make a sub form widget
 */
static Widget
makesubform(formwin,lastwin,direction)
Widget formwin,lastwin;
String direction;
{
    /* set up chain to previous widget */
    XtSetArg(Formargs[0],direction, (XtArgVal)lastwin);

    /* Create the local form */
    return(XtCreateManagedWidget("subform",
				 formWidgetClass,
				 formwin,
				 Formargs,
				 XtNumber(Formargs)));
}

/*
 * Make a status widget
 */
static Widget
makestatuswidget(label,formwin,lastwin,name,direction)
char *label;
Widget formwin,lastwin;
String name,direction;
{
    XtSetArg(Statusargs[XtNumber(Statusargs)-1],
	     XtNlabel,(XtArgVal)label);
    /* set up chain to previous Load widget */
    XtSetArg(Statusargs[0],direction,(XtArgVal)lastwin);
    
    /* Create the load widget */
    return(XtCreateManagedWidget(name,
				 loadWidgetClass,
				 formwin,
				 Statusargs,
				 XtNumber(Statusargs)));
}

/*
 * Make a remote status widget
 */
static Widget
makeremotestatus(hostname,formwin,lastwin,direction)
String hostname;
Widget formwin,lastwin;
String direction;
{
    String labelname;		/* label, could be "machine DOWN" */
    struct hostent *host;	/* for gethostbyname() */
    Widget remotestatus;

    /* translate command arg to a hostname */
    host = gethostbyname(hostname);
    /* if host == NULL, then hostname is junk for some reason */
    if(!host) 
      return(NULL);

    /* get rid of domain's in fully specified host names */
    strtok(host->h_name,".");
    /* allocate memory for name and downmsg for label */
    if(!(labelname=malloc((strlen(host->h_name)
			   +strlen(DOWNMSG)+1)*sizeof(char))
	 )) {
	perror("xnetload: Ran out of memory for labelname");
	exit(1);
    }
    /* set label name */
    strcpy(labelname,host->h_name);
    remotestatus = makestatuswidget(labelname,formwin,lastwin,
				    "remote",direction);

    modifyloadcall(remotestatus,host->h_name);
    return(remotestatus);
}
    

/*
 * replace standard load widget status call with one that looks at rwho
 */
static void
modifyloadcall(statuswin,name)
Widget statuswin;
String name;
{
    String hostname;

    /* allocate memory for hostname */
    if(!(hostname=
	 malloc((strlen(name)+1)*sizeof(char)))) {
	perror("xnetload: Ran out of memory for name");
	exit(1);
    }
    /* Set up hostname, and load callback (GetStatus()) */
    strcpy(hostname,name);
    XtRemoveAllCallbacks(statuswin, XtNgetLoadProc);
    XtAddCallback(statuswin,XtNgetLoadProc,
		  GetStatus,(caddr_t)hostname);
}

/*
 * You're wondering why I'm doing this, right?  Well, the default update
 * for the Load widget is 5 seconds (I think), but if rwhod updates the
 * files every 60 seconds, then the load displays will look weird.  So, I
 * don't force the user to have a 60 second update, but I at least make
 * this the default.
 */
static void
SetRemUpdate(w)
Widget w;
{
    XrmDatabase rdb;
    char resourceline[50];

    /* Make a database with new default */
    sprintf(resourceline,"%s: %d\n","xnetload*remote*update",DEFREMUPDATE);
    rdb = XrmGetStringDatabase(resourceline);

    /* merge existing Display database into new one */
    /* so that user choices are preserved */
    XrmMergeDatabases(XtDisplay(w)->db,&rdb);

    /* Set Display database to new one */
    XtDisplay(w)->db = rdb;
}

/* GetStatus() global vars */
static char *Label;
static Arg Labelarg[1];
/*
 * Load callback function
 *    - calls getload to determine current load, then updates labels
 */
void
GetStatus(w,closure,call_data)
Widget w;                        /* load widget */
caddr_t closure;                 /* name of host, char*  */
caddr_t call_data;               /* load, float*  */
{
    int load;                    /* current load for this machine */

    /* get current label for widget */
    XtSetArg(Labelarg[0],XtNlabel,(XtArgVal)&Label);
    XtGetValues(w,Labelarg,(Cardinal)1);

    /* if load is less than zero, then machine is down or something */
    if((load = getload(closure)) < 0) {
        *(double *)call_data = 0.0;
        /* update label with DOWN message */
        if(!matchstr(Label,DOWNMSG)) {
	    updatelabel(w,(char *)closure,DOWNMSG);
	}
    }
    /* otherwise we found a good load, so use it */
    else {
        *(double *)call_data = (double)load/100.0;
        /* hate to do this everytime, but we have to check */
        if(matchstr(Label,DOWNMSG)) {
	    updatelabel(w,(char *)closure,"");
	}
        return;
    }
}

/*
 * look for pat in str - exact match, no wild cards here
 */
static int
matchstr(str,pat)
register char *str, *pat;
{
    while(str = index(str,*pat))
      if(! strcmp(str++,pat))
        return(TRUE);
    return(FALSE);
}

/*
 * replace label with str and msg
 */
static int
updatelabel(w,str,msg)
Widget w;
register char *str, *msg;
{
    XEvent Sendevent;

    sprintf(Label,"%s%s",str,msg);
    XtSetArg(Labelarg[0],XtNlabel, (XtArgVal)Label);
    XtSetValues(w,Labelarg, (Cardinal)1);

    /* Load widget doesn't redraw when label is changed.  Force it! */
    XClearArea(XtDisplay(w),XtWindow(w),0,0,0,0,TRUE);
}

static int
usage()
{
    fprintf(stderr,"usage:\n xnetload [-configuration mXn] [-nolocal] [machine] ...\n");
}
@//E*O*F xnetload/xnetload.c//
chmod u=rw,g=r,o=r xnetload/xnetload.c
 
echo x - xnetload/getload.c
sed 's/^@//' > "xnetload/getload.c" <<'@//E*O*F xnetload/getload.c//'
/* 
 * getload.c: get load, using rwho statistics - used in xnetload
 *
 * Copyright 1989 University of Waterloo
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of UW not be used in advertising
 * or publicity pertaining to distribution of the software without specific,
 * written prior permission.  UW makes no representations about the
 * suitability of this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 *
 * UW DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL UW
 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * Author:  Mike Berkley, jmberkley@watnext.waterloo.edu, 1989
 *
 */

/* getload includes - no need for X stuff */
#include <stdio.h>
#include <ctype.h>
#include <netdb.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/time.h>

/* Dynix doesn't have an rwhod.h file, so here's the struct for that system */
#ifdef sequent
struct  outmp {
    char  out_line[8];    /* tty name */
    char  out_name[8];    /* user id */
    long  out_time;       /* time on */
};
struct  whod {
    char  wd_vers;
    char  wd_type;
    char  wd_fill[2];
    int   wd_sendtime;
    int   wd_recvtime;
    char  wd_hostname[32];
    int   wd_loadav[3];
    int   wd_boottime;
    struct whoent {
        struct outmp    we_utmp;
        int             we_idle;
    } wd_we[1024 / sizeof (struct whoent)];
};
#else
#include <protocols/rwhod.h>
#endif

extern time_t time();

#define DOWNTIME (60 * 5)
#define ISDOWN(now,wd) (((now) - (wd).wd_recvtime) > DOWNTIME)
#define RWHODHD "/usr/spool/rwho/whod"

/*
 * actual function to get the load
 * reads rwhod stats to find the load average of a machine
 */
int
getload(hostname)
char *hostname;
{
    int nbytes;			/* number of bytes read */
    int fd;			/* file descriptor */
    int counter;		/* loop counter */
    time_t currtime;		/* current time */
    struct whod    wd;
    char buf[sizeof(struct whod)], fname[256];

    sprintf(fname,"%s.%s",RWHODHD,(char *)hostname);

    (void) time(&currtime);

    /* Loop 3 times, looking for a non-zero load. */
    /* This keeps us from getting a zero load if rwhod is in */
    /* the process of doing something to the file.  This is */
    /* important, since we'll get strange looking zero gaps */
    /* in the graph if we don't try our best to get a non-zero load. */
    for(counter=0;counter<=3;counter++) {
        /* open and read from the file */
        if((fd = open(fname, O_RDONLY, 0)) < 0)
          /* we can't open file, so it's down or something */
          return(-1);

        nbytes = read(fd, buf, sizeof(struct whod));
        close(fd);
        /* if we got enough bytes, then copy it to wd */
        if(nbytes >= (sizeof (wd) - sizeof(wd.wd_we))) {

            /* NOT everbody has memcpy, but can't use strncpy() */
            memcpy(&wd,buf,(sizeof (wd) - sizeof(wd.wd_we)));

            /* ISDOWN macro, if machine is really down or faking */
            if(ISDOWN(currtime,wd))
              return(-1);
            else if(wd.wd_loadav[0] > 0)
              return(wd.wd_loadav[0]);
        }

        /* If we got to here, then we need to loop again */
        sleep(1);
    }
    return(0);		/* couldn't read load if here */
}
@//E*O*F xnetload/getload.c//
chmod u=rw,g=r,o=r xnetload/getload.c
 
echo x - xnetload/xnetload.1
sed 's/^@//' > "xnetload/xnetload.1" <<'@//E*O*F xnetload/xnetload.1//'
@.TH XNETLOAD l "April 1989" "X Version 11"
@.SH NAME
xnetload - display load averages from local network machines
@.SH SYNOPSIS
@.B xnetload
@.br
[
@.B \-configuration
(<column>x<row>|horizontal|vertical)
]
@.br
[
@.B \-nolocal
]
@.br
[
machine
] ...
@.SH DESCRIPTION
@.I xnetload 
uses the Athena Load widget and
@.B rwhod
statistics to display the
load average of a number of machines on a local network.
@.SH OPTIONS
@.PP
@.I xnetload
accepts all of the standard X Toolkit command line options along with the 
following additional options:
@.PP
@.TP 8
@.B \-configuration <column>x<row>
@.I xnetload
can be configured to show the machine loads horizontally, vertically,
or in multiple columns and rows:

@.br
\fI-configuration horizontal\fR - display machine loads in one row

@.br
\fI-configuration vertical\fR - display machine loads in one column

@.br
\fI-configuration 4x3\fR - display machine loads in four columns,
three columns (maximum of 12 machines).

@.br
\fI-configuration 2x*\fR - display machine loads in two columns, no
maximum number of machines, but an odd number will result in one blank
entry.

@.br
\fI-configuration *x3\fR - display machine loads in three rows, no
maximum number of machines, but a number not divisible by three will
result in blank entries.

@.PP
@.TP 8
@.B \-nolocal
By default, the load for the local machine is displayed and the
load calculated using the
Load widget's standard LoadProc.  With this option,
@.I xnetload
will NOT display
the local machine's load unless the hostname is listed
on the command line.  When
@.B \-nolocal
is used,
@.I xnetload
will not try to read
@.I kmem.
@.PP
@.TP 8
@.B \-local
If
@.I xnetload
is compiled with
@.B \-DNOLOCAL,
then no local load will
be displayed by default.
@.B \-local
will turn the local load back on, and make
@.I xnetload
read
@.I kmem.
@.PP
@.TP 8
@.B [machine] ...
The load for each machine listed will be displayed.
@.SH RESOURCES
@.I xnetload
uses the standard toolkit resources and some private resources.
These are some of the standard resources that
I find useful:
@.br
@.sp
!**** XLOAD ****
@.br
*Load*font:                          6x10
@.br
*Load*height:                        35
@.br
*Load*width:                         90
@.br
!**** XMULTIMETER ****
@.br
xnetload.geometry:                   -0+0
@.br
! number of seconds between updates for local machine
@.br
xnetload*local*update:               15
@.br
! number of seconds between updates for remote machines
@.br
xnetload*remote*update:              60
@.sp
@.PP
Note - if you define "*Load*update", but not "xnetload*remote*update",
then
@.I xnetload
will set both the remote and local update times to the "*Load*update"
value.  If this time is much less than the
@.B rwhod
update period, (eg. 5 seconds instead of 60 seconds)
then the remote load displays will look like bar graphs.
@.PP
The private
@.I xnetload
resources are as follows:
@.sp
@.IP "\fBconfiguration\fP (string) ``horizontal''"
Gives the configuration of the load widgets - use the same syntax as with
the command line argument: \fIhorizontal\fP for one row \fIvertical\fP
for one column, \fImxn\fP or \fImXn\fP for m columns and n rows,
\fImx*\fP for m columns and the rows necessary, and \fI*xn\fP for n
rows with the necessary number of columns.
@.IP "\fBlocalload\fP (boolean) ``on''"
If
@.I localload
is true, then
@.I xnetload
will use the standard Load widget load function to get the load
average for the local machine.  There is a compile time switch to make
this default "off."
@.SH SEE ALSO
X(1), xload(1), Athena Load widget
@.SH BUGS
@.I xnetload
uses the Load widget's normal load function for the local
system, therefore
@.I xnetload
should be setgid
@.I kmem.
If
@.I xnetload
cannot be setgid
@.I kmem,
then either compile with
@.B \-DNOLOCAL
or use the
@.B \-nolocal
option.
@.PP
@.I xnetload
will also take a width and height on the application
geometry specification, but sometimes the
@.I Form
widget will leave small gaps between the loads.
@.PP
A very wide
@.I xnetload
will often have wide margins.  I think the
@.I Form
widget is doing this.
@.SH AUTHOR
J. Michael Berkley
(jmberkley@watnext.waterloo.edu)
@//E*O*F xnetload/xnetload.1//
chmod u=rw,g=r,o=r xnetload/xnetload.1
 
echo x - xnetload/Makefile
sed 's/^@//' > "xnetload/Makefile" <<'@//E*O*F xnetload/Makefile//'
#
# Makefile for xnetload
#
# Written by: Mike Berkley, jmberkley@watnext.waterloo.edu, 1989
#
# Permission to use, copy, modify and distribute (without charge) this
# software and its documentation is granted, provided that this comment
# is retained.
#

# Since xnetload finds the local machine's load using the standard Load
# widget stuff, it needs to be able to read kmem, i.e.  setgid kmem.  If
# you do not want this, then define NOLOCAL
# CFLAGS = -O -DNOLOCAL
# CC=gcc -traditional
CFLAGS = -O

XINC = /software/X11/include
XLIB = /software/X11/lib

# Dynix doesn't have -L option on load, but I have left changing the
# XLIB specification up to individual sites.  Dynix and our Sun 3.5
# don't come with a memcpy, so you'll have to add in whichever library
# has memcpy.

INCLUDES = -I$(XINC)
LIBS = -L$(XLIB) -lXaw -lXmu -lXt -lX11
OBJS = xnetload.o getload.o

xnetload: $(OBJS)
	$(CC) $(CFLAGS) -o xnetload $(OBJS) $(LIBS)

@.c.o:
	$(CC) $(CFLAGS) -c $(INCLUDES) $<
@//E*O*F xnetload/Makefile//
chmod u=rw,g=r,o=r xnetload/Makefile
 
echo x - xnetload/AUTHOR
sed 's/^@//' > "xnetload/AUTHOR" <<'@//E*O*F xnetload/AUTHOR//'
xnetload was written by Mike Berkley, jmberkley@watnext.waterloo.edu

Permission to use, copy, modify and distribute (without charge) this
software and its documentation is granted, provided that this comment
is retained.
@//E*O*F xnetload/AUTHOR//
chmod u=rw,g=r,o=r xnetload/AUTHOR
 
echo x - xnetload/patchlevel.h
sed 's/^@//' > "xnetload/patchlevel.h" <<'@//E*O*F xnetload/patchlevel.h//'
#define PATCHLEVEL 0
@//E*O*F xnetload/patchlevel.h//
chmod u=rw,g=r,o=r xnetload/patchlevel.h
 
echo Inspecting for damage in transit...
temp=/tmp/shar$$; dtemp=/tmp/.shar$$
trap "rm -f $temp $dtemp; exit" 0 1 2 3 15
cat > $temp <<\!!!
      46     336    2026 README
      32     157    1060 Imakefile
     446    1637   13099 xnetload.c
     115     559    3843 getload.c
     177     652    4086 xnetload.1
      34     164     977 Makefile
       5      29     222 AUTHOR
       1       3      21 patchlevel.h
     856    3537   25334 total
!!!
wc  xnetload/README xnetload/Imakefile xnetload/xnetload.c xnetload/getload.c xnetload/xnetload.1 xnetload/Makefile xnetload/AUTHOR xnetload/patchlevel.h | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp
if [ -s $dtemp ]
then echo "Ouch [diff of wc output]:" ; cat $dtemp
else echo "No problems found."
fi
exit 0