1147999Semax/*
2147999Semax * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
3147999Semax * unrestricted use provided that this legend is included on all tape
4147999Semax * media and as a part of the software program in whole or part.  Users
5147999Semax * may copy or modify Sun RPC without charge, but are not authorized
6147999Semax * to license or distribute it to anyone else except as part of a product or
7147999Semax * program developed by the user.
8147999Semax *
9147999Semax * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
10147999Semax * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
11147999Semax * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
12147999Semax *
13147999Semax * Sun RPC is provided with no support and without any obligation on the
14147999Semax * part of Sun Microsystems, Inc. to assist in its use, correction,
15147999Semax * modification or enhancement.
16147999Semax *
17147999Semax * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
18147999Semax * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
19147999Semax * OR ANY PART THEREOF.
20147999Semax *
21147999Semax * In no event will Sun Microsystems, Inc. be liable for any lost revenue
22147999Semax * or profits or other special, indirect and consequential damages, even if
23147999Semax * Sun has been advised of the possibility of such damages.
24147999Semax *
25147999Semax * Sun Microsystems, Inc.
26147999Semax * 2550 Garcia Avenue
27147999Semax * Mountain View, California  94043
28147999Semax */
29147999Semax
30147999Semax/*
31147999Semax * rstat service:  built with rstat.x and derived from rpc.rstatd.c
32147999Semax *
33147999Semax * Copyright (c) 1984 by Sun Microsystems, Inc.
34162711Sru */
35147999Semax
36298430Semaste#include <sys/types.h>
37147999Semax#include <sys/socket.h>
38147999Semax#include <sys/sysctl.h>
39156167Semax#include <sys/time.h>
40147999Semax#include <sys/resource.h>
41147999Semax#include <sys/param.h>
42147999Semax
43147999Semax#include <err.h>
44147999Semax#include <errno.h>
45147999Semax#include <fcntl.h>
46147999Semax#include <limits.h>
47147999Semax#include <signal.h>
48147999Semax#include <stdio.h>
49147999Semax#include <stdlib.h>
50147999Semax#include <string.h>
51147999Semax#include <syslog.h>
52147999Semax#include <unistd.h>
53147999Semax#include <devstat.h>
54147999Semax
55147999Semax#include <net/if.h>
56147999Semax#include <net/if_mib.h>
57147999Semax
58298430Semaste#undef FSHIFT			 /* Use protocol's shift and scale values */
59298430Semaste#undef FSCALE
60298430Semaste#undef if_ipackets
61298430Semaste#undef if_ierrors
62298430Semaste#undef if_opackets
63298430Semaste#undef if_oerrors
64298430Semaste#undef if_collisions
65147999Semax#include <rpcsvc/rstat.h>
66147999Semax
67147999Semaxint haveadisk(void);
68147999Semaxvoid updatexfers(int, int *);
69147999Semaxint stats_service(void);
70147999Semax
71147999Semaxextern int from_inetd;
72147999Semaxint sincelastreq = 0;		/* number of alarms since last request */
73147999Semaxextern int closedown;
74147999Semax
75147999Semaxunion {
76147999Semax    struct stats s1;
77147999Semax    struct statsswtch s2;
78147999Semax    struct statstime s3;
79147999Semax} stats_all;
80147999Semax
81147999Semaxvoid updatestat();
82147999Semaxstatic int stat_is_init = 0;
83147999Semax
84147999Semaxstatic int	cp_time_xlat[RSTAT_CPUSTATES] = { CP_USER, CP_NICE, CP_SYS,
85147999Semax							CP_IDLE };
86147999Semaxstatic long	bsd_cp_time[CPUSTATES];
87147999Semax
88147999Semax
89147999Semax#ifndef FSCALE
90147999Semax#define FSCALE (1 << 8)
91147999Semax#endif
92147999Semax
93147999Semaxvoid
94147999Semaxstat_init(void)
95147999Semax{
96147999Semax    stat_is_init = 1;
97147999Semax    alarm(0);
98147999Semax    updatestat();
99147999Semax    (void) signal(SIGALRM, updatestat);
100147999Semax    alarm(1);
101147999Semax}
102147999Semax
103147999Semaxstatstime *
104147999Semaxrstatproc_stats_3_svc(void *argp, struct svc_req *rqstp)
105147999Semax{
106147999Semax    if (! stat_is_init)
107147999Semax        stat_init();
108147999Semax    sincelastreq = 0;
109147999Semax    return(&stats_all.s3);
110147999Semax}
111147999Semax
112147999Semaxstatsswtch *
113147999Semaxrstatproc_stats_2_svc(void *argp, struct svc_req *rqstp)
114190857Semax{
115190857Semax    if (! stat_is_init)
116190857Semax        stat_init();
117190857Semax    sincelastreq = 0;
118147999Semax    return(&stats_all.s2);
119147999Semax}
120147999Semax
121147999Semaxstats *
122147999Semaxrstatproc_stats_1_svc(void *argp, struct svc_req *rqstp)
123147999Semax{
124147999Semax    if (! stat_is_init)
125147999Semax        stat_init();
126147999Semax    sincelastreq = 0;
127147999Semax    return(&stats_all.s1);
128147999Semax}
129147999Semax
130147999Semaxu_int *
131147999Semaxrstatproc_havedisk_3_svc(void *argp, struct svc_req *rqstp)
132147999Semax{
133147999Semax    static u_int have;
134147999Semax
135147999Semax    if (! stat_is_init)
136147999Semax        stat_init();
137147999Semax    sincelastreq = 0;
138147999Semax    have = haveadisk();
139147999Semax	return(&have);
140147999Semax}
141147999Semax
142147999Semaxu_int *
143147999Semaxrstatproc_havedisk_2_svc(void *argp, struct svc_req *rqstp)
144193512Sed{
145193512Sed    return(rstatproc_havedisk_3_svc(argp, rqstp));
146193512Sed}
147147999Semax
148147999Semaxu_int *
149147999Semaxrstatproc_havedisk_1_svc(void *argp, struct svc_req *rqstp)
150147999Semax{
151147999Semax    return(rstatproc_havedisk_3_svc(argp, rqstp));
152147999Semax}
153147999Semax
154147999Semaxvoid
155147999Semaxupdatestat(void)
156147999Semax{
157147999Semax	int i, hz;
158147999Semax	struct clockinfo clockrate;
159147999Semax	struct ifmibdata ifmd;
160147999Semax	double avrun[3];
161147999Semax	struct timeval tm, btm;
162147999Semax	int mib[6];
163147999Semax	size_t len;
164147999Semax	uint64_t val;
165147999Semax	int ifcount;
166147999Semax
167147999Semax#ifdef DEBUG
168147999Semax	fprintf(stderr, "entering updatestat\n");
169147999Semax#endif
170147999Semax	if (sincelastreq >= closedown) {
171147999Semax#ifdef DEBUG
172147999Semax                fprintf(stderr, "about to closedown\n");
173147999Semax#endif
174147999Semax                if (from_inetd)
175147999Semax                        exit(0);
176147999Semax                else {
177147999Semax                        stat_is_init = 0;
178147999Semax                        return;
179193512Sed                }
180193512Sed	}
181193512Sed	sincelastreq++;
182193512Sed
183193512Sed	mib[0] = CTL_KERN;
184193512Sed	mib[1] = KERN_CLOCKRATE;
185193512Sed	len = sizeof clockrate;
186193512Sed	if (sysctl(mib, 2, &clockrate, &len, 0, 0) < 0) {
187193512Sed		syslog(LOG_ERR, "sysctl(kern.clockrate): %m");
188193512Sed		exit(1);
189193512Sed	}
190193512Sed	hz = clockrate.hz;
191193512Sed
192193752Sed	len = sizeof(bsd_cp_time);
193193512Sed	if (sysctlbyname("kern.cp_time", bsd_cp_time, &len, 0, 0) < 0) {
194193512Sed		syslog(LOG_ERR, "sysctl(kern.cp_time): %m");
195193752Sed		exit(1);
196193512Sed	}
197193512Sed	for(i = 0; i < RSTAT_CPUSTATES ; i++)
198193512Sed		stats_all.s1.cp_time[i] = bsd_cp_time[cp_time_xlat[i]];
199193512Sed
200193512Sed        (void)getloadavg(avrun, sizeof(avrun) / sizeof(avrun[0]));
201193512Sed
202193512Sed	stats_all.s2.avenrun[0] = avrun[0] * FSCALE;
203193512Sed	stats_all.s2.avenrun[1] = avrun[1] * FSCALE;
204193512Sed	stats_all.s2.avenrun[2] = avrun[2] * FSCALE;
205193512Sed
206193512Sed	mib[0] = CTL_KERN;
207147999Semax	mib[1] = KERN_BOOTTIME;
208147999Semax	len = sizeof btm;
209147999Semax	if (sysctl(mib, 2, &btm, &len, 0, 0) < 0) {
210147999Semax		syslog(LOG_ERR, "sysctl(kern.boottime): %m");
211147999Semax		exit(1);
212147999Semax	}
213147999Semax
214147999Semax	stats_all.s2.boottime.tv_sec = btm.tv_sec;
215147999Semax	stats_all.s2.boottime.tv_usec = btm.tv_usec;
216174984Swkoszek
217147999Semax
218147999Semax#ifdef DEBUG
219147999Semax	fprintf(stderr, "%d %d %d %d\n", stats_all.s1.cp_time[0],
220147999Semax	    stats_all.s1.cp_time[1], stats_all.s1.cp_time[2], stats_all.s1.cp_time[3]);
221147999Semax#endif
222147999Semax
223147999Semax#define	FETCH_CNT(stat, cnt) do {					\
224147999Semax	len = sizeof(uint64_t);						\
225147999Semax	if (sysctlbyname("vm.stats." #cnt , &val, &len, NULL, 0) < 0) {	\
226147999Semax		syslog(LOG_ERR, "sysctl(vm.stats." #cnt "): %m");	\
227147999Semax		exit(1);						\
228147999Semax	}								\
229147999Semax	stat = val;							\
230147999Semax} while (0)
231147999Semax
232147999Semax	FETCH_CNT(stats_all.s1.v_pgpgin, vm.v_vnodepgsin);
233147999Semax	FETCH_CNT(stats_all.s1.v_pgpgout, vm.v_vnodepgsout);
234147999Semax	FETCH_CNT(stats_all.s1.v_pswpin, vm.v_swappgsin);
235147999Semax	FETCH_CNT(stats_all.s1.v_pswpout, vm.v_swappgsout);
236147999Semax	FETCH_CNT(stats_all.s1.v_intr, sys.v_intr);
237147999Semax	FETCH_CNT(stats_all.s2.v_swtch, sys.v_swtch);
238147999Semax	(void)gettimeofday(&tm, NULL);
239147999Semax	stats_all.s1.v_intr -= hz*(tm.tv_sec - btm.tv_sec) +
240147999Semax	    hz*(tm.tv_usec - btm.tv_usec)/1000000;
241147999Semax
242147999Semax	/* update disk transfers */
243147999Semax	updatexfers(RSTAT_DK_NDRIVE, stats_all.s1.dk_xfer);
244147999Semax
245193512Sed	mib[0] = CTL_NET;
246147999Semax	mib[1] = PF_LINK;
247147999Semax	mib[2] = NETLINK_GENERIC;
248147999Semax	mib[3] = IFMIB_SYSTEM;
249147999Semax	mib[4] = IFMIB_IFCOUNT;
250147999Semax	len = sizeof ifcount;
251147999Semax	if (sysctl(mib, 5, &ifcount, &len, 0, 0) < 0) {
252147999Semax		syslog(LOG_ERR, "sysctl(net.link.generic.system.ifcount): %m");
253147999Semax		exit(1);
254147999Semax	}
255147999Semax
256147999Semax	stats_all.s1.if_ipackets = 0;
257147999Semax	stats_all.s1.if_opackets = 0;
258147999Semax	stats_all.s1.if_ierrors = 0;
259147999Semax	stats_all.s1.if_oerrors = 0;
260147999Semax	stats_all.s1.if_collisions = 0;
261147999Semax	for (i = 1; i <= ifcount; i++) {
262147999Semax		len = sizeof ifmd;
263147999Semax		mib[3] = IFMIB_IFDATA;
264147999Semax		mib[4] = i;
265147999Semax		mib[5] = IFDATA_GENERAL;
266147999Semax		if (sysctl(mib, 6, &ifmd, &len, 0, 0) < 0) {
267156013Semax			if (errno == ENOENT)
268156013Semax				continue;
269156013Semax
270156013Semax			syslog(LOG_ERR, "sysctl(net.link.ifdata.%d.general)"
271156013Semax			       ": %m", i);
272156013Semax			exit(1);
273156013Semax		}
274156013Semax
275156013Semax		stats_all.s1.if_ipackets += ifmd.ifmd_data.ifi_ipackets;
276156013Semax		stats_all.s1.if_opackets += ifmd.ifmd_data.ifi_opackets;
277174984Swkoszek		stats_all.s1.if_ierrors += ifmd.ifmd_data.ifi_ierrors;
278174984Swkoszek		stats_all.s1.if_oerrors += ifmd.ifmd_data.ifi_oerrors;
279147999Semax		stats_all.s1.if_collisions += ifmd.ifmd_data.ifi_collisions;
280156010Semax	}
281147999Semax	(void)gettimeofday(&tm, NULL);
282147999Semax	stats_all.s3.curtime.tv_sec = tm.tv_sec;
283147999Semax	stats_all.s3.curtime.tv_usec = tm.tv_usec;
284147999Semax	alarm(1);
285147999Semax}
286193512Sed
287147999Semax/*
288147999Semax * returns true if have a disk
289147999Semax */
290193512Sedint
291147999Semaxhaveadisk(void)
292147999Semax{
293147999Semax	register int i;
294147999Semax	struct statinfo stats;
295147999Semax	int num_devices, retval = 0;
296147999Semax
297147999Semax	if ((num_devices = devstat_getnumdevs(NULL)) < 0) {
298147999Semax		syslog(LOG_ERR, "rstatd: can't get number of devices: %s",
299147999Semax		       devstat_errbuf);
300147999Semax		exit(1);
301147999Semax	}
302147999Semax
303147999Semax	if (devstat_checkversion(NULL) < 0) {
304147999Semax		syslog(LOG_ERR, "rstatd: %s", devstat_errbuf);
305147999Semax		exit(1);
306147999Semax	}
307147999Semax
308147999Semax	stats.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
309147999Semax	bzero(stats.dinfo, sizeof(struct devinfo));
310147999Semax
311147999Semax	if (devstat_getdevs(NULL, &stats) == -1) {
312147999Semax		syslog(LOG_ERR, "rstatd: can't get device list: %s",
313147999Semax		       devstat_errbuf);
314147999Semax		exit(1);
315147999Semax	}
316147999Semax	for (i = 0; i < stats.dinfo->numdevs; i++) {
317147999Semax		if (((stats.dinfo->devices[i].device_type
318147999Semax		      & DEVSTAT_TYPE_MASK) == DEVSTAT_TYPE_DIRECT)
319147999Semax		 && ((stats.dinfo->devices[i].device_type
320147999Semax		      & DEVSTAT_TYPE_PASS) == 0)) {
321147999Semax			retval = 1;
322147999Semax			break;
323147999Semax		}
324147999Semax	}
325147999Semax
326147999Semax	if (stats.dinfo->mem_ptr)
327147999Semax		free(stats.dinfo->mem_ptr);
328147999Semax
329147999Semax	free(stats.dinfo);
330147999Semax	return(retval);
331147999Semax}
332147999Semax
333147999Semaxvoid
334147999Semaxupdatexfers(int numdevs, int *devs)
335147999Semax{
336147999Semax	register int i, j, k, t;
337147999Semax	struct statinfo stats;
338147999Semax	int num_devices = 0;
339147999Semax	u_int64_t total_transfers;
340147999Semax
341147999Semax	if ((num_devices = devstat_getnumdevs(NULL)) < 0) {
342147999Semax		syslog(LOG_ERR, "rstatd: can't get number of devices: %s",
343147999Semax		       devstat_errbuf);
344147999Semax		exit(1);
345147999Semax	}
346147999Semax
347147999Semax	if (devstat_checkversion(NULL) < 0) {
348147999Semax		syslog(LOG_ERR, "rstatd: %s", devstat_errbuf);
349147999Semax		exit(1);
350147999Semax	}
351147999Semax
352147999Semax	stats.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
353147999Semax	bzero(stats.dinfo, sizeof(struct devinfo));
354147999Semax
355147999Semax	if (devstat_getdevs(NULL, &stats) == -1) {
356147999Semax		syslog(LOG_ERR, "rstatd: can't get device list: %s",
357147999Semax		       devstat_errbuf);
358147999Semax		exit(1);
359147999Semax	}
360147999Semax
361147999Semax	for (i = 0, j = 0; i < stats.dinfo->numdevs && j < numdevs; i++) {
362147999Semax		if (((stats.dinfo->devices[i].device_type
363147999Semax		      & DEVSTAT_TYPE_MASK) == DEVSTAT_TYPE_DIRECT)
364147999Semax		 && ((stats.dinfo->devices[i].device_type
365147999Semax		      & DEVSTAT_TYPE_PASS) == 0)) {
366147999Semax			total_transfers = 0;
367147999Semax			for (k = 0; k < DEVSTAT_N_TRANS_FLAGS; k++)
368147999Semax				total_transfers +=
369147999Semax				    stats.dinfo->devices[i].operations[k];
370147999Semax			/*
371147999Semax			 * XXX KDM If the total transfers for this device
372147999Semax			 * are greater than the amount we can fit in a
373147999Semax			 * signed integer, just set them to the maximum
374147999Semax			 * amount we can fit in a signed integer.  I have a
375147999Semax			 * feeling that the rstat protocol assumes 32-bit
376147999Semax			 * integers, so this could well break on a 64-bit
377147999Semax			 * architecture like the Alpha.
378147999Semax			 */
379147999Semax			if (total_transfers > INT_MAX)
380147999Semax				t = INT_MAX;
381147999Semax			else
382147999Semax				t = total_transfers;
383147999Semax			devs[j] = t;
384147999Semax			j++;
385147999Semax		}
386147999Semax	}
387147999Semax
388147999Semax	if (stats.dinfo->mem_ptr)
389241885Seadler		free(stats.dinfo->mem_ptr);
390241885Seadler
391156167Semax	free(stats.dinfo);
392147999Semax}
393147999Semax
394147999Semaxvoid
395147999Semaxrstat_service(struct svc_req *rqstp, SVCXPRT *transp)
396147999Semax{
397147999Semax	union {
398147999Semax		int fill;
399147999Semax	} argument;
400147999Semax	void *result;
401147999Semax	xdrproc_t xdr_argument, xdr_result;
402147999Semax	typedef void *(svc_cb)(void *arg, struct svc_req *rqstp);
403147999Semax	svc_cb *local;
404147999Semax
405147999Semax	switch (rqstp->rq_proc) {
406147999Semax	case NULLPROC:
407147999Semax		(void)svc_sendreply(transp, (xdrproc_t)xdr_void, NULL);
408147999Semax		goto leave;
409147999Semax
410147999Semax	case RSTATPROC_STATS:
411147999Semax		xdr_argument = (xdrproc_t)xdr_void;
412147999Semax		xdr_result = (xdrproc_t)xdr_statstime;
413147999Semax                switch (rqstp->rq_vers) {
414147999Semax                case RSTATVERS_ORIG:
415147999Semax                        local = (svc_cb *)rstatproc_stats_1_svc;
416147999Semax                        break;
417147999Semax                case RSTATVERS_SWTCH:
418147999Semax                        local = (svc_cb *)rstatproc_stats_2_svc;
419147999Semax                        break;
420147999Semax                case RSTATVERS_TIME:
421147999Semax                        local = (svc_cb *)rstatproc_stats_3_svc;
422147999Semax                        break;
423147999Semax                default:
424147999Semax                        svcerr_progvers(transp, RSTATVERS_ORIG, RSTATVERS_TIME);
425147999Semax                        goto leave;
426147999Semax                        /*NOTREACHED*/
427147999Semax                }
428147999Semax		break;
429147999Semax
430147999Semax	case RSTATPROC_HAVEDISK:
431147999Semax		xdr_argument = (xdrproc_t)xdr_void;
432147999Semax		xdr_result = (xdrproc_t)xdr_u_int;
433147999Semax                switch (rqstp->rq_vers) {
434147999Semax                case RSTATVERS_ORIG:
435147999Semax                        local = (svc_cb *)rstatproc_havedisk_1_svc;
436147999Semax                        break;
437147999Semax                case RSTATVERS_SWTCH:
438147999Semax                        local = (svc_cb *)rstatproc_havedisk_2_svc;
439147999Semax                        break;
440147999Semax                case RSTATVERS_TIME:
441147999Semax                        local = (svc_cb *)rstatproc_havedisk_3_svc;
442147999Semax                        break;
443147999Semax                default:
444147999Semax                        svcerr_progvers(transp, RSTATVERS_ORIG, RSTATVERS_TIME);
445147999Semax                        goto leave;
446147999Semax                        /*NOTREACHED*/
447147999Semax                }
448147999Semax		break;
449147999Semax
450147999Semax	default:
451147999Semax		svcerr_noproc(transp);
452147999Semax		goto leave;
453147999Semax	}
454147999Semax	bzero((char *)&argument, sizeof(argument));
455147999Semax	if (!svc_getargs(transp, xdr_argument, &argument)) {
456147999Semax		svcerr_decode(transp);
457147999Semax		goto leave;
458147999Semax	}
459147999Semax	result = (*local)(&argument, rqstp);
460147999Semax	if (result != NULL &&
461147999Semax	    !svc_sendreply(transp, xdr_result, result)) {
462147999Semax		svcerr_systemerr(transp);
463147999Semax	}
464147999Semax	if (!svc_freeargs(transp, xdr_argument, &argument))
465147999Semax		errx(1, "unable to free arguments");
466147999Semaxleave:
467147999Semax        if (from_inetd)
468147999Semax                exit(0);
469147999Semax}
470147999Semax