xref: /openbsd-src/libexec/rpc.rstatd/rstat_proc.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /*	$OpenBSD: rstat_proc.c,v 1.15 2001/07/08 21:18:09 deraadt 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 #ifndef lint
32 /*static char sccsid[] = "from: @(#)rpc.rstatd.c 1.1 86/09/25 Copyr 1984 Sun Micro";*/
33 /*static char sccsid[] = "from: @(#)rstat_proc.c	2.2 88/08/01 4.0 RPCSRC";*/
34 static char rcsid[] = "$OpenBSD: rstat_proc.c,v 1.15 2001/07/08 21:18:09 deraadt Exp $";
35 #endif
36 
37 /*
38  * rstat service:  built with rstat.x and derived from rpc.rstatd.c
39  *
40  * Copyright (c) 1984 by Sun Microsystems, Inc.
41  */
42 
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <signal.h>
47 #include <fcntl.h>
48 #include <kvm.h>
49 #include <limits.h>
50 #include <rpc/rpc.h>
51 #include <sys/socket.h>
52 #include <nlist.h>
53 #include <syslog.h>
54 #include <errno.h>
55 #include <sys/param.h>
56 #ifdef BSD
57 #include <sys/vmmeter.h>
58 #include <sys/dkstat.h>
59 #include "dkstats.h"
60 #else
61 #include <sys/dk.h>
62 #endif
63 #include <net/if.h>
64 
65 #include <vm/vm.h>
66 #include <sys/sysctl.h>
67 #include <uvm/uvm_extern.h>
68 
69 #undef FSHIFT			 /* Use protocol's shift and scale values */
70 #undef FSCALE
71 #undef DK_NDRIVE
72 #undef CPUSTATES
73 #undef if_ipackets
74 #undef if_ierrors
75 #undef if_opackets
76 #undef if_oerrors
77 #undef if_collisions
78 #include <rpcsvc/rstat.h>
79 
80 #ifdef BSD
81 #define BSD_CPUSTATES	5	/* Use protocol's idea of CPU states */
82 int	cp_xlat[CPUSTATES] = { CP_USER, CP_NICE, CP_SYS, CP_IDLE };
83 #endif
84 
85 struct nlist nl[] = {
86 #define	X_IFNET		0
87 	{ "_ifnet" },
88 #define	X_BOOTTIME	1
89 	{ "_boottime" },
90 #ifndef BSD
91 #define	X_HZ		2
92 	{ "_hz" },
93 #define	X_CPTIME	3
94 	{ "_cp_time" },
95 #define	X_DKXFER	4
96 	{ "_dk_xfer" },
97 #define	X_CNT		5
98 	{ "_cnt" },
99 #else
100 #endif
101 	{ NULL },
102 };
103 
104 #ifdef BSD
105 extern int dk_ndrive;		/* from dkstats.c */
106 extern struct _disk cur, last;
107 char *memf = NULL, *nlistf = NULL;
108 #endif
109 int hz;
110 
111 struct ifnet_head ifnetq;	/* chain of ethernet interfaces */
112 int numintfs;
113 
114 extern int from_inetd;
115 int sincelastreq = 0;		/* number of alarms since last request */
116 extern int closedown;
117 kvm_t *kfd;
118 
119 union {
120 	struct stats s1;
121 	struct statsswtch s2;
122 	struct statstime s3;
123 } stats_all;
124 
125 int stats_service();
126 void updatestat(void);
127 void setup(void);
128 int havedisk(void);
129 
130 static int stat_is_init = 0;
131 
132 #ifndef FSCALE
133 #define FSCALE (1 << 8)
134 #endif
135 
136 void
137 stat_init()
138 {
139 	stat_is_init = 1;
140 	setup();
141 	updatestat();
142 	(void) signal(SIGALRM, updatestat);	/* XXX huge signal race */
143 	alarm(1);
144 }
145 
146 statstime *
147 rstatproc_stats_3_svc(arg, rqstp)
148 	void *arg;
149 	struct svc_req *rqstp;
150 {
151 	if (!stat_is_init)
152 		stat_init();
153 	sincelastreq = 0;
154 	return (&stats_all.s3);
155 }
156 
157 statsswtch *
158 rstatproc_stats_2_svc(arg, rqstp)
159 	void *arg;
160 	struct svc_req *rqstp;
161 {
162 	if (!stat_is_init)
163 		stat_init();
164 	sincelastreq = 0;
165 	return (&stats_all.s2);
166 }
167 
168 stats *
169 rstatproc_stats_1_svc(arg, rqstp)
170 	void *arg;
171 	struct svc_req *rqstp;
172 {
173 	if (!stat_is_init)
174 		stat_init();
175 	sincelastreq = 0;
176 	return (&stats_all.s1);
177 }
178 
179 u_int *
180 rstatproc_havedisk_3_svc(arg, rqstp)
181 	void *arg;
182 	struct svc_req *rqstp;
183 {
184 	static u_int have;
185 
186 	if (!stat_is_init)
187 		stat_init();
188 	sincelastreq = 0;
189 	have = havedisk();
190 	return (&have);
191 }
192 
193 u_int *
194 rstatproc_havedisk_2_svc(arg, rqstp)
195 	void *arg;
196 	struct svc_req *rqstp;
197 {
198 	return (rstatproc_havedisk_3_svc(arg, rqstp));
199 }
200 
201 u_int *
202 rstatproc_havedisk_1_svc(arg, rqstp)
203 	void *arg;
204 	struct svc_req *rqstp;
205 {
206 	return (rstatproc_havedisk_3_svc(arg, rqstp));
207 }
208 
209 void
210 updatestat()
211 {
212 	long off;
213 	int i, save_errno = errno;
214 	struct uvmexp uvmexp;
215 	int mib[2];
216 	size_t len;
217 	struct ifnet ifnet;
218 	double avrun[3];
219 	struct timeval tm, btm;
220 #ifdef BSD
221 	long *cp_time = cur.cp_time;
222 #endif
223 
224 #ifdef DEBUG
225 	syslog(LOG_DEBUG, "entering updatestat");
226 #endif
227 	if (sincelastreq >= closedown) {
228 #ifdef DEBUG
229 		syslog(LOG_DEBUG, "about to closedown");
230 #endif
231 		if (from_inetd)
232 			exit(0);
233 		else {
234 			stat_is_init = 0;
235 			return;
236 		}
237 	}
238 	sincelastreq++;
239 
240 	/*
241 	 * dkreadstats reads in the "disk_count" as well as the "disklist"
242 	 * statistics.  It also retrieves "hz" and the "cp_time" array.
243 	 */
244 	dkreadstats();
245 	memset(stats_all.s1.dk_xfer, '\0', sizeof(stats_all.s1.dk_xfer));
246 	for (i = 0; i < dk_ndrive && i < DK_NDRIVE; i++)
247 		stats_all.s1.dk_xfer[i] = cur.dk_xfer[i];
248 
249 #ifdef BSD
250 	for (i = 0; i < CPUSTATES; i++)
251 		stats_all.s1.cp_time[i] = cp_time[cp_xlat[i]];
252 #else
253 	if (kvm_read(kfd, (long)nl[X_HZ].n_value, (char *)&hz, sizeof hz) !=
254 	    sizeof hz) {
255 		syslog(LOG_ERR, "can't read hz from kmem");
256 		exit(1);
257 	}
258  	if (kvm_read(kfd, (long)nl[X_CPTIME].n_value,
259 	    (char *)stats_all.s1.cp_time, sizeof (stats_all.s1.cp_time))
260 	    != sizeof (stats_all.s1.cp_time)) {
261 		syslog(LOG_ERR, "can't read cp_time from kmem");
262 		exit(1);
263 	}
264 #endif
265 #ifdef BSD
266 	(void)getloadavg(avrun, sizeof(avrun) / sizeof(avrun[0]));
267 #endif
268 	stats_all.s2.avenrun[0] = avrun[0] * FSCALE;
269 	stats_all.s2.avenrun[1] = avrun[1] * FSCALE;
270 	stats_all.s2.avenrun[2] = avrun[2] * FSCALE;
271  	if (kvm_read(kfd, (long)nl[X_BOOTTIME].n_value,
272 	    (char *)&btm, sizeof (stats_all.s2.boottime))
273 	    != sizeof (stats_all.s2.boottime)) {
274 		syslog(LOG_ERR, "can't read boottime from kmem");
275 		exit(1);
276 	}
277 	stats_all.s2.boottime.tv_sec = btm.tv_sec;
278 	stats_all.s2.boottime.tv_usec = btm.tv_usec;
279 
280 
281 #ifdef DEBUG
282 	syslog(LOG_DEBUG, "%d %d %d %d", stats_all.s1.cp_time[0],
283 	    stats_all.s1.cp_time[1], stats_all.s1.cp_time[2],
284 	    stats_all.s1.cp_time[3]);
285 #endif
286 
287 	mib[0] = CTL_VM;
288 	mib[1] = VM_UVMEXP;
289 	len = sizeof(uvmexp);
290 	if (sysctl(mib, 2, &uvmexp, &len, NULL, 0) < 0) {
291 		syslog(LOG_ERR, "can't sysctl vm.uvmexp");
292 		exit(1);
293 	}
294 	stats_all.s1.v_pgpgin = uvmexp.fltanget;
295 	stats_all.s1.v_pgpgout = uvmexp.pdpageouts;
296 	stats_all.s1.v_pswpin = uvmexp.swapins;
297 	stats_all.s1.v_pswpout = uvmexp.swapouts;
298 	stats_all.s1.v_intr = uvmexp.intrs;
299 	stats_all.s2.v_swtch = uvmexp.swtch;
300 	gettimeofday(&tm, (struct timezone *) 0);
301 	stats_all.s1.v_intr -= hz*(tm.tv_sec - btm.tv_sec) +
302 	    hz*(tm.tv_usec - btm.tv_usec)/1000000;
303 
304 #ifndef BSD
305  	if (kvm_read(kfd, (long)nl[X_DKXFER].n_value,
306 	    (char *)stats_all.s1.dk_xfer, sizeof (stats_all.s1.dk_xfer))
307 	    != sizeof (stats_all.s1.dk_xfer)) {
308 		syslog(LOG_ERR, "can't read dk_xfer from kmem");
309 		exit(1);
310 	}
311 #endif
312 
313 	stats_all.s1.if_ipackets = 0;
314 	stats_all.s1.if_opackets = 0;
315 	stats_all.s1.if_ierrors = 0;
316 	stats_all.s1.if_oerrors = 0;
317 	stats_all.s1.if_collisions = 0;
318 	for (off = (long)ifnetq.tqh_first, i = 0; off && i < numintfs; i++) {
319 		if (kvm_read(kfd, off, (char *)&ifnet, sizeof ifnet) !=
320 		    sizeof ifnet) {
321 			syslog(LOG_ERR, "can't read ifnet from kmem");
322 			exit(1);
323 		}
324 		stats_all.s1.if_ipackets += ifnet.if_data.ifi_ipackets;
325 		stats_all.s1.if_opackets += ifnet.if_data.ifi_opackets;
326 		stats_all.s1.if_ierrors += ifnet.if_data.ifi_ierrors;
327 		stats_all.s1.if_oerrors += ifnet.if_data.ifi_oerrors;
328 		stats_all.s1.if_collisions += ifnet.if_data.ifi_collisions;
329 		off = (long)ifnet.if_list.tqe_next;
330 	}
331 	gettimeofday((struct timeval *)&stats_all.s3.curtime,
332 		(struct timezone *) 0);
333 	alarm(1);
334 	errno = save_errno;
335 }
336 
337 void
338 setup()
339 {
340 	struct ifnet ifnet;
341 	long off;
342 	char errbuf[_POSIX2_LINE_MAX];
343 
344 	kfd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf);
345 	if (kfd == NULL) {
346 		syslog(LOG_ERR, "%s", errbuf);
347 		exit (1);
348 	}
349 
350 	if (kvm_nlist(kfd, nl) != 0) {
351 		syslog(LOG_ERR, "can't get namelist");
352 		exit (1);
353 	}
354 
355 	if (kvm_read(kfd, (long)nl[X_IFNET].n_value, &ifnetq,
356 	    sizeof ifnetq) != sizeof ifnetq) {
357 		syslog(LOG_ERR, "can't read ifnet queue head from kmem");
358 		exit(1);
359 	}
360 
361 	numintfs = 0;
362 	for (off = (long)ifnetq.tqh_first; off;) {
363 		if (kvm_read(kfd, off, (char *)&ifnet, sizeof ifnet) !=
364 		    sizeof ifnet) {
365 			syslog(LOG_ERR, "can't read ifnet from kmem");
366 			exit(1);
367 		}
368 		numintfs++;
369 		off = (long)ifnet.if_list.tqe_next;
370 	}
371 #ifdef BSD
372 	dkinit(0);
373 #endif
374 }
375 
376 /*
377  * returns true if have a disk
378  */
379 int
380 havedisk()
381 {
382 #ifdef BSD
383 	return dk_ndrive != 0;
384 #else
385 	int i, cnt;
386 	long  xfer[DK_NDRIVE];
387 
388 	if (kvm_nlist(kfd, nl) != 0) {
389 		syslog(LOG_ERR, "can't get namelist");
390 		exit (1);
391 	}
392 
393 	if (kvm_read(kfd, (long)nl[X_DKXFER].n_value,
394 		     (char *)xfer, sizeof xfer) != sizeof xfer) {
395 		syslog(LOG_ERR, "can't read dk_xfer from kmem");
396 		exit(1);
397 	}
398 	cnt = 0;
399 	for (i=0; i < DK_NDRIVE; i++)
400 		cnt += xfer[i];
401 	return (cnt != 0);
402 #endif
403 }
404 
405 void
406 rstat_service(rqstp, transp)
407 	struct svc_req *rqstp;
408 	SVCXPRT *transp;
409 {
410 	union {
411 		int fill;
412 	} argument;
413 	char *result;
414 	xdrproc_t xdr_argument, xdr_result;
415 	char *(*local) __P((void *, struct svc_req *));
416 
417 	switch (rqstp->rq_proc) {
418 	case NULLPROC:
419 		(void)svc_sendreply(transp, xdr_void, (char *)NULL);
420 		return;
421 
422 	case RSTATPROC_STATS:
423 		xdr_argument = (xdrproc_t)xdr_void;
424 		xdr_result = (xdrproc_t)xdr_statstime;
425 		switch (rqstp->rq_vers) {
426 		case RSTATVERS_ORIG:
427 			local = (char *(*) __P((void *, struct svc_req *)))
428 				rstatproc_stats_1_svc;
429 			break;
430 		case RSTATVERS_SWTCH:
431 			local = (char *(*) __P((void *, struct svc_req *)))
432 				rstatproc_stats_2_svc;
433 			break;
434 		case RSTATVERS_TIME:
435 			local = (char *(*) __P((void *, struct svc_req *)))
436 				rstatproc_stats_3_svc;
437 			break;
438 		default:
439 			svcerr_progvers(transp, RSTATVERS_ORIG, RSTATVERS_TIME);
440 			return;
441 		}
442 		break;
443 
444 	case RSTATPROC_HAVEDISK:
445 		xdr_argument = (xdrproc_t)xdr_void;
446 		xdr_result = (xdrproc_t)xdr_u_int;
447 		switch (rqstp->rq_vers) {
448 		case RSTATVERS_ORIG:
449 			local = (char *(*) __P((void *, struct svc_req *)))
450 				rstatproc_havedisk_1_svc;
451 			break;
452 		case RSTATVERS_SWTCH:
453 			local = (char *(*) __P((void *, struct svc_req *)))
454 				rstatproc_havedisk_2_svc;
455 			break;
456 		case RSTATVERS_TIME:
457 			local = (char *(*) __P((void *, struct svc_req *)))
458 				rstatproc_havedisk_3_svc;
459 			break;
460 		default:
461 			svcerr_progvers(transp, RSTATVERS_ORIG, RSTATVERS_TIME);
462 			return;
463 		}
464 		break;
465 
466 	default:
467 		svcerr_noproc(transp);
468 		return;
469 	}
470 	bzero((char *)&argument, sizeof(argument));
471 	if (!svc_getargs(transp, xdr_argument, (caddr_t)&argument)) {
472 		svcerr_decode(transp);
473 		return;
474 	}
475 	result = (*local)(&argument, rqstp);
476 	if (result != NULL && !svc_sendreply(transp, xdr_result, result)) {
477 		svcerr_systemerr(transp);
478 	}
479 	if (!svc_freeargs(transp, xdr_argument, (caddr_t)&argument)) {
480 		syslog(LOG_ERR, "unable to free arguments");
481 		exit(1);
482 	}
483 }
484