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