rstat_proc.c revision 1.37 1 /* $NetBSD: rstat_proc.c,v 1.37 2000/11/30 23:59:03 simonb 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.37 2000/11/30 23:59:03 simonb 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 extern int dk_ndrive; /* From dkstats.c */
105 extern struct _disk cur, last;
106 int hz;
107 char *memf = NULL, *nlistf = NULL;
108
109 struct ifnet_head ifnetq; /* chain of ethernet interfaces */
110 int numintfs;
111
112 extern int from_inetd;
113 int sincelastreq = 0; /* number of alarms since last request */
114 extern int closedown;
115 kvm_t *kfd;
116
117 union {
118 struct stats s1;
119 struct statsswtch s2;
120 struct statstime s3;
121 } stats_all;
122
123 extern void dkreadstats(void);
124 extern int dkinit(int, gid_t);
125
126 void updatestat(int);
127 void setup(void);
128 void setup_kd_once(void);
129 void stat_init(void);
130 int havedisk(void);
131 void rstat_service(struct svc_req *, SVCXPRT *);
132
133 static int stat_is_init = 0;
134
135 #ifndef FSCALE
136 #define FSCALE (1 << 8)
137 #endif
138
139 void
140 stat_init()
141 {
142 stat_is_init = 1;
143 setup();
144 updatestat(0);
145 (void) signal(SIGALRM, updatestat);
146 alarm(1);
147 }
148
149 statstime *
150 rstatproc_stats_3_svc(void *arg, struct svc_req *rqstp)
151 {
152 if (!stat_is_init)
153 stat_init();
154 sincelastreq = 0;
155 return (&stats_all.s3);
156 }
157
158 statsswtch *
159 rstatproc_stats_2_svc(void *arg, struct svc_req *rqstp)
160 {
161 if (!stat_is_init)
162 stat_init();
163 sincelastreq = 0;
164 stats_all.s2.if_opackets = stats_all.s3.if_opackets;
165 return (&stats_all.s2);
166 }
167
168 stats *
169 rstatproc_stats_1_svc(void *arg, struct svc_req *rqstp)
170 {
171 if (!stat_is_init)
172 stat_init();
173 sincelastreq = 0;
174 stats_all.s1.if_opackets = stats_all.s3.if_opackets;
175 return (&stats_all.s1);
176 }
177
178 u_int *
179 rstatproc_havedisk_3_svc(void *arg, struct svc_req *rqstp)
180 {
181 static u_int have;
182
183 if (!stat_is_init)
184 stat_init();
185 sincelastreq = 0;
186 have = havedisk();
187 return (&have);
188 }
189
190 u_int *
191 rstatproc_havedisk_2_svc(void *arg, struct svc_req *rqstp)
192 {
193 return (rstatproc_havedisk_3_svc(arg, rqstp));
194 }
195
196 u_int *
197 rstatproc_havedisk_1_svc(void *arg, struct svc_req *rqstp)
198 {
199 return (rstatproc_havedisk_3_svc(arg, rqstp));
200 }
201
202 void
203 updatestat(int dummy)
204 {
205 long off;
206 int i;
207 size_t len;
208 int mib[2];
209 struct uvmexp_sysctl uvmexp;
210 struct ifnet ifnet;
211 double avrun[3];
212 struct timeval tm, btm;
213
214 #ifdef DEBUG
215 syslog(LOG_DEBUG, "entering updatestat");
216 #endif
217 if (sincelastreq >= closedown) {
218 #ifdef DEBUG
219 syslog(LOG_DEBUG, "about to closedown");
220 #endif
221 if (from_inetd)
222 exit(0);
223 else {
224 stat_is_init = 0;
225 return;
226 }
227 }
228 sincelastreq++;
229
230 /*
231 * dkreadstats reads in the "disk_count" as well as the "disklist"
232 * statistics. It also retrieves "hz" and the "cp_time" array.
233 */
234 dkreadstats();
235 memset(stats_all.s3.dk_xfer, 0, sizeof(stats_all.s3.dk_xfer));
236 for (i = 0; i < dk_ndrive && i < DK_NDRIVE; i++)
237 stats_all.s3.dk_xfer[i] = cur.dk_xfer[i];
238
239 #ifdef BSD
240 for (i = 0; i < CPUSTATES; i++)
241 stats_all.s3.cp_time[i] = cur.cp_time[cp_xlat[i]];
242 #else
243 if (kvm_read(kfd, (long)nl[X_CPTIME].n_value,
244 (char *)stats_all.s3.cp_time,
245 sizeof (stats_all.s3.cp_time))
246 != sizeof (stats_all.s3.cp_time)) {
247 syslog(LOG_ERR, "can't read cp_time from kmem");
248 exit(1);
249 }
250 #endif
251 #ifdef BSD
252 (void)getloadavg(avrun, sizeof(avrun) / sizeof(avrun[0]));
253 #endif
254 stats_all.s3.avenrun[0] = avrun[0] * FSCALE;
255 stats_all.s3.avenrun[1] = avrun[1] * FSCALE;
256 stats_all.s3.avenrun[2] = avrun[2] * FSCALE;
257 mib[0] = CTL_KERN;
258 mib[1] = KERN_BOOTTIME;
259 len = sizeof(btm);
260 if (sysctl(mib, 2, &btm, &len, NULL, 0) < 0) {
261 syslog(LOG_ERR, "can't sysctl kern.boottime");
262 exit(1);
263 }
264 stats_all.s3.boottime.tv_sec = btm.tv_sec;
265 stats_all.s3.boottime.tv_usec = btm.tv_usec;
266
267
268 #ifdef DEBUG
269 syslog(LOG_DEBUG, "%d %d %d %d %d\n", stats_all.s3.cp_time[0],
270 stats_all.s3.cp_time[1], stats_all.s3.cp_time[2],
271 stats_all.s3.cp_time[3], stats_all.s3.cp_time[4]);
272 #endif
273
274 mib[0] = CTL_VM;
275 mib[1] = VM_UVMEXP2;
276 len = sizeof(uvmexp);
277 if (sysctl(mib, 2, &uvmexp, &len, NULL, 0) < 0) {
278 syslog(LOG_ERR, "can't sysctl vm.uvmexp2");
279 exit(1);
280 }
281 stats_all.s3.v_pgpgin = uvmexp.fltanget;
282 stats_all.s3.v_pgpgout = uvmexp.pdpageouts;
283 stats_all.s3.v_pswpin = uvmexp.swapins;
284 stats_all.s3.v_pswpout = uvmexp.swapouts;
285 stats_all.s3.v_intr = uvmexp.intrs;
286 stats_all.s3.v_swtch = uvmexp.swtch;
287 gettimeofday(&tm, (struct timezone *) 0);
288 stats_all.s3.v_intr -= hz*(tm.tv_sec - btm.tv_sec) +
289 hz*(tm.tv_usec - btm.tv_usec)/1000000;
290
291 stats_all.s3.if_ipackets = 0;
292 stats_all.s3.if_opackets = 0;
293 stats_all.s3.if_ierrors = 0;
294 stats_all.s3.if_oerrors = 0;
295 stats_all.s3.if_collisions = 0;
296 for (off = (long)ifnetq.tqh_first, i = 0; off && i < numintfs; i++) {
297 if (kvm_read(kfd, off, (char *)&ifnet, sizeof ifnet) !=
298 sizeof ifnet) {
299 syslog(LOG_ERR, "can't read ifnet from kmem");
300 exit(1);
301 }
302 stats_all.s3.if_ipackets += ifnet.if_data.ifi_ipackets;
303 stats_all.s3.if_opackets += ifnet.if_data.ifi_opackets;
304 stats_all.s3.if_ierrors += ifnet.if_data.ifi_ierrors;
305 stats_all.s3.if_oerrors += ifnet.if_data.ifi_oerrors;
306 stats_all.s3.if_collisions += ifnet.if_data.ifi_collisions;
307 off = (long)ifnet.if_list.tqe_next;
308 }
309 gettimeofday((struct timeval *)&stats_all.s3.curtime,
310 (struct timezone *) 0);
311 alarm(1);
312 }
313
314 void
315 setup_kd_once()
316 {
317 char errbuf[_POSIX2_LINE_MAX];
318 kfd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf);
319 if (kfd == NULL) {
320 syslog(LOG_ERR, "%s", errbuf);
321 exit (1);
322 }
323 }
324
325 void
326 setup()
327 {
328 struct ifnet ifnet;
329 long off;
330 static int is_kfd_setup = 0;
331
332 /* setup() is called after each dormant->active
333 * transition. Since we never close the kvm files
334 * (there's no reason), make sure we don't open them
335 * each time, as that can lead to exhaustion of all open
336 * files! */
337 if (!is_kfd_setup) {
338 setup_kd_once();
339 is_kfd_setup = 1;
340 }
341
342 if (kvm_nlist(kfd, nl) != 0) {
343 syslog(LOG_ERR, "can't get namelist");
344 exit (1);
345 }
346
347 if (kvm_read(kfd, (long)nl[X_IFNET].n_value, &ifnetq,
348 sizeof ifnetq) != sizeof ifnetq) {
349 syslog(LOG_ERR, "can't read ifnet queue head from kmem");
350 exit(1);
351 }
352
353 numintfs = 0;
354 for (off = (long)ifnetq.tqh_first; off;) {
355 if (kvm_read(kfd, off, (char *)&ifnet, sizeof ifnet) !=
356 sizeof ifnet) {
357 syslog(LOG_ERR, "can't read ifnet from kmem");
358 exit(1);
359 }
360 numintfs++;
361 off = (long)ifnet.if_list.tqe_next;
362 }
363 dkinit(0, getgid());
364 }
365
366 /*
367 * returns true if have a disk
368 */
369 int
370 havedisk()
371 {
372 return dk_ndrive != 0;
373 }
374
375 void
376 rstat_service(struct svc_req *rqstp, SVCXPRT *transp)
377 {
378 union {
379 int fill;
380 } argument;
381 char *result;
382 xdrproc_t xdr_argument, xdr_result;
383 char *(*local)(void *, struct svc_req *);
384
385 switch (rqstp->rq_proc) {
386 case NULLPROC:
387 (void)svc_sendreply(transp, xdr_void, (char *)NULL);
388 goto leave;
389
390 case RSTATPROC_STATS:
391 xdr_argument = (xdrproc_t)xdr_void;
392 xdr_result = (xdrproc_t)xdr_statstime;
393 switch (rqstp->rq_vers) {
394 case RSTATVERS_ORIG:
395 local = (char *(*)(void *, struct svc_req *))
396 rstatproc_stats_1_svc;
397 break;
398 case RSTATVERS_SWTCH:
399 local = (char *(*)(void *, struct svc_req *))
400 rstatproc_stats_2_svc;
401 break;
402 case RSTATVERS_TIME:
403 local = (char *(*)(void *, struct svc_req *))
404 rstatproc_stats_3_svc;
405 break;
406 default:
407 svcerr_progvers(transp, RSTATVERS_ORIG, RSTATVERS_TIME);
408 goto leave;
409 }
410 break;
411
412 case RSTATPROC_HAVEDISK:
413 xdr_argument = (xdrproc_t)xdr_void;
414 xdr_result = (xdrproc_t)xdr_u_int;
415 switch (rqstp->rq_vers) {
416 case RSTATVERS_ORIG:
417 local = (char *(*)(void *, struct svc_req *))
418 rstatproc_havedisk_1_svc;
419 break;
420 case RSTATVERS_SWTCH:
421 local = (char *(*)(void *, struct svc_req *))
422 rstatproc_havedisk_2_svc;
423 break;
424 case RSTATVERS_TIME:
425 local = (char *(*)(void *, struct svc_req *))
426 rstatproc_havedisk_3_svc;
427 break;
428 default:
429 svcerr_progvers(transp, RSTATVERS_ORIG, RSTATVERS_TIME);
430 goto leave;
431 }
432 break;
433
434 default:
435 svcerr_noproc(transp);
436 goto leave;
437 }
438 memset((char *)&argument, 0, sizeof(argument));
439 if (!svc_getargs(transp, xdr_argument, (caddr_t)&argument)) {
440 svcerr_decode(transp);
441 goto leave;
442 }
443 result = (*local)(&argument, rqstp);
444 if (result != NULL && !svc_sendreply(transp, xdr_result, result)) {
445 svcerr_systemerr(transp);
446 }
447 if (!svc_freeargs(transp, xdr_argument, (caddr_t)&argument)) {
448 (void)fprintf(stderr, "unable to free arguments\n");
449 exit(1);
450 }
451 leave:
452 if (from_inetd)
453 exit(0);
454 }
455