Path: seismo!harvard!talcott!panda!sources-request From: sources-request@panda.UUCP Newsgroups: mod.sources Subject: Routines to check the load average Message-ID: <1745@panda.UUCP> Date: 25 Apr 86 12:29:43 GMT Sender: jpn@panda.UUCP Lines: 444 Approved: jpn@panda.UUCP Mod.sources: Volume 4, Issue 78 Submitted by: genrad!decvax!trwrb!jsb Here are some routines I wrote that will allow any program to check on the load average without themselves having to have permission to read /dev/kmem. John Bien decvax!trwrb!jsb ------------------------ Cut Here --------------------------- #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create the files: # README # Makefile # example.c # getld.c # getload.3 # getload.c # loadstuff.h # u_getloads.c # This archive created: Fri Apr 25 07:20:24 1986 export PATH; PATH=/bin:$PATH echo shar: extracting "'README'" '(1513 characters)' if test -f 'README' then echo shar: will not over-write existing file "'README'" else cat << \SHAR_EOF > 'README' Here are 2 library funtions that will allow non-setuid (or setgid) functions to check the load average. They are: getload(): A fast routine which will return only the 1, 5, or 15 minute average. It only returns 1 decimal place of accuracy (ie 3.1 instead of 3.08) and will only tell load averages below 25.0 It requires a program installed on the system called "getld". This setgid program reads /dev/kmem and exits with information we need. ugetloads(): A much slower routine that will return all 3 load averages via an array passed to it. It just runs uptime(1) and captures the information it needs from there not requiring the external program getld. If getload() is usable (ie you install the program "getld") it is much faster than ugetloads() and can be called 3 times (to get the 3 load averages) in much less time than ugetloads() can be called once. The speed of getload() lies in the fact that "getld" exits() with the load average we want instead of printing it. This way getload() doesn't have to open a pipe to read what "getld" might otherwise print, it just has to look at the high byte of the exit status. The routine ugetloads() allows anyone to get the load averages without any extra programs on the system. It's good if you don't happen to be super-user and can let "getld" read /dev/kmem. A sample program example.c demonstrates the use of these routines. John Bien {ihnp4 | ucbvax | decvax}!trwrb!jsb SHAR_EOF if test 1513 -ne "`wc -c < 'README'`" then echo shar: error transmitting "'README'" '(should have been 1513 characters)' fi fi echo shar: extracting "'Makefile'" '(880 characters)' if test -f 'Makefile' then echo shar: will not over-write existing file "'Makefile'" else cat << \SHAR_EOF > 'Makefile' # Makefile for load average routines. # John Bien # Define -DBSD41 if you are running 4.1 (only used in ugetload()) CFLAGS = -O CC = cc RANLIB = ranlib LIBNAME = libload.a LIBDIR = /usr/local/lib # PROGDIR is where the setgid program getld will reside PROGDIR = /usr/local/lib # MEMGRP = the group that is allowed to read /dev/kmem MEMGRP = sys CFILES = getload.c u_getloads.c OFILES = getload.o u_getloads.o all: $(LIBNAME) getld $(LIBNAME): $(OFILES) ar rc $(LIBNAME) $(OFILES) $(RANLIB) $(LIBNAME) getld: getld.c $(CC) $(CFLAGS) -o getld getld.c example: $(CC) $(CFLAGS) -o example example.c $(LIBNAME) install: $(LIBNAME) getld cp $(LIBNAME) $(LIBDIR) $(RANLIB) $(LIBDIR)/$(LIBNAME) cp getld $(PROGDIR) chgrp $(MEMGRP) $(PROGDIR)/getld chmod 2111 $(PROGDIR)/getld shar: shar README getload.3 Makefile getld.c loadstuff.h \ $(CFILES) example.c > Shar SHAR_EOF if test 880 -ne "`wc -c < 'Makefile'`" then echo shar: error transmitting "'Makefile'" '(should have been 880 characters)' fi fi echo shar: extracting "'example.c'" '(874 characters)' if test -f 'example.c' then echo shar: will not over-write existing file "'example.c'" else cat << \SHAR_EOF > 'example.c' main() { float getload(), average; float aves[3]; average = getload(1); if((int)average == -99) puts("1 minute average is greater than 25.0"); else if(average < 0) puts("Error using getload"); else printf("One minute average is %.1f\n",average); average = getload(5); if((int)average == -99) puts("5 minute average is greater than 25.0"); else if(average < 0) puts("Error using getload"); else printf("Five minute average is %.1f\n",average); average = getload(15); if((int)average == -99) puts("15 minute average is greater than 25.0"); else if(average < 0) puts("Error using getload"); else printf("Fifteen minute average is %.1f\n",average); ugetloads(aves); printf("And using ugetloads, the load averages are: %.2f, %.2f, %.2f\n", aves[0], aves[1], aves[2]); } SHAR_EOF if test 874 -ne "`wc -c < 'example.c'`" then echo shar: error transmitting "'example.c'" '(should have been 874 characters)' fi fi echo shar: extracting "'getld.c'" '(1511 characters)' if test -f 'getld.c' then echo shar: will not over-write existing file "'getld.c'" else cat << \SHAR_EOF > 'getld.c' /* getld.c * This program must be setgid to memory (or whoever is allowed * to read /dev/kmem). It exits with with the load average, or * a negative number to indicate an error. * It serves primarily to be called by the library routine * getload(); */ #include #include #include "loadstuff.h" struct nlist avenrun[] = { { "_avenrun" }, { 0 } }; main(argc,argv) int argc; char **argv; { register int kmem, whatave, returnave; double avg[3]; if((kmem = open("/dev/kmem", 0)) < 0) exit(NOKMEM); nlist(NAMELIST, avenrun); /* Find where our kernel keeps it */ if(avenrun[0].n_type == 0) exit(NONAMELST); whatave = atoi(argv[1]); --whatave; if(whatave == 4) whatave = 1; if(whatave == 14) whatave = 2; if(whatave <0 || whatave > 2) whatave = 0; lseek(kmem, (long) avenrun[0].n_value, 0); read(kmem, avg, sizeof (avg)); returnave = avg[whatave] * 100; /* Now we have the load average to 2 floating point accuracy * 100 * However, we want to return it with 1 point accuracy that is correctly * rounded up or down. So we divide it by 10 and see what the * remainder is and do the rounding (for the integer). */ if(returnave % 10 >= 5) /* Round up */ returnave = returnave / 10 + 1; else returnave = returnave / 10; if(returnave >= 250) /* We cannot return with a number > 255 */ returnave = 250; /* and 255 = -1, 254 = -2 */ exit(returnave); /* Exit with an integer load average */ } SHAR_EOF if test 1511 -ne "`wc -c < 'getld.c'`" then echo shar: error transmitting "'getld.c'" '(should have been 1511 characters)' fi fi echo shar: extracting "'getload.3'" '(1320 characters)' if test -f 'getload.3' then echo shar: will not over-write existing file "'getload.3'" else cat << \SHAR_EOF > 'getload.3' .TH GETLOAD 3-local .SH NAME getload, ugetload \- load average routines .SH SYNOPSIS .nf .B float getload(whatave) .B int whatave; .PP .B ugetloads(aves) .B float aves[3]; .fi .SH DESCRIPTION These functions allows any program to find what the current load averages are. .PP .I Getload() returns the 1, 5, or 15 minute load average. .I Ugetloads() puts the 1, 5, and 15 load averages into the array passed to it. .I Getload() is much faster than .I ugetloads() and can be invoked 3 times (to return all 3 load averages) in much less time than .I ugetloads() can be invoked once. .I Getload() can only return a load average between 0.0 and 25.0, which should be fine for most applications. .SH FILES .B /usr/local/lib/getld \- The program that actually reads /dev/kmem for .I getload() .br .B uptime(1) \- The program that reads /dev/kmem for .I ugetload() .SH DIAGNOSTICS .I Getload() returns: .RS -99 if the load average is over 25.0 .br -2 if the program .B /usr/local/lib/getld could not read .B /dev/kmem .br -1 if .B /usr/local/lib/getld could not read the namelist (\fB/vmunix\fR). .SH BUGS The function .I getload() will return a load average of 12.7 if it couldn't execute the program .B /usr/local/lib/getld. .SH AUTHOR John Bien .br UUCP: {ihnp4 | ucbvax | decvax}!trwrb!jsb .SH DATE April 1986 SHAR_EOF if test 1320 -ne "`wc -c < 'getload.3'`" then echo shar: error transmitting "'getload.3'" '(should have been 1320 characters)' fi fi echo shar: extracting "'getload.c'" '(1245 characters)' if test -f 'getload.c' then echo shar: will not over-write existing file "'getload.c'" else cat << \SHAR_EOF > 'getload.c' /* getload(what) * int what; * * Returns the 1, 5, or 15 minute load average. * Call with getload(1), or getload(5), or getload(15). * Defaults to getload(1); * The program "getld" must be setgid to read /dev/kmem and * located where the execl expects it (/usr/local/lib). */ #include #include "loadstuff.h" float getload(whatave) int whatave; { int loadav; float load; char *charav = "1 "; int status, pid, w; if(whatave == 15) charav = "15"; if (whatave == 5) charav = "5"; /* Now run the getld program, using fork/exec. */ if ((pid = vfork()) == 0) { /* This Line should be changed if the program "getld" is not * going to be actually called "getld" or wont be in /usr/local/lib */ execl("/usr/local/lib/getld","getld",charav,0); _exit(254); } while ((w = wait(&status)) != pid && w != -1) ; if (w == -1) status = -1; loadav = (status >> 8); /* Set loadav to system exit */ load = (float)loadav / 10; /* Get the correct floating point value */ /* that corresponds to the integer */ /* exit value we received from getld */ if(loadav == 254) load = NOKMEM; if(loadav == 255) load = NONAMELST; if(loadav == 250) load = OVERFLOW; return(load); } SHAR_EOF if test 1245 -ne "`wc -c < 'getload.c'`" then echo shar: error transmitting "'getload.c'" '(should have been 1245 characters)' fi fi echo shar: extracting "'loadstuff.h'" '(87 characters)' if test -f 'loadstuff.h' then echo shar: will not over-write existing file "'loadstuff.h'" else cat << \SHAR_EOF > 'loadstuff.h' #define NOKMEM -2 #define NONAMELST -1 #define OVERFLOW -99 #define NAMELIST "/vmunix" SHAR_EOF if test 87 -ne "`wc -c < 'loadstuff.h'`" then echo shar: error transmitting "'loadstuff.h'" '(should have been 87 characters)' fi fi echo shar: extracting "'u_getloads.c'" '(876 characters)' if test -f 'u_getloads.c' then echo shar: will not over-write existing file "'u_getloads.c'" else cat << \SHAR_EOF > 'u_getloads.c' /* ugetloads(ls) * fload ld[3]; * * Puts the 1, 5, and 15 minute load averages in the float * array passed to it. This program calls upon uptime(1) * which could have different ways of printing ie. with bsd4.2 * " 9:34pm up 11 hrs, 3 users, load average: 0.25, 0.22, 0.24 " * notice the commas -- ^ --- ^. * while bsd4.1 does not print commas. The BSD41 define will * take care of this if that is your system, it defaults to * the 4.2 version. */ #include FILE *popen(); ugetloads(ld) float ld[3]; { FILE *stream; if((stream = popen("/usr/ucb/uptime","r")) == NULL) return(-1); #ifdef BSD41 fscanf(stream,"%*[^l] load average: %f %f %f", &ld[0],&ld[1],&ld[2]); #else fscanf(stream,"%*[^l] load average: %f, %f, %f", &ld[0],&ld[1],&ld[2]); #endif BSD41 pclose(stream); return(NULL); } SHAR_EOF if test 876 -ne "`wc -c < 'u_getloads.c'`" then echo shar: error transmitting "'u_getloads.c'" '(should have been 876 characters)' fi fi exit 0 # End of shell archive