argv@island.uu.net (Dan Heller) (05/08/89)
Submitted-by: Mike Berkley <jmberkley@watnext.waterloo.edu> Posting-number: Volume 3, Issue 103 Archive-name: xnetload/part01 [ I compiled, but didn't run this (we don't use rwhod here). Check the makefile before compiling or use the imakefile. The shar given makes its own directory. --argv ] What follows was written by the author: 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.) 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. Don't forget to read the man page (there's a caveat about "*Load*update"). Send comments, bug reports, etc. to jmberkley@watnext.waterloo.edu. --------------------------------------------------------------------------- # 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 Tue May 2 10:56:54 EDT 1989 # Contents: xnetload/ xnetload/README xnetload/Imakefile xnetload/xnetload.c # xnetload/xnetload.1 xnetload/Makefile xnetload/AUTHOR # xnetload/patchlevel.h echo mkdir - xnetload mkdir xnetload chmod u=rwx,g=rx,o=rx xnetload echo x - xnetload/README sed 's/^@//' > "xnetload/README" <<'@//E*O*F xnetload/README//' README for xnetload Mike Berkley, April 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 Suns (both SunOS 3.5 and 4) and Sequents running Dynix. Of course, there are some little glitches: the Makefile needs to be altered and you may need to find a 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. 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 status.c INSTALLFLAGS = $(INSTKMEMFLAGS) all: xnetload SingleProgramTarget(xnetload,xnetload.o,,$(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 * * 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. * * The X Window system is a copyright of the Massachusetts * Institute of Technology * */ /* * 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 <ctype.h> #include <netdb.h> #include <X11/Xos.h> #include <X11/Xlib.h> #include <X11/IntrinsicP.h> #include <X11/LoadP.h> #include <X11/StringDefs.h> #include <X11/Shell.h> #include <X11/Form.h> #include "patchlevel.h" extern void GetStatus(); /* gets rwho load */ extern char *XtMalloc(); extern time_t time(); static Widget Toplevel, Formwin, Statuswin; #define DEFREMUPDATE 60 /* remote update defaults to 60 seconds */ #define DOWNTIME (DEFREMUPDATE * 5) #define DOWNMSG " DOWN" #define RWHODHD "/usr/spool/rwho/whod" /* arguments to Form widget */ static Arg Formargs[] = { { XtNfromVert, (XtArgVal) NULL }, /* Chain vert from value widget */ { XtNdefaultDistance, (XtArgVal) 1 }, /* default spacing */ { XtNresizable, (XtArgVal) TRUE }, /* allow resize */ }; /* Arguments to load windows */ static Arg Statusargs[] = { { XtNfromVert, (XtArgVal) NULL }, /* Chain vert from .value wid */ { XtNresizable, (XtArgVal) TRUE }, /* allow resize */ { XtNlabel, (XtArgVal) NULL }, /* Load title */ { XtNborderWidth, (XtArgVal) 0 }, /* width for each load wid */ }; /* Application specific resources - turn off local load display */ 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 }, }; /* command line options for local load display */ /* If compiled NOLOCAL, then off is default, but on still allowed */ static XrmOptionDescRec app_options[] = { #ifdef NOLOCAL { "-local", "localload", XrmoptionNoArg, "on" }, #else { "-nolocal", "localload", XrmoptionNoArg, "off" }, #endif }; /* resources specific to the remote load displays */ /* * 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 int Remupdate = DEFREMUPDATE; static XtResource rem_resources[] = { { XtNupdate, XtCInterval, XtRInt, sizeof(int), (Cardinal) &Remupdate, XtRInt, (caddr_t) &Remupdate }, }; /* GetStatus() global vars */ static char *Label; static Arg Labelarg; /* getload includes */ /* 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 main(argc,argv) int argc; char *argv[]; { int currarg = 1; /* arg counter to walk through hosts */ char *hostname, /* raw host name */ *labelname; /* label, could be "machine DOWN" */ struct hostent *host; /* for gethostbyname() */ /* 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],"-h",2)) { usage(); exit(1); } /* Initialize the top level */ Toplevel = XtInitialize(*argv[0], "XNetload", app_options, XtNumber(app_options), &argc, argv); /* Get local vs nolocal */ XtGetApplicationResources( (Widget)Toplevel, (caddr_t)NULL, app_resources, XtNumber(app_resources), NULL, (Cardinal) 0); /* Create a Form widget to contain all of the Loads */ Formwin = XtCreateManagedWidget("shell", formWidgetClass, Toplevel, Formargs, XtNumber(Formargs)); /* * Load widget reads kmem for local load. Define NOLOCAL if you * don't want this. */ if(Localload) { /* Get and set label name */ labelname = XtMalloc(BUFSIZ*sizeof(char)); gethostname(labelname,BUFSIZ); XtSetArg(Statusargs[XtNumber(Statusargs)-1], XtNlabel,(XtArgVal)labelname); /* Create the local load widget */ Statuswin = XtCreateManagedWidget("local", loadWidgetClass, Formwin, Statusargs, XtNumber(Statusargs)); } /* Remote Loads */ /* currarg initialized above */ for(;currarg < argc;currarg++) { /* translate command arg to a hostname */ host = gethostbyname(argv[currarg]); /* if host == NULL, then hostname is junk for some reason */ if(host) { /* allocate memory for hostname */ if(!(hostname=XtMalloc((strlen(host->h_name)+1)*sizeof(char)))) { perror("xnetload: Ran out of memory for name"); exit(1); } /* allocate memory for name and downmsg for label */ if(!(labelname=XtMalloc((strlen(host->h_name) +strlen(DOWNMSG)+1)*sizeof(char)) )) { perror("xnetload: Ran out of memory for labelname"); exit(1); } /* set up chain to previous Load widget */ XtSetArg(Statusargs[0],XtNfromVert, (XtArgVal)Statuswin); /* set label name */ strcpy(labelname,host->h_name); XtSetArg(Statusargs[XtNumber(Statusargs)-1], XtNlabel,(XtArgVal)labelname); /* create the remote load widget, taking into account */ /* modified label and vertical chaining */ Statuswin = XtCreateManagedWidget("remote", loadWidgetClass, Formwin, Statusargs, XtNumber(Statusargs)); /* This makes sure of 60 second default, without forcing! */ XtGetApplicationResources( (Widget)Statuswin, (caddr_t)NULL, rem_resources, XtNumber(rem_resources), NULL, (Cardinal) 0); /* Set up hostname, and load callback (GetStatus()) */ strcpy(hostname,host->h_name); XtRemoveAllCallbacks(Statuswin, XtNgetLoadProc); XtAddCallback(Statuswin,XtNgetLoadProc, GetStatus,(caddr_t)hostname); } } /* 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(); } /* * Load callback function * */ 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 */ char fname[BUFSIZ]; /* name of rwhod file */ /* get current label for widget */ XtSetArg(Labelarg,XtNlabel,(XtArgVal)&Label); XtGetValues(w,&Labelarg,(Cardinal)1); sprintf(fname,"%s.%s",RWHODHD,(char *)closure); /* if load is less than zero, then machine is down or something */ if((load = getload(fname)) < 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,XtNlabel, (XtArgVal)Label); XtSetValues(w,&Labelarg, (Cardinal)1); /* Load widget doesn't redraw when label is changed. Force it! */ Sendevent.xexpose.x = 0; Sendevent.xexpose.width = ((LoadWidget)w)->core.width; XClearWindow(XtDisplay(w),XtWindow(w)); (*((LoadWidgetClass)&loadClassRec)->core_class.expose) (w, &Sendevent, (Region) NULL); } #define ISDOWN(now,wd) (((now) - (wd).wd_recvtime) > DOWNTIME) /* * actual function to get the load * reads rwhod stats to find the load average of a machine */ static int getload(fname) char *fname; { 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)]; (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); } } static int usage() { fprintf(stderr,"usage:\n xnetload [-nolocal] [machine] ...\n"); } @//E*O*F xnetload/xnetload.c// chmod u=rw,g=r,o=r xnetload/xnetload.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 [-\fInolocal\fP] [\fImachine\fP] ... @.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 \-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. These are some of the ones 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 ! Display the local machine load by reading kmem @.br xnetload.localload: on @.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: 40 @.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, then the remote load displays will look more like bar graphs. @.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 CFLAGS = -O XINC = /usr/software/X11/include XLIB = /usr/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 xnetload: xnetload.o $(CC) $(CFLAGS) -o xnetload xnetload.o $(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 <<\!!! 49 345 2160 README 31 153 1023 Imakefile 365 1371 11622 xnetload.c 121 421 2644 xnetload.1 32 157 941 Makefile 5 29 222 AUTHOR 1 3 21 patchlevel.h 604 2479 18633 total !!! wc xnetload/README xnetload/Imakefile xnetload/xnetload.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