:
~
1 /*
2 * xmeter.c - Display histogram of rstat(3) output for multiple hosts.
3 *
4 * Copyright (c) 1991, Bob Schwartzkopf
5 *
6 * Permission to use, copy, modify and distribute this software and its
7 * documentation for any purpose is hereby granted without fee, provided
8 * that the above copyright notice appear in all copies and that both the
9 * copyright notice and this permission notice appear in supporting
10 * documentation, and that the name of the RAND Corporation not be used
11 * in advertising or publicity pertaining to distribution of the software
12 * without specific, written prior permission. The RAND Corporation
13 * makes no representations about the suitability of this software for
14 * any purpose. It is provided "as is" without express or implied
15 * warranty.
16 *
17 * The X Consortium, and any party obtaining a copy of these files from
18 * the X Consortium, directly or indirectly, is granted, free of charge, a
19 * full and unrestricted irrevocable, world-wide, paid up, royalty-free,
20 * nonexclusive right and license to deal in this software and
21 * documentation files (the "Software"), including without limitation the
22 * rights to use, copy, modify, merge, publish, distribute, sublicense,
23 * and/or sell copies of the Software, and to permit persons who receive
24 * copies from any such party to do so. This license includes without
25 * limitation a license to do the foregoing actions under any patents of
26 * the party supplying this software to the X Consortium.
27 *
28 * Author: Bob Schwartzkopf, The RAND Corporation
29 *
30 * Suggestions for improvements and bug fixes can be sent to "bobs@rand.org".
31 * As my schedule permits I'll try to incorporate them.
32 */
33
34 #ifndef lint
35 static char *RCSid="$Header: /net/src/rand/bin/xmeter/RCS/xmeter.c,v 1.19 1994/05/25 01:01:08 bobs Exp $";
36 #endif lint
37
38 /*
39 * $Log: xmeter.c,v $
40 * Revision 1.19 1994/05/25 01:01:08 bobs
41 * Add X copyright.
42 *
43 * Revision 1.18 1994/02/12 02:46:40 bobs
44 * Port to solaris 2.
45 *
46 * Revision 1.17 1994/02/10 20:54:17 bobs
47 * Fixed bug in quit() that caused core dumps.
48 * Removed bcopy() call in newshmeter().
49 *
50 * Revision 1.16 1993/12/09 20:10:26 bobs
51 * Patches 12 and 13 (see README for details).
52 *
53 * Revision 1.15 1993/08/18 01:42:33 bobs
54 * Fixed bug in fdisks(), dk_xfer dimensioned is 4.
55 * Include <X11/Xos.h>, remove some other includes and external declarations.
56 * Changed vfork to vork.
57 * Don't destroy popup widget in popdownscale if it's null.
58 * Include <sys/dk.h> under hpux.
59 *
60 * Revision 1.14 1993/05/25 00:06:27 bobs
61 * Use sigset(3) under SVR4.
62 *
63 * Revision 1.13 1993/01/25 22:32:18 bobs
64 * Redo previous 2 edits.
65 *
66 * Revision 1.12 1993/01/19 00:24:22 bobs
67 * Have child processes exit if parent has gone away.
68 *
69 * Revision 1.11 1993/01/19 00:12:33 bobs
70 * Kill children on INT signal too.
71 *
72 * Revision 1.10 1993/01/19 00:08:56 bobs
73 * Kill child processes on TERM signal.
74 *
75 * Revision 1.9 1992/05/21 00:11:17 bobs
76 * Add option to display current value in label.
77 * Add quit option to stat menu.
78 * Redo application resource handling in more portable way.
79 * Use complete hostname for comparisons when shortnames has been specified.
80 * Redo child process cleanup code in a hopefully more portable way.
81 * Add stat name as fourth arg to alert programs.
82 * Fix divide by zero problem in functions that return stat values.
83 *
84 * Revision 1.8 1991/09/16 21:01:04 bobs
85 * Fix bugs handling down hosts.
86 * Add composite stats swap, disks, page and pkts.
87 * Add popup window with version number, scale and downtime (if applicable).
88 *
89 * Revision 1.7 1991/06/03 22:21:43 root
90 * Add -v option to print xmeter version number.
91 * Add defstat option.
92 * Add code to clear stripchart when watched stat is changed.
93 * When host is down fork process to wait for it to come up instead of
94 * having toplevel process wait for timeouts.
95 *
96 * Revision 1.6 90/09/28 20:32:34 root
97 * Add explicit closes of sockets back in, apparently some versions of
98 * clnt_destroy() don't do it automatically.
99 *
100 * Revision 1.5 90/09/18 20:47:03 root
101 * Allow user specification of foreground colors.
102 *
103 * Revision 1.4 90/08/20 14:18:01 root
104 * Added menus, column and row options, background bitmaps, and user
105 * specifiable programs to be invoked when graphs change state.
106 *
107 * Revision 1.3 90/06/07 16:17:06 bobs
108 * Removed "retries".
109 * Changed name of paned widgets to host name displayed in that widget.
110 * Used actual time between rstat calls instead of "update" interval in
111 * computing rates in functions that return values to stripchart widgets.
112 * Removed "ost" variable, rewrote functions that return values to
113 * stripcharts.
114 * Fixed bug in fsys, fcpu and fuser that could cause divide by 0 errors.
115 * Fixed rpc timeout handling in getmeter and getport.
116 * Use RSTATVERS_TIME instead of RSTATVERS, which isn't defined in SunOS 4.1.
117 *
118 * Revision 1.2 90/05/04 12:31:52 bobs
119 * Fix memory leak in getport(). Wasn't freeing resources allocated by
120 * clntudp_create when clnt_call failed. Also removed explicit calls
121 * to close sockets since clnt_destroy() does this.
122 *
123 * Revision 1.1 90/04/30 14:33:59 bobs
124 * Initial revision
125 *
126 */
127
128 #include <stdio.h>
129 #include <sys/time.h>
130 #include <sys/param.h>
131 #include <sys/socket.h>
132 #include <sys/wait.h>
133 #include <signal.h>
134 #include <netdb.h>
135 #define PORTMAP /* Get right function declarations on Solaris 2 */
136 #include <rpc/rpc.h>
137 #include <rpc/pmap_prot.h>
138 #include <rpcsvc/rstat.h>
139 /*
140 * We need the definitions of CP_USER, CP_NICE and CP_SYS. Some systems
141 * declare them in <sys/dk.h>, which may or may not be included by
142 * <rpcsvc/rstat.h>, others define them in <rpcsvc/rstat.h>, and others
143 * don't seem to define them anywhere. To simplify things I'm just
144 * going to define them here if I didn't get them from <rpcsvc/rstat.h>.
145 * If they ever change this is going to break.
146 */
147 #ifndef CP_USER
148 #define CP_USER 0
149 #define CP_NICE 1
150 #define CP_SYS 2
151 #endif
152 #ifndef CPUSTATES
153 #ifdef RSTAT_CPUSTATES
154 #define CPUSTATES RSTAT_CPUSTATES
155 #else
156 #define CPUSTATES 4
157 #endif
158 #endif
159 #include <X11/Xos.h>
160 #include <X11/Xlib.h>
161 #include <X11/Intrinsic.h>
162 #include <X11/StringDefs.h>
163 #include <X11/Xatom.h>
164 #include <X11/Shell.h>
165 #include <X11/Xaw/Cardinals.h>
166 #include <X11/Xaw/Form.h>
167 #include <X11/Xaw/StripChart.h>
168 #include <X11/Xaw/SimpleMenu.h>
169 #include <X11/Xaw/MenuButton.h>
170 #include <X11/Xaw/SmeBSB.h>
171 #include <X11/Xaw/Paned.h>
172 #include <X11/Xmu/Xmu.h>
173 #include "patchlevel.h"
174
175 #define MAJORVERSION 1
176 #define DMSG "down"
177 #define OK 0
178 #define WARN 1
179 #define ERROR 2
180 #define FATAL 3
181 #define MAXBACKS (FATAL+1)
182 #define STATMENU "statmenu"
183 #ifndef FSCALE
184 #define FSCALE (1 << 8)
185 #endif
186
187 /*
188 * There is one METER structure per strip chart. If multiple stats
189 * on a single host are being watched we save rstatd calls by sharing
190 * part of this structure (SHMETER) between the different meters watching
191 * the same host.
192 */
193 typedef struct _shmeter {
194 char *name; /* Host name */
195 char *label; /* Displayed host name */
196 struct sockaddr_in addr;
197 CLIENT *clnt;
198 int s; /* Socket */
199 struct statstime st[2]; /* Keep last 2 stats to compute diffs */
200 int idx; /* Index into st array */
201 int refcnt; /* Meters sharing this structure */
202 int curcnt; /* Meters who've displayed current data */
203 int first; /* TRUE when only 1 rstat has been done */
204 int pid; /* Pid of proc waiting for host */
205 time_t when; /* Time we first notice host down */
206 struct _shmeter *nxt; /* Link these together */
207 } SHMETER;
208
209 typedef struct _meter {
210 char *label;
211 int stat; /* Which stat to watch */
212 Widget pdwidget; /* Paned widget */
213 Widget mbwidget; /* MenuButton widget */
214 Widget scwidget; /* StripChart widget */
215 int oldstate; /* Save state so know when to */
216 /* change backgrounds */
217 SHMETER *sh; /* Shared info */
218 Pixmap pm; /* Current background pixmap */
219 int oldjumpscroll; /* Old jumpscroll value */
220 struct _meter *nxt;
221 } METER;
222
223 /*
224 * There is a STATDATA structure for each statistic that can be monitored.
225 */
226 typedef struct {
227 int (*val)(); /* Function that computes this stat */
228 char *name; /* Name of stat for menu widget */
229 } STATDATA;
230
231 #if defined(SVR4) || defined(sgi)
232 #define SIGTYPE void
233 #else
234 #define SIGTYPE int
235 #endif
236
237 int getstatus();
238 int changestat();
239 int selecthost();
240 SIGTYPE freechild();
241 METER *initmeter();
242 SHMETER *newshmeter();
243 int fcoll(), fcpu(), fierr(), fintr(), fipkt(), fload();
244 int foerr(), fopkt(), fpage(), fpgpgin(), fpgpgout();
245 int fpswpin(), fpswpout(), fswt(), fsys(), fuser();
246 int fpkts(), fdisks(), fswap(), ferr();
247 void popupscale();
248 void popdownscale();
249 void quit();
250 char *mystrdup();
251
252 static STATDATA sd[] = {
253 #define S_COLL 0
254 {fcoll, "coll"},
255 #define S_CPU S_COLL+1
256 {fcpu, "cpu"},
257 #define S_DISKS S_CPU+1
258 {fdisks, "disks"},
259 #define S_ERR S_DISKS+1
260 {ferr, "err"},
261 #define S_IERR S_ERR+1
262 {fierr, "ierr"},
263 #define S_INTR S_IERR+1
264 {fintr, "intr"},
265 #define S_IPKT S_INTR+1
266 {fipkt, "ipkt"},
267 #define S_LOAD S_IPKT+1
268 {fload, "load"},
269 #define S_OERR S_LOAD+1
270 {foerr, "oerr"},
271 #define S_OPKT S_OERR+1
272 {fopkt, "opkt"},
273 #define S_PAGE S_OPKT+1
274 {fpage, "page"},
275 #define S_PGPGIN S_PAGE+1
276 {fpgpgin, "pgpgin"},
277 #define S_PGPGOUT S_PGPGIN+1
278 {fpgpgout, "pgpgout"},
279 #define S_PKTS S_PGPGOUT+1
280 {fpkts, "pkts"},
281 #define S_PSWPIN S_PKTS+1
282 {fpswpin, "pswpin"},
283 #define S_PSWPOUT S_PSWPIN+1
284 {fpswpout, "pswpout"},
285 #define S_SWAP S_PSWPOUT+1
286 {fswap, "swap"},
287 #define S_SWT S_SWAP+1
288 {fswt, "swt"},
289 #define S_SYS S_SWT+1
290 {fsys, "sys"},
291 #define S_USER S_SYS+1
292 {fuser, "user"},
293 };
294 #define DEFSTATNAME "load"
295 #define MAXSTAT (sizeof(sd) / sizeof(STATDATA))
296 #define MAXSTATNAME 10 /* Max length of stat name */
297 #define MAXSTATVALUE 10 /* Max length of stat value */
298
299 /*
300 * xmeter supports 4 background colors and pixmaps that it will switch
301 * between as a meter changes state, called OK, WARN, ERROR and FATAL.
302 * The background bitmaps are stored in BITMAP structures.
303 */
304 typedef struct {
305 int w; /* Width of bitmap */
306 int h; /* Height of bitmap */
307 Pixmap bm; /* Bitmap */
308 } BITMAP;
309
310 /*
311 * Application specific resources are stored in an APPRESOURCES structure.
312 **/
313 typedef struct {
314 Pixel back[MAXBACKS]; /* Meter backgrounds */
315 Pixel bd[MAXBACKS]; /* Meter borders */
316 BITMAP bitmap[MAXBACKS]; /* Stripchart bg pixmaps*/
317 int columns; /* Columns to display */
318 String defStat; /* Default stat */
319 Boolean displayValue; /* Show value in label */
320 int errorLevel; /* Error level */
321 Pixel fore[MAXBACKS]; /* Meter foregrounds */
322 Pixel hl[MAXBACKS]; /* Meter highlights */
323 Pixel ibd[MAXBACKS]; /* Meter internal bds */
324 Pixel lback[MAXBACKS]; /* Label backgrounds */
325 Pixel lfore[MAXBACKS]; /* Label foregrounds */
326 String prog[MAXBACKS]; /* Alert programs */
327 int retries; /* RPC retries */
328 int rows; /* Rows to display */
329 int scales[MAXSTAT]; /* Scale for each stat */
330 Boolean shortName; /* Short hostname flag */
331 int timeout; /* RPC timeout */
332 int warnLevel; /* Warning level */
333 } APPRESOURCES;
334
335 /*
336 * Global variables.
337 */
338 static char *progname;
339 static Pixmap bitmapdef = XtUnspecifiedPixmap;
340 static struct timeval ptto = {0, 0}; /* RPC timeout interval */
341 static struct timeval tto = {0, 0}; /* Total timeout */
342 static SHMETER *shmeters = NULL; /* List of SHMETERs */
343 static METER *selected; /* Current meter */
344 static METER *meterlist = NULL; /* List of METERs */
345 static int loadscaledef = FSCALE; /* Scale for load ave. */
346 static Widget popup; /* Used by pop[up|down]scale */
347 static APPRESOURCES ar; /* App specific resources */
348
349 #define offset(f) XtOffsetOf(APPRESOURCES, f)
350 static XtResource resources[] = {
351 {"columns", "Columns", XtRInt, sizeof(int),
352 offset(columns), XtRString, ""},
353 {"defStat", "DefStat", XtRString, sizeof(char *),
354 offset(defStat), XtRString, DEFSTATNAME},
355 {"displayValue", "DisplayValue", XtRBoolean, sizeof(Boolean),
356 offset(displayValue), XtRString, "False"},
357 {"errorBack", XtCBackground, XtRPixel, sizeof(Pixel),
358 offset(back[ERROR]), XtRString, XtDefaultBackground},
359 {"errorBd", XtCForeground, XtRPixel, sizeof(Pixel),
360 offset(bd[ERROR]), XtRString, XtDefaultForeground},
361 {"errorBitmap", XtCBitmap, XtRBitmap, sizeof(Pixmap),
362 offset(bitmap[ERROR].bm), XtRBitmap,(caddr_t) &bitmapdef},
363 {"errorFore", XtCForeground, XtRPixel, sizeof(Pixel),
364 offset(fore[ERROR]), XtRString, XtDefaultForeground},
365 {"errorHl", XtCForeground, XtRPixel, sizeof(Pixel),
366 offset(hl[ERROR]), XtRString, XtDefaultForeground},
367 {"errorIbd", XtCForeground, XtRPixel, sizeof(Pixel),
368 offset(ibd[ERROR]), XtRString, XtDefaultForeground},
369 {"errorLevel", "ErrorLevel", XtRInt, sizeof(int),
370 offset(errorLevel), XtRString, "6"},
371 {"errorProg", "Program", XtRString, sizeof(char *),
372 offset(prog[ERROR]), XtRString, NULL},
373 {"fatalBack", XtCBackground, XtRPixel, sizeof(Pixel),
374 offset(back[FATAL]), XtRString, XtDefaultBackground},
375 {"fatalBd", XtCForeground, XtRPixel, sizeof(Pixel),
376 offset(bd[FATAL]), XtRString, XtDefaultForeground},
377 {"fatalBitmap", XtCBitmap, XtRBitmap, sizeof(Pixmap),
378 offset(bitmap[FATAL].bm), XtRBitmap,(caddr_t) &bitmapdef},
379 {"fatalFore", XtCForeground, XtRPixel, sizeof(Pixel),
380 offset(fore[FATAL]), XtRString, XtDefaultForeground},
381 {"fatalHl", XtCForeground, XtRPixel, sizeof(Pixel),
382 offset(hl[FATAL]), XtRString, XtDefaultForeground},
383 {"fatalIbd", XtCForeground, XtRPixel, sizeof(Pixel),
384 offset(ibd[FATAL]), XtRString, XtDefaultForeground},
385 {"fatalProg", "Program", XtRString, sizeof(char *),
386 offset(prog[FATAL]), XtRString, NULL},
387 {"lErrorBack", XtCBackground, XtRPixel, sizeof(Pixel),
388 offset(lback[ERROR]), XtRString, XtDefaultBackground},
389 {"lErrorFore", XtCForeground, XtRPixel, sizeof(Pixel),
390 offset(lfore[ERROR]), XtRString, XtDefaultForeground},
391 {"lFatalBack", XtCBackground, XtRPixel, sizeof(Pixel),
392 offset(lback[FATAL]), XtRString, XtDefaultBackground},
393 {"lFatalFore", XtCForeground, XtRPixel, sizeof(Pixel),
394 offset(lfore[FATAL]), XtRString, XtDefaultForeground},
395 {"lOkBack", XtCBackground, XtRPixel, sizeof(Pixel),
396 offset(lback[OK]), XtRString, XtDefaultBackground},
397 {"lOkFore", XtCForeground, XtRPixel, sizeof(Pixel),
398 offset(lfore[OK]), XtRString, XtDefaultForeground},
399 {"lWarnBack", XtCBackground, XtRPixel, sizeof(Pixel),
400 offset(lback[WARN]), XtRString, XtDefaultBackground},
401 {"lWarnFore", XtCForeground, XtRPixel, sizeof(Pixel),
402 offset(lfore[WARN]), XtRString, XtDefaultForeground},
403 {"okBack", XtCBackground, XtRPixel, sizeof(Pixel),
404 offset(back[OK]), XtRString, XtDefaultBackground},
405 {"okBd", XtCForeground, XtRPixel, sizeof(Pixel),
406 offset(bd[OK]), XtRString, XtDefaultForeground},
407 {"okBitmap", XtCBitmap, XtRBitmap, sizeof(Pixmap),
408 offset(bitmap[OK].bm), XtRBitmap, (caddr_t) &bitmapdef},
409 {"okFore", XtCForeground, XtRPixel, sizeof(Pixel),
410 offset(fore[OK]), XtRString, XtDefaultForeground},
411 {"okHl", XtCForeground, XtRPixel, sizeof(Pixel),
412 offset(hl[OK]), XtRString, XtDefaultForeground},
413 {"okIbd", XtCForeground, XtRPixel, sizeof(Pixel),
414 offset(ibd[OK]), XtRString, XtDefaultForeground},
415 {"okProg", "Program", XtRString, sizeof(char *),
416 offset(prog[OK]), XtRString, NULL},
417 {"retries", "Retries", XtRInt, sizeof(int),
418 offset(retries), XtRString, "2"},
419 {"rows", "Rows", XtRInt, sizeof(int),
420 offset(rows), XtRString, ""},
421 {"scaleColl", "ScaleColl", XtRInt, sizeof(int),
422 offset(scales[S_COLL]), XtRString, "1"},
423 {"scaleCpu", "ScaleCpu", XtRInt, sizeof(int),
424 offset(scales[S_CPU]), XtRString, "20"},
425 {"scaleDisks", "ScaleDisks", XtRInt, sizeof(int),
426 offset(scales[S_DISKS]), XtRString, "40"},
427 {"scaleErr", "ScaleErr", XtRInt, sizeof(int),
428 offset(scales[S_ERR]), XtRString, "1"},
429 {"scaleIerr", "ScaleIerr", XtRInt, sizeof(int),
430 offset(scales[S_IERR]), XtRString, "1"},
431 {"scaleIntr", "ScaleIntr", XtRInt, sizeof(int),
432 offset(scales[S_INTR]), XtRString, "50"},
433 {"scaleIpkt", "ScaleIpkt", XtRInt, sizeof(int),
434 offset(scales[S_IPKT]), XtRString, "20"},
435 {"scaleLoad", "ScaleLoad", XtRInt, sizeof(int),
436 offset(scales[S_LOAD]), XtRInt, (caddr_t) &loadscaledef},
437 {"scaleOerr", "ScaleOerr", XtRInt, sizeof(int),
438 offset(scales[S_OERR]), XtRString, "1"},
439 {"scaleOpkt", "ScaleOpkt", XtRInt, sizeof(int),
440 offset(scales[S_OPKT]), XtRString, "20"},
441 {"scalePage", "ScalePage", XtRInt, sizeof(int),
442 offset(scales[S_PAGE]), XtRString, "10"},
443 {"scalePgpgin", "ScalePgpgin", XtRInt, sizeof(int),
444 offset(scales[S_PGPGIN]), XtRString, "10"},
445 {"scalePgpgout", "ScalePgpgout", XtRInt, sizeof(int),
446 offset(scales[S_PGPGOUT]), XtRString, "10"},
447 {"scalePkts", "ScalePkts", XtRInt, sizeof(int),
448 offset(scales[S_PKTS]), XtRString, "40"},
449 {"scalePswpin", "ScalePswpin", XtRInt, sizeof(int),
450 offset(scales[S_PSWPIN]), XtRString, "5"},
451 {"scalePswpout", "ScalePswpout", XtRInt, sizeof(int),
452 offset(scales[S_PSWPOUT]), XtRString, "5"},
453 {"scaleSwap", "ScaleSwap", XtRInt, sizeof(int),
454 offset(scales[S_SWAP]), XtRString, "5"},
455 {"scaleSwt", "ScaleSwt", XtRInt, sizeof(int),
456 offset(scales[S_SWT]), XtRString, "30"},
457 {"scaleSys", "ScaleSys", XtRInt, sizeof(int),
458 offset(scales[S_SYS]), XtRString, "20"},
459 {"scaleUser", "ScaleUser", XtRInt, sizeof(int),
460 offset(scales[S_USER]), XtRString, "20"},
461 {"shortName", "Shortname", XtRBoolean, sizeof(Boolean),
462 offset(shortName), XtRString, "False"},
463 {"timeout", "Timeout", XtRInt, sizeof(int),
464 offset(timeout), XtRString, "5"},
465 {"warnBack", XtCBackground, XtRPixel, sizeof(Pixel),
466 offset(back[WARN]), XtRString, XtDefaultBackground},
467 {"warnBd", XtCForeground, XtRPixel, sizeof(Pixel),
468 offset(bd[WARN]), XtRString, XtDefaultForeground},
469 {"warnBitmap", XtCBitmap, XtRBitmap, sizeof(Pixmap),
470 offset(bitmap[WARN].bm), XtRBitmap, (caddr_t) &bitmapdef},
471 {"warnFore", XtCForeground, XtRPixel, sizeof(Pixel),
472 offset(fore[WARN]), XtRString, XtDefaultForeground},
473 {"warnHl", XtCForeground, XtRPixel, sizeof(Pixel),
474 offset(hl[WARN]), XtRString, XtDefaultForeground},
475 {"warnIbd", XtCForeground, XtRPixel, sizeof(Pixel),
476 offset(ibd[WARN]), XtRString, XtDefaultForeground},
477 {"warnLevel", "WarnLevel", XtRInt, sizeof(int),
478 offset(warnLevel), XtRString, "3"},
479 {"warnProg", "Program", XtRString, sizeof(char *),
480 offset(prog[WARN]), XtRString, NULL},
481 };
482 static XrmOptionDescRec options[] = {
483 {"-cols", "columns", XrmoptionSepArg, NULL},
484 {"-dv", "displayValue", XrmoptionNoArg, "True"},
485 {"-ebd", "errorBd", XrmoptionSepArg, NULL},
486 {"-ebg", "errorBack", XrmoptionSepArg, NULL},
487 {"-efg", "errorFore", XrmoptionSepArg, NULL},
488 {"-ehl", "errorHl", XrmoptionSepArg, NULL},
489 {"-eibd", "errorIbd", XrmoptionSepArg, NULL},
490 {"-elevel", "errorLevel", XrmoptionSepArg, NULL},
491 {"-ep", "errorProg", XrmoptionSepArg, NULL},
492 {"-fbd", "fatalBd", XrmoptionSepArg, NULL},
493 {"-fbg", "fatalBack", XrmoptionSepArg, NULL},
494 {"-ffg", "fatalFore", XrmoptionSepArg, NULL},
495 {"-fhl", "fatalHl", XrmoptionSepArg, NULL},
496 {"-fibd", "fatalIbd", XrmoptionSepArg, NULL},
497 {"-fp", "fatalProg", XrmoptionSepArg, NULL},
498 {"-h", "*Paned.height", XrmoptionSepArg, NULL},
499 {"-lebg", "lErrorBack", XrmoptionSepArg, NULL},
500 {"-lefg", "lErrorFore", XrmoptionSepArg, NULL},
501 {"-lfbg", "lFatalBack", XrmoptionSepArg, NULL},
502 {"-lffg", "lFatalFore", XrmoptionSepArg, NULL},
503 {"-lobg", "lOkBack", XrmoptionSepArg, NULL},
504 {"-lofg", "lOkFore", XrmoptionSepArg, NULL},
505 {"-lwbg", "lWarnBack", XrmoptionSepArg, NULL},
506 {"-lwfg", "lWarnFore", XrmoptionSepArg, NULL},
507 {"-obd", "okBd", XrmoptionSepArg, NULL},
508 {"-obg", "okBack", XrmoptionSepArg, NULL},
509 {"-ofg", "okFore", XrmoptionSepArg, NULL},
510 {"-ohl", "okHl", XrmoptionSepArg, NULL},
511 {"-oibd", "okIbd", XrmoptionSepArg, NULL},
512 {"-op", "okProg", XrmoptionSepArg, NULL},
513 {"-rows", "rows", XrmoptionSepArg, NULL},
514 {"-rt", "retries", XrmoptionSepArg, NULL},
515 {"-scoll", "scaleColl", XrmoptionSepArg, NULL},
516 {"-scpu", "scaleCpu", XrmoptionSepArg, NULL},
517 {"-sdisks", "scaleDisks", XrmoptionSepArg, NULL},
518 {"-serr", "scaleErr", XrmoptionSepArg, NULL},
519 {"-sierr", "scaleIerr", XrmoptionSepArg, NULL},
520 {"-sintr", "scaleIntr", XrmoptionSepArg, NULL},
521 {"-sipkt", "scaleIpkt", XrmoptionSepArg, NULL},
522 {"-sload", "scaleLoad", XrmoptionSepArg, NULL},
523 {"-sn", "shortName", XrmoptionNoArg, "True"},
524 {"-soerr", "scaleOerr", XrmoptionSepArg, NULL},
525 {"-sopkt", "scaleOpkt", XrmoptionSepArg, NULL},
526 {"-spage", "scalePage", XrmoptionSepArg, NULL},
527 {"-spgpgin", "scalePgpgin", XrmoptionSepArg, NULL},
528 {"-spgpgout", "scalePgpgout", XrmoptionSepArg, NULL},
529 {"-spkts", "scalePkts", XrmoptionSepArg, NULL},
530 {"-spswpin", "scalePswpin", XrmoptionSepArg, NULL},
531 {"-spswpout", "scalePswpout", XrmoptionSepArg, NULL},
532 {"-sswap", "scaleSwap", XrmoptionSepArg, NULL},
533 {"-sswt", "scaleSwt", XrmoptionSepArg, NULL},
534 {"-ssys", "scaleSys", XrmoptionSepArg, NULL},
535 {"-suser", "scaleUser", XrmoptionSepArg, NULL},
536 {"-to", "timeout", XrmoptionSepArg, NULL},
537 {"-update", "*StripChart.update", XrmoptionSepArg, NULL},
538 {"-w", "*Paned.width", XrmoptionSepArg, NULL},
539 {"-wbd", "warnBd", XrmoptionSepArg, NULL},
540 {"-wbg", "warnBack", XrmoptionSepArg, NULL},
541 {"-wfg", "warnFore", XrmoptionSepArg, NULL},
542 {"-whl", "warnHl", XrmoptionSepArg, NULL},
543 {"-wibd", "warnIbd", XrmoptionSepArg, NULL},
544 {"-wlevel", "warnLevel", XrmoptionSepArg, NULL},
545 {"-wp", "warnProg", XrmoptionSepArg, NULL},
546 };
547
548 static XtActionsRec scact[] = {
549 {"popupscale", popupscale},
550 {"popdownscale", popdownscale}
551 };
552 #define NUMSCACT (sizeof(scact) / sizeof(XtActionsRec))
553
554 static XtActionsRec actions[] = {
555 {"quit", quit},
556 };
557 Atom wm_delete_window;
558
559 main(argc, argv)
560
561 int argc;
562 char **argv;
563
564 {
565 Widget toplevel;
566 Widget form;
567 Widget pane = NULL;
568 int i;
569 int n;
570 Arg args[10];
571 XtAppContext appcon;
572 String mbtrans =
573 "<EnterWindow>: highlight()\n\
574 <LeaveWindow>: reset()\n\
575 <BtnDown>: set() notify() PopupMenu()";
576 XtTranslations mbtt;
577 String sctrans =
578 "<BtnDown>: popupscale()\n\
579 <BtnUp>: popdownscale()";
580 XtTranslations scacttt;
581 Widget *lw; /* Last column or row of widgets */
582 int numwgt;
583
584 progname = argv[0];
585 toplevel = XtAppInitialize(&appcon, "XMeter", options,
586 XtNumber(options), &argc, argv, NULL, NULL, ZERO);
587 if (argc < 2)
588 usage();
589 XtGetApplicationResources(toplevel, (XtPointer) &ar,
590 resources, XtNumber(resources), NULL, ZERO);
591 form = XtCreateManagedWidget("form", formWidgetClass, toplevel, NULL, ZERO);
592 /* Handle f.delete */
593 XtAppAddActions(XtWidgetToApplicationContext(toplevel), actions,
594 XtNumber(actions));
595 XtOverrideTranslations(toplevel,
596 XtParseTranslationTable("<Message>WM_PROTOCOLS: quit()"));
597 if (strcmp("-v", argv[1]) == 0)
598 printversion();
599 init(toplevel, &lw);
600 createmenus(form);
601 mbtt = XtParseTranslationTable(mbtrans);
602 scacttt = XtParseTranslationTable(sctrans);
603 XtAppAddActions(appcon, scact, NUMSCACT);
604 /*
605 * Each meter consists of a paned widget, each paned widget has two
606 * children, which are a menubutton widget and stripchart widget.
607 * The following loop creates and initializes the appropriate widgets.
608 */
609 for (i = 1, numwgt = 0; i < argc; i++, numwgt++) {
610 meterlist = initmeter(meterlist, &i, argc, argv);
611 /*
612 * Create paned widget.
613 */
614 n = 0;
615 if (ar.columns > 0) { /* Locate pane in form ... */
616 XtSetArg(args[n], XtNfromVert, lw[numwgt % ar.columns]); n++;
617 if (numwgt % ar.columns != 0) {
618 XtSetArg(args[n], XtNfromHoriz, pane); n++;
619 }
620 } else {
621 XtSetArg(args[n], XtNfromHoriz, lw[numwgt % ar.rows]); n++;
622 if (numwgt % ar.rows != 0) {
623 XtSetArg(args[n], XtNfromVert, pane); n++;
624 }
625 }
626 XtSetArg(args[n], XtNborder, ar.bd[OK]); n++;
627 XtSetArg(args[n], XtNinternalBorderColor, ar.ibd[OK]); n++;
628 pane = XtCreateManagedWidget(meterlist->sh->label, panedWidgetClass,
629 form, args, n);
630 lw[numwgt % (ar.columns > 0 ? ar.columns : ar.rows)] = pane;
631 meterlist->pdwidget = pane;
632 /*
633 * Create menubutton widget.
634 */
635 n = 0;
636 XtSetArg(args[n], XtNshowGrip, XtEno); n++;
637 XtSetArg(args[n], XtNlabel, meterlist->label); n++;
638 XtSetArg(args[n], XtNbackground, ar.lback[OK]); n++;
639 XtSetArg(args[n], XtNforeground, ar.lfore[OK]); n++;
640 XtSetArg(args[n], XtNmenuName, STATMENU); n++;
641 XtSetArg(args[n], XtNtranslations, mbtt); n++;
642 meterlist->mbwidget = XtCreateManagedWidget("menu",
643 menuButtonWidgetClass, pane, args, n);
644 XtRemoveAllCallbacks(meterlist->mbwidget, XtNcallback);
645 XtAddCallback(meterlist->mbwidget, XtNcallback,
646 (XtCallbackProc) selecthost, (XtPointer) meterlist);
647 /*
648 * Create stripchart widget.
649 */
650 n = 0;
651 XtSetArg(args[n], XtNfromVert, meterlist->mbwidget); n++;
652 XtSetArg(args[n], XtNresizable, TRUE); n++;
653 XtSetArg(args[n], XtNbackground, ar.back[OK]); n++;
654 XtSetArg(args[n], XtNforeground, ar.fore[OK]); n++;
655 XtSetArg(args[n], XtNhighlight, ar.hl[OK]); n++;
656 XtSetArg(args[n], XtNtranslations, scacttt); n++;
657 meterlist->scwidget = XtCreateManagedWidget("load",
658 stripChartWidgetClass, pane, args, n);
659 XtRemoveAllCallbacks(meterlist->scwidget, XtNgetValue);
660 XtAddCallback(meterlist->scwidget, XtNgetValue,
661 (XtCallbackProc) getstatus, (XtPointer) meterlist);
662 }
663 free(lw);
664 #ifdef SVR4
665 sigset(SIGCHLD, freechild);
666 #else
667 signal(SIGCHLD, freechild); /* Clean up after our children */
668 #endif
669 XtRealizeWidget(toplevel);
670 wm_delete_window = XInternAtom(XtDisplay(toplevel), "WM_DELETE_WINDOW",
671 False);
672 XSetWMProtocols(XtDisplay(toplevel), XtWindow(toplevel),
673 &wm_delete_window, 1);
674 setokbackgrounds(XtDisplay(toplevel), XtScreen(toplevel));
675 XtAppMainLoop(appcon);
676 return(0);
677 }
678
679 usage()
680
681 {
682 int i;
683
684 fprintf(stderr, "Usage: %s [toolkit options] [options] [stat] host ...\n",
685 progname);
686 fprintf(stderr, " Options are:");
687 for (i = 0; i < XtNumber(options); i++) {
688 if (i % 8 == 0)
689 fprintf(stderr, "\n ");
690 else
691 fprintf(stderr, ", ");
692 fprintf(stderr, "%s", options[i].option);
693 }
694 fprintf(stderr, ", -v\n Stats to watch are:");
695 for (i = 0; i < MAXSTAT; i++) {
696 if (i % 8 == 0)
697 fprintf(stderr, "\n ");
698 else
699 fprintf(stderr, ", ");
700 fprintf(stderr, "-%s", sd[i].name);
701 }
702 fprintf(stderr, "\n");
703 exit(1);
704 }
705
706 /*
707 * printversion - Print version number and exit.
708 */
709 printversion()
710
711 {
712 printf("XMeter version %d.%d\n", MAJORVERSION, PATCHLEVEL);
713 exit(0);
714 }
715
716 /*
717 * init - Initialze various globals.
718 */
719 init(toplevel, lw)
720
721 Widget toplevel;
722 Widget **lw;
723
724 {
725 int i;
726 Window rw;
727 int x;
728 int y;
729 int bwidth;
730 int depth;
731
732 ptto.tv_sec = ar.timeout;
733 tto.tv_sec = ar.timeout * ar.retries;
734 if (ar.columns <= 0 && ar.rows <= 0)
735 ar.columns = 1;
736 if (ar.columns > 0) {
737 if ((*lw = (Widget *) calloc(ar.columns, sizeof(Widget))) == NULL)
738 fatal("lw");
739 } else
740 if ((*lw = (Widget *) calloc(ar.rows, sizeof(Widget))) == NULL)
741 fatal("lw");
742 for (i = 0; i < MAXBACKS; i++)
743 if (ar.bitmap[i].bm != bitmapdef) /* Get bitmap dimensions */
744 XGetGeometry(XtDisplay(toplevel), ar.bitmap[i].bm, &rw, &x, &y,
745 (unsigned int *) &ar.bitmap[i].w,
746 (unsigned int *) &ar.bitmap[i].h,
747 (unsigned int *) &bwidth, (unsigned int *) &depth);
748 }
749
750 /*
751 * createmenus - Create menus used by each meter. Currently there's just
752 * one which consists of the stats that can be monitored, and a quit
753 * command.
754 */
755 createmenus(parent)
756
757 Widget parent;
758
759 {
760 int i;
761 int n;
762 Widget menu;
763 Widget me;
764 Arg args[10];
765
766 menu = XtCreatePopupShell(STATMENU, simpleMenuWidgetClass, parent,
767 NULL, ZERO);
768 for (i = 0; i < MAXSTAT; i++) {
769 n = 0;
770 XtSetArg(args[n], XtNlabel, sd[i].name); n++;
771 me = XtCreateManagedWidget(sd[i].name, smeBSBObjectClass, menu,
772 args, n);
773 XtRemoveAllCallbacks(me, XtNcallback);
774 XtAddCallback(me, XtNcallback,
775 (XtCallbackProc) changestat, (XtPointer) i);
776 }
777 n = 0;
778 XtSetArg(args[n], XtNlabel, "quit"); n++;
779 me = XtCreateManagedWidget("quit", smeBSBObjectClass, menu, args, n);
780 XtRemoveAllCallbacks(me, XtNcallback);
781 XtAddCallback(me, XtNcallback, (XtCallbackProc) quit, (XtPointer) NULL);
782 }
783
784 void
785 popupscale(w, event, params, num)
786
787 Widget w;
788 XEvent *event;
789 String *params;
790 Cardinal num;
791
792 {
793 int n;
794 Arg args[10];
795 Position x;
796 Position y;
797 static char buf[100];
798 METER *h;
799 time_t tdown;
800
801 for (h = meterlist; h; h = h->nxt)
802 if (h->scwidget == w)
803 break;
804 if (!h) {
805 fprintf(stderr, "popupscale: Bad stripchart widget\n");
806 exit(1);
807 }
808 XtTranslateCoords(w, (Position) 0, (Position) 0, &x, &y);
809 n = 0;
810 XtSetArg(args[n], XtNx, x); n++;
811 XtSetArg(args[n], XtNy, y); n++;
812 popup = XtCreatePopupShell("popup", transientShellWidgetClass, w, args, n);
813 if (h->sh->pid != -1) {
814 tdown = time(0) - h->sh->when;
815 sprintf(buf, "%s\nversion %d.%d\nscale %d\ndown %02d:%02d:%02d",
816 h->sh->name,
817 MAJORVERSION, PATCHLEVEL,
818 h->stat == S_LOAD ? ar.scales[h->stat] / FSCALE :
819 ar.scales[h->stat],
820 tdown / (60*60), (tdown / 60) % 60, tdown % 60);
821 } else
822 sprintf(buf, "%s\nversion %d.%d\nscale %d",
823 h->sh->name,
824 MAJORVERSION, PATCHLEVEL,
825 h->stat == S_LOAD ? ar.scales[h->stat] / FSCALE :
826 ar.scales[h->stat]);
827 n = 0;
828 XtSetArg(args[n], XtNlabel, buf); n++;
829 XtCreateManagedWidget("label", labelWidgetClass, popup, args, n);
830 XtPopup(popup, XtGrabNone);
831 }
832
833 void
834 popdownscale(w, event, params, num)
835
836 Widget w;
837 XEvent *event;
838 String *params;
839 Cardinal num;
840
841 {
842 if (popup) /* Can be NULL in some cases apparently */
843 XtDestroyWidget(popup);
844 }
845
846 /*
847 * setokbackgrounds - Set initial background pixmaps. Can't do this
848 * when the stripchart widgets are created as they have to be
849 * realized before the pixmaps can be created.
850 */
851 setokbackgrounds(d, s)
852
853 Display *d;
854 Screen *s;
855
856 {
857 METER *h;
858 int n;
859 Arg args[10];
860
861 if (ar.bitmap[OK].bm != bitmapdef) {
862 for (h = meterlist; h; h = h->nxt) {
863 h->pm = XmuCreatePixmapFromBitmap(d, XtWindow(h->scwidget),
864 ar.bitmap[OK].bm,
865 ar.bitmap[OK].w, ar.bitmap[OK].h,
866 DefaultDepthOfScreen(s),
867 ar.fore[OK],
868 ar.back[OK]);
869 n = 0;
870 XtSetArg(args[n], XtNbackgroundPixmap, h->pm); n++;
871 XtSetValues(h->scwidget, args, n);
872 }
873 }
874 }
875
876 /*
877 * freechild - Clean up child processes.
878 */
879 SIGTYPE
880 freechild(sig)
881
882 int sig;
883
884 {
885 int pid;
886 METER *h;
887
888 pid = wait(NULL);
889 #ifdef SYSV
890 #ifdef SVR4
891 sigset(SIGCHLD, freechild);
892 #else !SVR4
893 signal(SIGCHLD, freechild);
894 #endif SVR4
895 #endif SYSV
896 if (pid <= 0)
897 return;
898 for (h = meterlist; h; h = h->nxt)
899 if (h->sh->pid == pid) { /* Start updating host again */
900 h->sh->pid = -1;
901 break;
902 }
903 }
904
905 /*
906 * getstatus - Get status from remote host, update meter appropriately,
907 * including changing colors, background pixmap and label if necessary.
908 */
909 getstatus(w, h, data)
910
911 Widget w;
912 METER *h;
913 char *data;
914
915 {
916 int l;
917 int n;
918 int s;
919 register SHMETER *sh;
920 Arg args[10];
921
922 sh = h->sh;
923 if (h->oldstate == FATAL && sh->pid != -1) { /* Ignore dead hosts */
924 *(double *) data = 0.0;
925 return;
926 }
927 if (h->oldjumpscroll) { /* Restore old jumpscroll value */
928 n = 0;
929 XtSetArg(args[n], XtNjumpScroll, h->oldjumpscroll); n++;
930 XtSetValues(h->scwidget, args, n);
931 h->oldjumpscroll = 0;
932 }
933 s = state(l = getmeter(h), h);
934 if (ar.displayValue) {
935 n = 0; /* Update menubutton */
936 XtSetArg(args[n], XtNbackground, ar.lback[s]); n++;
937 XtSetArg(args[n], XtNforeground, ar.lfore[s]); n++;
938 sprintf(h->label, "%s %s %.2f", sh->label,
939 s == FATAL ? DMSG : sd[h->stat].name,
940 s == FATAL ? 0.0 : (float) l / ar.scales[h->stat]);
941 XtSetArg(args[n], XtNlabel, h->label); n++;
942 XtSetValues(h->mbwidget, args, n);
943 }
944 *(double *) data = (s == FATAL) ? 0.0 : (double) l / ar.scales[h->stat];
945 if (s != h->oldstate) {
946 sh->when = time(0);
947 if (ar.bitmap[h->oldstate].bm != bitmapdef)
948 XFreePixmap(XtDisplay(w), h->pm);
949 n = 0; /* Update stripchart widget */
950 XtSetArg(args[n], XtNbackground, ar.back[s]); n++;
951 XtSetArg(args[n], XtNforeground, ar.fore[s]); n++;
952 XtSetArg(args[n], XtNhighlight, ar.hl[s]); n++;
953 if (ar.bitmap[s].bm != bitmapdef) {
954 h->pm = XmuCreatePixmapFromBitmap(XtDisplay(w),
955 XtWindow(w),
956 ar.bitmap[s].bm,
957 ar.bitmap[s].w, ar.bitmap[s].h,
958 DefaultDepthOfScreen(XtScreen(w)),
959 ar.fore[s],
960 ar.back[s]);
961 XtSetArg(args[n], XtNbackgroundPixmap, h->pm); n++;
962 } else if (ar.bitmap[h->oldstate].bm != bitmapdef) {
963 XtSetArg(args[n], XtNbackgroundPixmap, XtUnspecifiedPixmap); n++;
964 }
965 XtSetValues(w, args, n);
966 n = 0; /* Update menubutton widget */
967 XtSetArg(args[n], XtNbackground, ar.lback[s]); n++;
968 XtSetArg(args[n], XtNforeground, ar.lfore[s]); n++;
969 if (ar.displayValue) {
970 sprintf(h->label, "%s %s %.2f", sh->label,
971 s == FATAL ? DMSG : sd[h->stat].name,
972 s == FATAL ? 0.0 : (float) l / ar.scales[h->stat]);
973 XtSetArg(args[n], XtNlabel, h->label); n++;
974 } else if (s == FATAL || h->oldstate == FATAL) {
975 sprintf(h->label, "%s %s", sh->label,
976 s == FATAL ? DMSG : sd[h->stat].name);
977 XtSetArg(args[n], XtNlabel, h->label); n++;
978 }
979 XtSetValues(h->mbwidget, args, n);
980 n = 0; /* Update paned widget */
981 XtSetArg(args[n], XtNborder, ar.bd[s]); n++;
982 XtSetArg(args[n], XtNinternalBorderColor, ar.ibd[s]); n++;
983 XtSetValues(h->pdwidget, args, n);
984 if (ar.prog[s])
985 runprog(h, s);
986 if (s == FATAL)
987 sh->pid = waitforhost(h);
988 }
989 h->oldstate = s;
990 }
991
992 /*
993 * waitforhost - Fork process which will wait for host to come back up.
994 * Child exits if parent is init (pid 1), to handle case where parent
995 * has gone away.
996 */
997 int
998 waitforhost(h)
999
1000 METER *h;
1001
1002 {
1003 int pid;
1004
1005 if (pid = fork())
1006 return(pid);
1007 #ifdef SVR4
1008 sigset(SIGTERM, SIG_DFL);
1009 #else !SVR4
1010 signal(SIGTERM, SIG_DFL);
1011 #endif !SVR4
1012 while (1) {
1013 sleep(10);
1014 if (getport(h) > 0 || getppid() < 2)
1015 exit(0);
1016 }
1017 }
1018
1019 /*
1020 * runprog - Run user specified alert program.
1021 */
1022 runprog(h, s)
1023
1024 METER *h;
1025 int s;
1026
1027 {
1028 char oldstate[4];
1029 char state[4];
1030
1031 sprintf(oldstate, "%d", h->oldstate);
1032 sprintf(state, "%d", s);
1033 if (vfork())
1034 return; /* Parent just returns */
1035 execlp(ar.prog[s], ar.prog[s], oldstate, state,
1036 h->sh->label, sd[h->stat].name, 0);
1037 fatal(ar.prog[s]);
1038 }
1039
1040 /*
1041 * selecthost - Select host for next changestat().
1042 */
1043 selecthost(w, h, data)
1044
1045 Widget w;
1046 METER *h;
1047 char *data;
1048
1049 {
1050 selected = h;
1051 }
1052
1053 /*
1054 * changestat - Change statistic we're looking at. To clear the stripchart
1055 * it's necessary to poke the "interval" field in the StripChartWidget
1056 * structure, and then set "jumpScroll" to the width of the stripchart.
1057 * jumpScroll is saved here and restored in getstatus().
1058 */
1059 #include <X11/IntrinsicP.h>
1060 #include <X11/Xaw/StripCharP.h>
1061 changestat(w, statidx, data)
1062
1063 Widget w;
1064 int statidx;
1065 char *data;
1066
1067 {
1068 int n;
1069 Arg args[10];
1070 int width;
1071
1072 selected->stat = statidx;
1073 sprintf(selected->label, "%s %s", selected->sh->label, sd[statidx].name);
1074 n = 0;
1075 XtSetArg(args[n], XtNlabel, selected->label); n++;
1076 XtSetValues(selected->mbwidget, args, n);
1077 n = 0;
1078 XtSetArg(args[n], XtNwidth, &width); n++;
1079 XtSetArg(args[n], XtNjumpScroll, &selected->oldjumpscroll); n++;
1080 XtGetValues(selected->scwidget, args, n);
1081 n = 0;
1082 XtSetArg(args[n], XtNjumpScroll, width); n++;
1083 XtSetValues(selected->scwidget, args, n);
1084 ((StripChartWidget) selected->scwidget)->strip_chart.interval = width;
1085 }
1086
1087 /*
1088 * state - Return state of current stat.
1089 */
1090 int
1091 state(l, h)
1092
1093 int l;
1094 METER *h;
1095
1096 {
1097 if (l < 0)
1098 return(FATAL);
1099 else if (l < ar.warnLevel * ar.scales[h->stat])
1100 return(OK);
1101 else if (l < ar.errorLevel * ar.scales[h->stat])
1102 return(WARN);
1103 else
1104 return(ERROR);
1105 }
1106
1107 /*
1108 * The following functions return the value of the specified stat, which
1109 * is normally computed by taking the difference between the current
1110 * value and the previous value, and dividing by the update interval in
1111 * order to get the current rate.
1112 */
1113 #define DIF(m,fld) (m->sh->st[m->sh->idx].fld - \
1114 m->sh->st[m->sh->idx ^ 1].fld)
1115 #define INT(m) (DIF(m,curtime.tv_sec) <= 0 ? 1 : \
1116 DIF(m,curtime.tv_sec))
1117
1118 int
1119 fcoll(h)
1120
1121 METER *h;
1122
1123 {
1124 return(DIF (h, if_collisions) / INT(h));
1125 }
1126
1127 int
1128 fcpu(h)
1129
1130 METER *h;
1131
1132 {
1133 int i;
1134 int t;
1135 int d[CPUSTATES];
1136
1137 for (t = 0, i= 0; i < CPUSTATES; i++)
1138 t += (d[i] = DIF (h, cp_time[i]));
1139 return(t ? (100 * (d[CP_USER]+d[CP_NICE]+d[CP_SYS])) / t : 0);
1140 }
1141
1142 int
1143 fdisks(h)
1144
1145 METER *h;
1146
1147 {
1148 int i;
1149 int t;
1150
1151 for (t = 0, i= 0; i < 4; i++)
1152 t += (DIF (h, dk_xfer[i]));
1153 return(t / INT(h));
1154 }
1155
1156 int
1157 ferr(h)
1158
1159 METER *h;
1160
1161 {
1162 return(fierr(h) + foerr(h));
1163 }
1164
1165 int
1166 fierr(h)
1167
1168 METER *h;
1169
1170 {
1171 return(DIF (h, if_ierrors) / INT(h));
1172 }
1173
1174 int
1175 fintr(h)
1176
1177 METER *h;
1178
1179 {
1180 return(DIF (h, v_intr) / INT(h));
1181 }
1182
1183 int
1184 fipkt(h)
1185
1186 METER *h;
1187
1188 {
1189 return(DIF (h, if_ipackets) / INT(h));
1190 }
1191
1192 int
1193 fload(h)
1194
1195 METER *h;
1196
1197 {
1198 return(h->sh->st[h->sh->idx].avenrun[0]);
1199 }
1200
1201 int
1202 foerr(h)
1203
1204 METER *h;
1205
1206 {
1207 return(DIF (h, if_oerrors) / INT(h));
1208 }
1209
1210 int
1211 fopkt(h)
1212
1213 METER *h;
1214
1215 {
1216 return(DIF (h, if_opackets) / INT(h));
1217 }
1218
1219 int
1220 fpage(h)
1221
1222 METER *h;
1223
1224 {
1225 return((DIF (h, v_pgpgin) + DIF (h, v_pgpgout)) / INT(h));
1226 }
1227
1228 int
1229 fpgpgin(h)
1230
1231 METER *h;
1232
1233 {
1234 return(DIF (h, v_pgpgin) / INT(h));
1235 }
1236
1237 int
1238 fpkts(h)
1239
1240 METER *h;
1241
1242 {
1243 return(fipkt(h) + fopkt(h));
1244 }
1245
1246 int
1247 fpgpgout(h)
1248
1249 METER *h;
1250
1251 {
1252 return(DIF (h, v_pgpgout) / INT(h));
1253 }
1254
1255 int
1256 fswap(h)
1257
1258 METER *h;
1259
1260 {
1261 return(fpswpin(h) + fpswpout(h));
1262 }
1263
1264 int
1265 fpswpin(h)
1266
1267 METER *h;
1268
1269 {
1270 return(DIF (h, v_pswpin) / INT(h));
1271 }
1272
1273 int
1274 fpswpout(h)
1275
1276 METER *h;
1277
1278 {
1279 return(DIF (h, v_pswpout) / INT(h));
1280 }
1281
1282 int
1283 fswt(h)
1284
1285 METER *h;
1286
1287 {
1288 return(DIF (h, v_swtch) / INT(h));
1289 }
1290
1291 int
1292 fsys(h)
1293
1294 METER *h;
1295
1296 {
1297 int i;
1298 int t;
1299 int d[CPUSTATES];
1300
1301 for (t = 0, i= 0; i < CPUSTATES; i++)
1302 t += (d[i] = DIF (h, cp_time[i]));
1303 return(t ? (100 * d[CP_SYS]) / t : 0);
1304 }
1305
1306 int
1307 fuser(h)
1308
1309 METER *h;
1310
1311 {
1312 int i;
1313 int t;
1314 int d[CPUSTATES];
1315
1316 for (t = 0, i= 0; i < CPUSTATES; i++)
1317 t += (d[i] = DIF (h, cp_time[i]));
1318 return(t ? (100 * (d[CP_USER] + d[CP_NICE])) / t : 0);
1319 }
1320
1321 /*
1322 * getmeter - Executes rstat(3) call to read statistics for specified host.
1323 * I do all the rpc junk myself so that I have better control over timeouts
1324 * than rstat(3) gives me. If we're watching multiple stats
1325 * on the same host I only do one rstat(3) call (refcnt and curcnt are
1326 * used for this).
1327 */
1328 int
1329 getmeter(h)
1330
1331 register METER *h;
1332
1333 {
1334 enum clnt_stat cs;
1335 register SHMETER *sh;
1336 int p;
1337
1338 sh = h->sh;
1339 if (sh->curcnt >= sh->refcnt)
1340 sh->curcnt = 0;
1341 if (!sh->curcnt++) {
1342 if (sh->clnt == NULL) {
1343 if ((p = getport(h)) <= 0)
1344 return(-1);
1345 sh->addr.sin_port = htons(p);
1346 sh->s = RPC_ANYSOCK;
1347 if (!(sh->clnt = clntudp_create(&sh->addr, RSTATPROG, RSTATVERS_TIME,
1348 ptto, &sh->s)))
1349 return(-1);
1350 sh->first = 1;
1351 sh->idx = 0;
1352 } else {
1353 sh->first = 0;
1354 sh->idx ^= 1;
1355 }
1356 cs = clnt_call(sh->clnt, RSTATPROC_STATS, xdr_void, 0, xdr_statstime,
1357 (caddr_t) &sh->st[sh->idx], tto);
1358 if (cs != RPC_SUCCESS) {
1359 clnt_destroy(sh->clnt);
1360 close(sh->s); /* Some clnt_destroy's don't do this */
1361 sh->clnt = NULL;
1362 return(-1);
1363 }
1364 }
1365 return(sh->first ? 0 : sh->clnt == NULL ? -1 : (sd[h->stat].val)(h));
1366 }
1367
1368 /*
1369 * getport - Get port rstatd is listening on.
1370 */
1371 int
1372 getport(h)
1373
1374 METER *h;
1375
1376 {
1377 CLIENT *c;
1378 enum clnt_stat cs;
1379 static struct pmap pm = {RSTATPROG, RSTATVERS_TIME, IPPROTO_UDP, 0};
1380 unsigned short p;
1381 register SHMETER *sh;
1382
1383 sh = h->sh;
1384 sh->s = RPC_ANYSOCK;
1385 sh->addr.sin_port = htons(PMAPPORT);
1386 if (!(c = clntudp_create(&sh->addr, PMAPPROG, PMAPVERS, ptto, &sh->s)))
1387 return(-1);
1388 cs = clnt_call(c, PMAPPROC_GETPORT, xdr_pmap, (caddr_t) &pm,
1389 xdr_u_short, (caddr_t) &p, tto);
1390 clnt_destroy(c);
1391 close(sh->s); /* Some clnt_destroys don't do this */
1392 return(cs == RPC_SUCCESS ? p : -1);
1393 }
1394
1395 /*
1396 * initmeter - Fill in METER and SHMETER structures for this stat.
1397 */
1398 METER
1399 *initmeter(meterlist, idx, argc, argv)
1400
1401 METER *meterlist;
1402 int *idx;
1403 int argc;
1404 char **argv;
1405
1406 {
1407 register METER *h;
1408 struct hostent *he;
1409 int i;
1410 char *cp;
1411
1412 /*
1413 * Create and fill in METER struct.
1414 */
1415 if (!(h = (METER *) malloc(sizeof(METER))))
1416 fatal("METER");
1417 h->nxt = meterlist;
1418 cp = (argv[*idx][0] == '-') ? &argv[*idx][1] : ar.defStat;
1419 for (i = 0; i < MAXSTAT; i++)
1420 if (strcmp(cp, sd[i].name) == 0) {
1421 h->stat = i;
1422 break;
1423 }
1424 if ((cp != ar.defStat) && (++(*idx) == argc || i >= MAXSTAT))
1425 usage();
1426 if ((he = gethostbyname(argv[*idx])) == NULL)
1427 fatal(argv[*idx]);
1428 h->oldstate = OK;
1429 h->oldjumpscroll = 0;
1430 h->sh = newshmeter(he);
1431 if (!(h->label = (char *) malloc(strlen(h->sh->label) + 2 + MAXSTATNAME +
1432 MAXSTATVALUE + 6)))
1433 fatal("label");
1434 sprintf(h->label, "%s %s", h->sh->label, sd[h->stat].name);
1435 return(h);
1436 }
1437
1438 /*
1439 * newshmeter - Return SHMETER structure for host. Keeps
1440 * reference count for each SHMETER. If an SHMETER for this host
1441 * doesn't already exist then one is created and saved on the shmeter
1442 * linked list. Note that he->h_name gets stepped on by this routine
1443 * after it is saved in sh->name.
1444 *
1445 */
1446 SHMETER
1447 *newshmeter(he)
1448
1449 struct hostent *he;
1450
1451 {
1452 SHMETER *sh;
1453 char *cp;
1454
1455 /*
1456 * If we're already looking at this host then just return existing
1457 * structure.
1458 */
1459 for (sh = shmeters; sh; sh = sh->nxt)
1460 if (strcmp(sh->name, he->h_name) == 0) {
1461 sh->refcnt++; /* Count how many references */
1462 return(sh);
1463 }
1464 /*
1465 * Not looking at this host yet, create and fill in SHMETER struct.
1466 */
1467 if (!(sh = (SHMETER *) malloc(sizeof(SHMETER))))
1468 fatal("SHMETER");
1469 sh->nxt = shmeters; /* Save this struct in linked list */
1470 shmeters = sh;
1471 if (!(sh->name = mystrdup(he->h_name)))
1472 fatal("newshmeter");
1473 if (ar.shortName && (cp = strchr(he->h_name, '.'))) {
1474 *cp = '\0';
1475 if (!(sh->label = mystrdup(he->h_name)))
1476 fatal("initmeter");
1477 } else
1478 sh->label = sh->name;
1479 sh->addr.sin_family = AF_INET;
1480 sh->addr.sin_addr.s_addr = *(int *) he->h_addr;
1481 sh->clnt = NULL;
1482 sh->refcnt = 1;
1483 sh->curcnt = 0;
1484 sh->pid = -1;
1485 return(sh);
1486 }
1487
1488 /*
1489 * mystrdup - Not everyone has strdup() in their C library, so provide
1490 * our own.
1491 */
1492 char *
1493 mystrdup(s)
1494
1495 char *s;
1496
1497 {
1498 char *t;
1499
1500 if (t = (char *) malloc(strlen(s) + 1))
1501 return(strcpy(t, s));
1502 else
1503 return(NULL);
1504 }
1505
1506 void
1507 quit(w)
1508
1509 Widget w;
1510
1511 {
1512 XtDestroyApplicationContext(XtWidgetToApplicationContext(w));
1513 exit(0);
1514 }
1515
1516 fatal(m)
1517
1518 char *m;
1519
1520 {
1521 perror(m);
1522 exit(1);
1523 }
1524
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.