xref: /freebsd/libexec/rpc.rstatd/rstat_proc.c (revision 5ebc7e6281887681c3a348a5a4c902e262ccd656)
1 /*
2  * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
3  * unrestricted use provided that this legend is included on all tape
4  * media and as a part of the software program in whole or part.  Users
5  * may copy or modify Sun RPC without charge, but are not authorized
6  * to license or distribute it to anyone else except as part of a product or
7  * program developed by the user.
8  *
9  * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
10  * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
11  * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
12  *
13  * Sun RPC is provided with no support and without any obligation on the
14  * part of Sun Microsystems, Inc. to assist in its use, correction,
15  * modification or enhancement.
16  *
17  * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
18  * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
19  * OR ANY PART THEREOF.
20  *
21  * In no event will Sun Microsystems, Inc. be liable for any lost revenue
22  * or profits or other special, indirect and consequential damages, even if
23  * Sun has been advised of the possibility of such damages.
24  *
25  * Sun Microsystems, Inc.
26  * 2550 Garcia Avenue
27  * Mountain View, California  94043
28  */
29 #ifndef lint
30 /*static char sccsid[] = "from: @(#)rpc.rstatd.c 1.1 86/09/25 Copyr 1984 Sun Micro";*/
31 /*static char sccsid[] = "from: @(#)rstat_proc.c	2.2 88/08/01 4.0 RPCSRC";*/
32 static char rcsid[] = "$Id: rstat_proc.c,v 1.2 1994/10/15 13:39:54 davidg Exp $";
33 #endif
34 
35 /*
36  * rstat service:  built with rstat.x and derived from rpc.rstatd.c
37  *
38  * Copyright (c) 1984 by Sun Microsystems, Inc.
39  */
40 
41 #include <fcntl.h>
42 #include <kvm.h>
43 #include <limits.h>
44 #include <nlist.h>
45 #include <signal.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <syslog.h>
50 
51 #include <rpc/rpc.h>
52 #include <sys/socket.h>
53 #include <sys/errno.h>
54 #include <sys/param.h>
55 #ifdef BSD
56 #include <sys/vmmeter.h>
57 #include <sys/dkstat.h>
58 #else
59 #include <sys/dk.h>
60 #endif
61 #include <net/if.h>
62 
63 #undef FSHIFT			 /* Use protocol's shift and scale values */
64 #undef FSCALE
65 #undef if_ipackets
66 #undef if_ierrors
67 #undef if_opackets
68 #undef if_oerrors
69 #undef if_collisions
70 #include <rpcsvc/rstat.h>
71 
72 struct nlist nl[] = {
73 #define	X_CPTIME	0
74 	{ "_cp_time" },
75 #define	X_CNT		1
76 	{ "_cnt" },
77 #define	X_IFNET		2
78 	{ "_ifnet" },
79 #define	X_DKXFER	3
80 	{ "_dk_xfer" },
81 #define	X_BOOTTIME	4
82 	{ "_boottime" },
83 #define X_HZ		5
84 	{ "_hz" },
85 #ifdef vax
86 #define	X_AVENRUN	6
87 	{ "_avenrun" },
88 #endif
89 	"",
90 };
91 int firstifnet, numintfs;	/* chain of ethernet interfaces */
92 int stats_service();
93 
94 extern int from_inetd;
95 int sincelastreq = 0;		/* number of alarms since last request */
96 extern int closedown;
97 
98 union {
99     struct stats s1;
100     struct statsswtch s2;
101     struct statstime s3;
102 } stats_all;
103 
104 void updatestat();
105 static stat_is_init = 0;
106 static kvm_t *kd;
107 extern int errno;
108 
109 #if defined(BSD)
110 static int	cp_time_xlat[RSTAT_CPUSTATES] = { CP_USER, CP_NICE, CP_SYS,
111 							CP_IDLE };
112 static long	bsd_cp_time[CPUSTATES];
113 #endif
114 
115 
116 #ifndef FSCALE
117 #define FSCALE (1 << 8)
118 #endif
119 
120 #ifndef BSD
121 /*
122  * BSD has the kvm facility for getting info from the
123  * kernel. If you aren't on BSD, this surfices.
124  */
125 int kmem;
126 
127 kvm_read(kd, off, addr, size)
128 	void * kd;
129         unsigned long off, size;
130         char *addr;
131 {
132         int len;
133 	if (lseek(kmem, (long)off, 0) == -1)
134                 return(-1);
135 	return(read(kmem, addr, size));
136 }
137 
138 kvm_nlist(kd, nl)
139 	void * kd;
140         struct nlist *nl;
141 {
142 	int n = nlist("/vmunix", nl);
143 	if (nl[0].n_value == 0)
144                 return(n);
145 
146 	if ((kmem = open("/dev/kmem", 0)) < 0)
147                 return(-1);
148         return(0);
149 }
150 #endif
151 
152 stat_init()
153 {
154     stat_is_init = 1;
155     setup();
156     updatestat();
157     (void) signal(SIGALRM, updatestat);
158     alarm(1);
159 }
160 
161 statstime *
162 rstatproc_stats_3()
163 {
164     if (! stat_is_init)
165         stat_init();
166     sincelastreq = 0;
167     return(&stats_all.s3);
168 }
169 
170 statsswtch *
171 rstatproc_stats_2()
172 {
173     if (! stat_is_init)
174         stat_init();
175     sincelastreq = 0;
176     return(&stats_all.s2);
177 }
178 
179 stats *
180 rstatproc_stats_1()
181 {
182     if (! stat_is_init)
183         stat_init();
184     sincelastreq = 0;
185     return(&stats_all.s1);
186 }
187 
188 u_int *
189 rstatproc_havedisk_3()
190 {
191     static u_int have;
192 
193     if (! stat_is_init)
194         stat_init();
195     sincelastreq = 0;
196     have = havedisk();
197 	return(&have);
198 }
199 
200 u_int *
201 rstatproc_havedisk_2()
202 {
203     return(rstatproc_havedisk_3());
204 }
205 
206 u_int *
207 rstatproc_havedisk_1()
208 {
209     return(rstatproc_havedisk_3());
210 }
211 
212 void
213 updatestat()
214 {
215 	int off, i, hz;
216 	struct vmmeter cnt;
217 	struct ifnet ifnet;
218 	double avrun[3];
219 	struct timeval tm, btm;
220 
221 #ifdef DEBUG
222 	fprintf(stderr, "entering updatestat\n");
223 #endif
224 	if (sincelastreq >= closedown) {
225 #ifdef DEBUG
226                 fprintf(stderr, "about to closedown\n");
227 #endif
228                 if (from_inetd)
229                         exit(0);
230                 else {
231                         stat_is_init = 0;
232                         return;
233                 }
234 	}
235 	sincelastreq++;
236 
237 	if (kvm_read(kd, (long)nl[X_HZ].n_value, (char *)&hz, sizeof hz) != sizeof hz) {
238 		syslog(LOG_ERR, "rstat: can't read hz from kmem\n");
239 		exit(1);
240 	}
241 #if defined(BSD)
242 	if (kvm_read(kd, (long)nl[X_CPTIME].n_value, (char *)bsd_cp_time, sizeof(bsd_cp_time))
243 	    != sizeof(bsd_cp_time)) {
244 		syslog(LOG_ERR, "rstat: can't read cp_time from kmem\n");
245 		exit(1);
246 	}
247 	for(i = 0; i < RSTAT_CPUSTATES ; i++)
248 		stats_all.s1.cp_time[i] = bsd_cp_time[cp_time_xlat[i]];
249 #else
250  	if (kvm_read(kd, (long)nl[X_CPTIME].n_value, (char *)stats_all.s1.cp_time, sizeof (stats_all.s1.cp_time))
251 	    != sizeof (stats_all.s1.cp_time)) {
252 		syslog(LOG_ERR, "rstat: can't read cp_time from kmem\n");
253 		exit(1);
254 	}
255 #endif
256 #ifdef vax
257  	if (kvm_read(kd, (long)nl[X_AVENRUN].n_value, (char *)avrun, sizeof (avrun)) != sizeof (avrun)) {
258 		syslog(LOG_ERR, "rstat: can't read avenrun from kmem\n");
259 		exit(1);
260 	}
261 #endif
262 #ifdef BSD
263         (void)getloadavg(avrun, sizeof(avrun) / sizeof(avrun[0]));
264 #endif
265 	stats_all.s2.avenrun[0] = avrun[0] * FSCALE;
266 	stats_all.s2.avenrun[1] = avrun[1] * FSCALE;
267 	stats_all.s2.avenrun[2] = avrun[2] * FSCALE;
268  	if (kvm_read(kd, (long)nl[X_BOOTTIME].n_value, (char *)&btm, sizeof (stats_all.s2.boottime))
269 	    != sizeof (stats_all.s2.boottime)) {
270 		syslog(LOG_ERR, "rstat: can't read boottime from kmem\n");
271 		exit(1);
272 	}
273 	stats_all.s2.boottime.tv_sec = btm.tv_sec;
274 	stats_all.s2.boottime.tv_usec = btm.tv_usec;
275 
276 
277 #ifdef DEBUG
278 	fprintf(stderr, "%d %d %d %d\n", stats_all.s1.cp_time[0],
279 	    stats_all.s1.cp_time[1], stats_all.s1.cp_time[2], stats_all.s1.cp_time[3]);
280 #endif
281 
282  	if (kvm_read(kd, (long)nl[X_CNT].n_value, (char *)&cnt, sizeof cnt) != sizeof cnt) {
283 		syslog(LOG_ERR, "rstat: can't read cnt from kmem\n");
284 		exit(1);
285 	}
286 	stats_all.s1.v_pgpgin = cnt.v_vnodepgsin;
287 	stats_all.s1.v_pgpgout = cnt.v_vnodepgsout;
288 	stats_all.s1.v_pswpin = cnt.v_swappgsin;
289 	stats_all.s1.v_pswpout = cnt.v_swappgsout;
290 	stats_all.s1.v_intr = cnt.v_intr;
291 	gettimeofday(&tm, (struct timezone *) 0);
292 	stats_all.s1.v_intr -= hz*(tm.tv_sec - btm.tv_sec) +
293 	    hz*(tm.tv_usec - btm.tv_usec)/1000000;
294 	stats_all.s2.v_swtch = cnt.v_swtch;
295 
296  	if (kvm_read(kd, (long)nl[X_DKXFER].n_value, (char *)stats_all.s1.dk_xfer, sizeof (stats_all.s1.dk_xfer))
297 	    != sizeof (stats_all.s1.dk_xfer)) {
298 		syslog(LOG_ERR, "rstat: can't read dk_xfer from kmem\n");
299 		exit(1);
300 	}
301 
302 	stats_all.s1.if_ipackets = 0;
303 	stats_all.s1.if_opackets = 0;
304 	stats_all.s1.if_ierrors = 0;
305 	stats_all.s1.if_oerrors = 0;
306 	stats_all.s1.if_collisions = 0;
307 	for (off = firstifnet, i = 0; off && i < numintfs; i++) {
308 		if (kvm_read(kd, off, (char *)&ifnet, sizeof ifnet) != sizeof ifnet) {
309 			syslog(LOG_ERR, "rstat: can't read ifnet from kmem\n");
310 			exit(1);
311 		}
312 		stats_all.s1.if_ipackets += ifnet.if_data.ifi_ipackets;
313 		stats_all.s1.if_opackets += ifnet.if_data.ifi_opackets;
314 		stats_all.s1.if_ierrors += ifnet.if_data.ifi_ierrors;
315 		stats_all.s1.if_oerrors += ifnet.if_data.ifi_oerrors;
316 		stats_all.s1.if_collisions += ifnet.if_data.ifi_collisions;
317 		off = (int) ifnet.if_next;
318 	}
319 	gettimeofday((struct timeval *)&stats_all.s3.curtime,
320 		(struct timezone *) 0);
321 	alarm(1);
322 }
323 
324 setup()
325 {
326 	struct ifnet ifnet;
327 	int off;
328 	char errbuf[_POSIX2_LINE_MAX];
329 
330 	int en;
331 
332 	if ((kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf)) == NULL) {
333 		syslog(LOG_ERR, "rpc.rstatd, %s", errbuf);
334 		exit(1);
335 	}
336 
337 	if ((en = kvm_nlist(kd, nl)) != 0) {
338 		syslog(LOG_ERR, "rstatd: Can't get namelist. %d", en);
339 		exit (1);
340         }
341 
342 	if (kvm_read(kd, (long)nl[X_IFNET].n_value, &firstifnet,
343                      sizeof(int)) != sizeof(int))  {
344 		syslog(LOG_ERR, "rstat: can't read firstifnet from kmem\n");
345 		exit(1);
346         }
347 
348 	numintfs = 0;
349 	for (off = firstifnet; off;) {
350 		if (kvm_read(kd, off, (char *)&ifnet, sizeof ifnet) != sizeof ifnet) {
351 			syslog(LOG_ERR, "rstat: can't read ifnet from kmem\n");
352 			exit(1);
353 		}
354 		numintfs++;
355 		off = (int) ifnet.if_next;
356 	}
357 }
358 
359 /*
360  * returns true if have a disk
361  */
362 havedisk()
363 {
364 	int i, cnt;
365 	long  xfer[DK_NDRIVE];
366 
367 	if (kvm_nlist(kd, nl) != 0) {
368 		syslog(LOG_ERR, "rstatd: Can't get namelist.(d)");
369 		exit (1);
370         }
371 
372 	if (kvm_read(kd, (long)nl[X_DKXFER].n_value, (char *)xfer, sizeof xfer)!= sizeof xfer) {
373 		syslog(LOG_ERR, "rstat: can't read kmem\n");
374 		exit(1);
375 	}
376 	cnt = 0;
377 	for (i=0; i < DK_NDRIVE; i++)
378 		cnt += xfer[i];
379 	return (cnt != 0);
380 }
381 
382 void
383 rstat_service(rqstp, transp)
384 	struct svc_req *rqstp;
385 	SVCXPRT *transp;
386 {
387 	union {
388 		int fill;
389 	} argument;
390 	char *result;
391 	bool_t (*xdr_argument)(), (*xdr_result)();
392 	char *(*local)();
393 
394 	switch (rqstp->rq_proc) {
395 	case NULLPROC:
396 		(void)svc_sendreply(transp, xdr_void, (char *)NULL);
397 		goto leave;
398 
399 	case RSTATPROC_STATS:
400 		xdr_argument = xdr_void;
401 		xdr_result = xdr_statstime;
402                 switch (rqstp->rq_vers) {
403                 case RSTATVERS_ORIG:
404                         local = (char *(*)()) rstatproc_stats_1;
405                         break;
406                 case RSTATVERS_SWTCH:
407                         local = (char *(*)()) rstatproc_stats_2;
408                         break;
409                 case RSTATVERS_TIME:
410                         local = (char *(*)()) rstatproc_stats_3;
411                         break;
412                 default:
413                         svcerr_progvers(transp, RSTATVERS_ORIG, RSTATVERS_TIME);
414                         goto leave;
415                         /*NOTREACHED*/
416                 }
417 		break;
418 
419 	case RSTATPROC_HAVEDISK:
420 		xdr_argument = xdr_void;
421 		xdr_result = xdr_u_int;
422                 switch (rqstp->rq_vers) {
423                 case RSTATVERS_ORIG:
424                         local = (char *(*)()) rstatproc_havedisk_1;
425                         break;
426                 case RSTATVERS_SWTCH:
427                         local = (char *(*)()) rstatproc_havedisk_2;
428                         break;
429                 case RSTATVERS_TIME:
430                         local = (char *(*)()) rstatproc_havedisk_3;
431                         break;
432                 default:
433                         svcerr_progvers(transp, RSTATVERS_ORIG, RSTATVERS_TIME);
434                         goto leave;
435                         /*NOTREACHED*/
436                 }
437 		break;
438 
439 	default:
440 		svcerr_noproc(transp);
441 		goto leave;
442 	}
443 	bzero((char *)&argument, sizeof(argument));
444 	if (!svc_getargs(transp, xdr_argument, &argument)) {
445 		svcerr_decode(transp);
446 		goto leave;
447 	}
448 	result = (*local)(&argument, rqstp);
449 	if (result != NULL && !svc_sendreply(transp, xdr_result, result)) {
450 		svcerr_systemerr(transp);
451 	}
452 	if (!svc_freeargs(transp, xdr_argument, &argument)) {
453 		(void)fprintf(stderr, "unable to free arguments\n");
454 		exit(1);
455 	}
456 leave:
457         if (from_inetd)
458                 exit(0);
459 }
460