xref: /openbsd-src/libexec/rpc.rstatd/rstat_proc.c (revision 898184e3e61f9129feb5978fad5a8c6865f00b92)
1 /*	$OpenBSD: rstat_proc.c,v 1.28 2010/09/01 14:43:34 millert Exp $	*/
2 
3 /*
4  * Copyright (c) 2010, Oracle America, Inc.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are
8  * met:
9  *
10  *     * Redistributions of source code must retain the above copyright
11  *       notice, this list of conditions and the following disclaimer.
12  *     * Redistributions in binary form must reproduce the above
13  *       copyright notice, this list of conditions and the following
14  *       disclaimer in the documentation and/or other materials
15  *       provided with the distribution.
16  *     * Neither the name of the "Oracle America, Inc." nor the names of its
17  *       contributors may be used to endorse or promote products derived
18  *       from this software without specific prior written permission.
19  *
20  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  *   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24  *   COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
25  *   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  *   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
27  *   GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  *   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29  *   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30  *   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 /*
35  * rstat service:  built with rstat.x and derived from rpc.rstatd.c
36  */
37 
38 #include <sys/param.h>
39 #include <sys/vmmeter.h>
40 #include <sys/dkstat.h>
41 #include <sys/socket.h>
42 #include <sys/sysctl.h>
43 #include <net/if.h>
44 #include <uvm/uvm_extern.h>
45 
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <signal.h>
50 #include <syslog.h>
51 #include <fcntl.h>
52 #include <limits.h>
53 #include <errno.h>
54 #include <ifaddrs.h>
55 #include "dkstats.h"
56 
57 #undef FSHIFT			 /* Use protocol's shift and scale values */
58 #undef FSCALE
59 #undef DK_NDRIVE
60 #undef CPUSTATES
61 #undef if_ipackets
62 #undef if_ierrors
63 #undef if_opackets
64 #undef if_oerrors
65 #undef if_collisions
66 #include <rpcsvc/rstat.h>
67 
68 int	cp_xlat[CPUSTATES] = { CP_USER, CP_NICE, CP_SYS, CP_IDLE };
69 
70 extern int dk_ndrive;		/* from dkstats.c */
71 extern struct _disk cur, last;
72 char *memf = NULL, *nlistf = NULL;
73 int hz;
74 
75 extern int from_inetd;
76 int sincelastreq = 0;		/* number of alarms since last request */
77 extern int closedown;
78 
79 union {
80 	struct stats s1;
81 	struct statsswtch s2;
82 	struct statstime s3;
83 } stats_all;
84 
85 void	updatestat(void);
86 void	updatestatsig(int sig);
87 void	setup(void);
88 
89 volatile sig_atomic_t wantupdatestat;
90 
91 static int stat_is_init = 0;
92 
93 #ifndef FSCALE
94 #define FSCALE (1 << 8)
95 #endif
96 
97 static void
98 stat_init(void)
99 {
100 	stat_is_init = 1;
101 	setup();
102 	updatestat();
103 	(void) signal(SIGALRM, updatestatsig);
104 	alarm(1);
105 }
106 
107 statstime *
108 rstatproc_stats_3_svc(void *arg, struct svc_req *rqstp)
109 {
110 	if (!stat_is_init)
111 		stat_init();
112 	sincelastreq = 0;
113 	return (&stats_all.s3);
114 }
115 
116 statsswtch *
117 rstatproc_stats_2_svc(void *arg, struct svc_req *rqstp)
118 {
119 	if (!stat_is_init)
120 		stat_init();
121 	sincelastreq = 0;
122 	return (&stats_all.s2);
123 }
124 
125 stats *
126 rstatproc_stats_1_svc(void *arg, struct svc_req *rqstp)
127 {
128 	if (!stat_is_init)
129 		stat_init();
130 	sincelastreq = 0;
131 	return (&stats_all.s1);
132 }
133 
134 u_int *
135 rstatproc_havedisk_3_svc(void *arg, struct svc_req *rqstp)
136 {
137 	static u_int have;
138 
139 	if (!stat_is_init)
140 		stat_init();
141 	sincelastreq = 0;
142 	have = dk_ndrive != 0;
143 	return (&have);
144 }
145 
146 u_int *
147 rstatproc_havedisk_2_svc(void *arg, struct svc_req *rqstp)
148 {
149 	return (rstatproc_havedisk_3_svc(arg, rqstp));
150 }
151 
152 u_int *
153 rstatproc_havedisk_1_svc(void *arg, struct svc_req *rqstp)
154 {
155 	return (rstatproc_havedisk_3_svc(arg, rqstp));
156 }
157 
158 /* ARGSUSED */
159 void
160 updatestatsig(int sig)
161 {
162 	wantupdatestat = 1;
163 }
164 
165 void
166 updatestat(void)
167 {
168 	int i, mib[2], save_errno = errno;
169 	struct uvmexp uvmexp;
170 	size_t len;
171 	struct if_data *ifdp;
172 	struct ifaddrs *ifaddrs, *ifa;
173 	double avrun[3];
174 	struct timeval tm, btm;
175 	long *cp_time = cur.cp_time;
176 
177 #ifdef DEBUG
178 	syslog(LOG_DEBUG, "entering updatestat");
179 #endif
180 	if (sincelastreq >= closedown) {
181 #ifdef DEBUG
182 		syslog(LOG_DEBUG, "about to closedown");
183 #endif
184 		if (from_inetd)
185 			_exit(0);
186 		else {
187 			stat_is_init = 0;
188 			errno = save_errno;
189 			return;
190 		}
191 	}
192 	sincelastreq++;
193 
194 	/*
195 	 * dkreadstats reads in the "disk_count" as well as the "disklist"
196 	 * statistics.  It also retrieves "hz" and the "cp_time" array.
197 	 */
198 	dkreadstats();
199 	memset(stats_all.s1.dk_xfer, '\0', sizeof(stats_all.s1.dk_xfer));
200 	for (i = 0; i < dk_ndrive && i < DK_NDRIVE; i++)
201 		stats_all.s1.dk_xfer[i] = cur.dk_rxfer[i] + cur.dk_wxfer[i];
202 
203 	for (i = 0; i < CPUSTATES; i++)
204 		stats_all.s1.cp_time[i] = cp_time[cp_xlat[i]];
205 	(void)getloadavg(avrun, sizeof(avrun) / sizeof(avrun[0]));
206 	stats_all.s2.avenrun[0] = avrun[0] * FSCALE;
207 	stats_all.s2.avenrun[1] = avrun[1] * FSCALE;
208 	stats_all.s2.avenrun[2] = avrun[2] * FSCALE;
209 	mib[0] = CTL_KERN;
210 	mib[1] = KERN_BOOTTIME;
211 	len = sizeof(btm);
212 	if (sysctl(mib, 2, &btm, &len, NULL, 0) < 0) {
213 		syslog(LOG_ERR, "can't sysctl kern.boottime: %m");
214 		_exit(1);
215 	}
216 	stats_all.s2.boottime.tv_sec = btm.tv_sec;
217 	stats_all.s2.boottime.tv_usec = btm.tv_usec;
218 
219 
220 #ifdef DEBUG
221 	syslog(LOG_DEBUG, "%d %d %d %d", stats_all.s1.cp_time[0],
222 	    stats_all.s1.cp_time[1], stats_all.s1.cp_time[2],
223 	    stats_all.s1.cp_time[3]);
224 #endif
225 
226 	mib[0] = CTL_VM;
227 	mib[1] = VM_UVMEXP;
228 	len = sizeof(uvmexp);
229 	if (sysctl(mib, 2, &uvmexp, &len, NULL, 0) < 0) {
230 		syslog(LOG_ERR, "can't sysctl vm.uvmexp: %m");
231 		_exit(1);
232 	}
233 	stats_all.s1.v_pgpgin = uvmexp.fltanget;
234 	stats_all.s1.v_pgpgout = uvmexp.pdpageouts;
235 	stats_all.s1.v_pswpin = uvmexp.swapins;
236 	stats_all.s1.v_pswpout = uvmexp.swapouts;
237 	stats_all.s1.v_intr = uvmexp.intrs;
238 	stats_all.s2.v_swtch = uvmexp.swtch;
239 	gettimeofday(&tm, (struct timezone *) 0);
240 	stats_all.s1.v_intr -= hz*(tm.tv_sec - btm.tv_sec) +
241 	    hz*(tm.tv_usec - btm.tv_usec)/1000000;
242 	stats_all.s1.if_ipackets = 0;
243 	stats_all.s1.if_opackets = 0;
244 	stats_all.s1.if_ierrors = 0;
245 	stats_all.s1.if_oerrors = 0;
246 	stats_all.s1.if_collisions = 0;
247 	if (getifaddrs(&ifaddrs) == -1) {
248 		syslog(LOG_ERR, "can't getifaddrs: %m");
249 		_exit(1);
250 	}
251 	for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
252 		if (ifa->ifa_addr->sa_family != AF_LINK)
253 			continue;
254 		ifdp = (struct if_data *)ifa->ifa_data;
255 		stats_all.s1.if_ipackets += ifdp->ifi_ipackets;
256 		stats_all.s1.if_opackets += ifdp->ifi_opackets;
257 		stats_all.s1.if_ierrors += ifdp->ifi_ierrors;
258 		stats_all.s1.if_oerrors += ifdp->ifi_oerrors;
259 		stats_all.s1.if_collisions += ifdp->ifi_collisions;
260 	}
261 	freeifaddrs(ifaddrs);
262 	stats_all.s3.curtime.tv_sec = tm.tv_sec;
263 	stats_all.s3.curtime.tv_usec = tm.tv_usec;
264 
265 	alarm(1);
266 	errno = save_errno;
267 }
268 
269 void
270 setup(void)
271 {
272 	dkinit(0);
273 }
274 
275 void	rstat_service(struct svc_req *, SVCXPRT *);
276 
277 void
278 rstat_service(struct svc_req *rqstp, SVCXPRT *transp)
279 {
280 	char *(*local)(void *, struct svc_req *);
281 	xdrproc_t xdr_argument, xdr_result;
282 	union {
283 		int fill;
284 	} argument;
285 	char *result;
286 
287 	switch (rqstp->rq_proc) {
288 	case NULLPROC:
289 		(void)svc_sendreply(transp, xdr_void, (char *)NULL);
290 		return;
291 
292 	case RSTATPROC_STATS:
293 		xdr_argument = (xdrproc_t)xdr_void;
294 		xdr_result = (xdrproc_t)xdr_statstime;
295 		switch (rqstp->rq_vers) {
296 		case RSTATVERS_ORIG:
297 			local = (char *(*)(void *, struct svc_req *))
298 				rstatproc_stats_1_svc;
299 			break;
300 		case RSTATVERS_SWTCH:
301 			local = (char *(*)(void *, struct svc_req *))
302 				rstatproc_stats_2_svc;
303 			break;
304 		case RSTATVERS_TIME:
305 			local = (char *(*)(void *, struct svc_req *))
306 				rstatproc_stats_3_svc;
307 			break;
308 		default:
309 			svcerr_progvers(transp, RSTATVERS_ORIG, RSTATVERS_TIME);
310 			return;
311 		}
312 		break;
313 
314 	case RSTATPROC_HAVEDISK:
315 		xdr_argument = (xdrproc_t)xdr_void;
316 		xdr_result = (xdrproc_t)xdr_u_int;
317 		switch (rqstp->rq_vers) {
318 		case RSTATVERS_ORIG:
319 			local = (char *(*)(void *, struct svc_req *))
320 				rstatproc_havedisk_1_svc;
321 			break;
322 		case RSTATVERS_SWTCH:
323 			local = (char *(*)(void *, struct svc_req *))
324 				rstatproc_havedisk_2_svc;
325 			break;
326 		case RSTATVERS_TIME:
327 			local = (char *(*)(void *, struct svc_req *))
328 				rstatproc_havedisk_3_svc;
329 			break;
330 		default:
331 			svcerr_progvers(transp, RSTATVERS_ORIG, RSTATVERS_TIME);
332 			return;
333 		}
334 		break;
335 
336 	default:
337 		svcerr_noproc(transp);
338 		return;
339 	}
340 	bzero((char *)&argument, sizeof(argument));
341 	if (!svc_getargs(transp, xdr_argument, (caddr_t)&argument)) {
342 		svcerr_decode(transp);
343 		return;
344 	}
345 	result = (*local)(&argument, rqstp);
346 	if (result != NULL && !svc_sendreply(transp, xdr_result, result)) {
347 		svcerr_systemerr(transp);
348 	}
349 	if (!svc_freeargs(transp, xdr_argument, (caddr_t)&argument)) {
350 		syslog(LOG_ERR, "unable to free arguments");
351 		exit(1);
352 	}
353 }
354